借助jhsdb来看看对象存在于何处以及Integer类型和String类型的缓存机制(详细得一批)

本文主要是介绍借助jhsdb来看看对象存在于何处以及Integer类型和String类型的缓存机制(详细得一批),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文使用环境jdk11+win10
首先贴实验代码,JVM参数设置为:-Xmx10m -XX:+UseSerialGC -XX:-UseCompressedOops

package test4;public class JHSDB_TestCse {//-Xmx10m -XX:+UseSerialGC -XX:-UseCompressedOops//static class Test{int k = 2;String s = "ssss";String ss = "ssss";String sss = new String("ssss");String ssss = new String("sssss");String sssss = new String("sssss");String ssssss = "sssss";Integer i = Integer.valueOf(25);Integer i1 = Integer.valueOf(25);Integer i10 = Integer.valueOf(127);Integer i11 = Integer.valueOf(127);Integer i2 = Integer.valueOf(128);Integer i3 = Integer.valueOf(128);Integer i4 = Integer.valueOf(-128);Integer i5 = Integer.valueOf(-128);Integer i6 = Integer.valueOf(0);Integer i7 = Integer.valueOf(0);Integer i8 = Integer.valueOf(-129);Integer i9 = Integer.valueOf(-129);static ObjectHolder staticObj = new ObjectHolder();ObjectHolder instanceObj = new ObjectHolder();void foo(){ObjectHolder localObj = new ObjectHolder();System.out.println("deon");}}private static class ObjectHolder{}public static void main(String[] args) {Test test = new Test();test.foo();}
}

0.初始

在终端使用jsp-l 查看当前运行程序的端口(可以使用断点使程序中断,这里我们在System.out.println("deon");处中断。)
然后使用jhsd b hsdb --pid 端口号 打开jhsdb
在这里插入图片描述
(下头那几个框框,刚打开是没有的)

点击如下命令,查看堆的情况。
在这里插入图片描述在这里插入图片描述

算一下可以知道,堆的总大小与我们设置是吻合的。堆的大小为10M,且老年代与新生代的比例为2:1 ,默认eden:Survivor = 8:1
然后我们打开Console ,输入查找某类型的对象的命令。

1.对象

在这里插入图片描述
在这里插入图片描述
阔以发现对象新创建是创建于年轻代的eden区。
然后再打开Inspector ,查看在堆中,该对象的存储信息
在这里插入图片描述
输入地址查看存放的对象在这里插入图片描述
注意看,类的基本类型的静态域是存于堆的啊 (其实这就是写这篇文章的原因,有人在问) 。大家来猜猜,最后一个ObjectHolder 是静态域还是实例域 (假设大家英语不好)
好嘛事实上,它是实例域
再打开命令行 (windows-Console) ,输入revptrs 地址 反查找引用它的是谁
在这里插入图片描述
这时你就会发现一个熟悉的地址。正是我们刚才那个Test实例对象。
在这里插入图片描述
细心的朋友阔能发现不对劲了,那个静态域static ObjectHolder staticObj 去哪了?
来让我们找找,打开Object Histogram ,看看整个程序都生成了哪些对象
在这里插入图片描述
输入对象的名称点击望远镜,选中。再点击放大镜在这里插入图片描述
依次选中这三个,分别点击Shwo Liveness Path在这里插入图片描述
在这里插入图片描述
可以发现,从上往下。
第一个对象被Test引用,它便是那个实例域
第二个对象没有被堆中任何变量引用,它便是foo中的局部变量
第三个对象,被Test的Class引用,它便是静态域
我们还可以用revptrs命令 查看一下引用静态域对象的到底是不是Class。(比较Class对象的地址说明其存在于堆中)
在这里插入图片描述
还可以看看引用Class的是谁…(疯狂套娃)在这里插入图片描述
那么那个局部变量被谁所引用呢?
选中main线程,再点击如图所示图标,打开栈内存情况
在这里插入图片描述
在这里插入图片描述
果然存在是被栈所引用的。

2 .String

再回到梦想开始的地方。
在这里插入图片描述
有没有发现有些地址是重复的呀,规律就是只要不是new String…只要前面的字符已经存在过,那么对象的地址一定是第一个""对象的地址。这咋回事呢?
相信大家都知道一个说法,如果使用"" 创建字符串,那么它会首先在字符串常量池中寻找,如果没有值相等的量,才会在常量池中创建,然后引用指向那个常量,如存在了就只会指向那个常量,而使用new String()无论字符串常量池中是否存在该字符串,都会在上创建,如果字符串常量池不存在,再在常量池中创建一个。那么到底是不是呢?
首先从地址上观察这些对象都存在堆内,这是毋庸置疑的。
好像有点不一样噢,大家都在Eden区。(jdk7以后已经将字符串常量池y移置堆中)
让我们再看看Test类的常量池。
在这里插入图片描述
可以看到该类的方法信息,注意这些是存在方法区中的(观察地址),一直翻到最后,进入常量池在这里插入图片描述
在这里插入图片描述
可以看到 “ssss” 字段和 “sssss” 字段就存在于这里了,如果再往下找,还会发现我们写在println语句中创建的 "deon"
还会发现常量池里还存了
在这里插入图片描述
本来我以为这是字段名称
然后我又发现了它,本来或许凑巧是内部的字面量呢,但是好像又不可能,我把单词拼错了,应该不会有。等最后加个汉字再测试一下。
在这里插入图片描述
通过上述资料,我做出大概猜测类型常量池里存的应该是指向字符常量池的指针,因为该常量池的地址位于堆之外,在方法区。字符串常量池会在编译期间将所有""存起来,如果再new String("")的话就会产生新的对象。那么new String()操作就可能产生1~2个对象,肯定会产生的是运行期间在堆中分配的对象,另一个则是编译期间生成的对象(如果常量池里没有的话),而"" 仅会在编译器产生对象在字符串常量池(如果常量池里没有的话)。
以上说法有误,见补充。

补充:

//仅截取关键部分代码String y = "源大彪";void foo(){y = new String("达标");}public static void main(String[] args) {Test test = new Test();···test.foo();····String y = "源大彪";System.out.println(y);}

这是执行到y = new String("达标");前的地址
在这里插入图片描述
这是执行到y = new String("达标");后的地址
在这里插入图片描述
在这里插入图片描述
栈上引用的仍然是字符串常量池的地址。
我还发现一个有意思的细节,就算是用new String() 包装过的对象也仅仅是对之前常量池的对象进行了一个浅复制。它们里面用来存储String字符的byte数组都是同一个。(下图与上图不是一次实验)
在这里插入图片描述
再用这种方式来验证一下是否会在常量池中存储
在这里插入图片描述
注意此时生成的对象里并没有以下所有字符串的
在这里插入图片描述
运行完System.out.println("done"); dnoe就出现了!不是说编译期生成""对象吗在这里插入图片描述
我们再看看当前类的常量池就会惊奇的发现,还没有运行到的地方已经生成了,由此可说明常量池中的字符串与堆中字符串并非同一字符串。
在这里插入图片描述
也就是说编译期间所有的""都会在常量池中产生字面量,运行时当有引用尝试用""产生对象时,则会在堆中生成一个对象,再用一个指针指向堆中对象,下次再有引用尝试时,则直接将引用指向堆中对象,也就是说""这种方式会创建0~1(如果没有重排序的话就是0个或者2个)。而new String("")的方式则会创建1~2个(如果不重排序的话,1个或者3个)。其他方式new String() 则会创建1个对象。
详细原理

3.Intger

其实看源码也能知道-127~128的范围会被缓存。
在这里插入图片描述
在这里插入图片描述

这里也证实了这一点。不过注意,被缓存的对象在老年代里。而且常量池并没有被堆中和栈中任何对象引用,但是它的状态仍然是存活那就只能在被方法区的引用了。(下图与之前不是一次运行结果)
在这里插入图片描述

下面是Integer缓存的源码。可以发现范围默认为-128~127,可以通过参数设置来修改integerCacheHighPropValue 修改范围。

private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}

这篇关于借助jhsdb来看看对象存在于何处以及Integer类型和String类型的缓存机制(详细得一批)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/239275

相关文章

Python pandas库自学超详细教程

《Pythonpandas库自学超详细教程》文章介绍了Pandas库的基本功能、安装方法及核心操作,涵盖数据导入(CSV/Excel等)、数据结构(Series、DataFrame)、数据清洗、转换... 目录一、什么是Pandas库(1)、Pandas 应用(2)、Pandas 功能(3)、数据结构二、安

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

2025版mysql8.0.41 winx64 手动安装详细教程

《2025版mysql8.0.41winx64手动安装详细教程》本文指导Windows系统下MySQL安装配置,包含解压、设置环境变量、my.ini配置、初始化密码获取、服务安装与手动启动等步骤,... 目录一、下载安装包二、配置环境变量三、安装配置四、启动 mysql 服务,修改密码一、下载安装包安装地

在macOS上安装jenv管理JDK版本的详细步骤

《在macOS上安装jenv管理JDK版本的详细步骤》jEnv是一个命令行工具,正如它的官网所宣称的那样,它是来让你忘记怎么配置JAVA_HOME环境变量的神队友,:本文主要介绍在macOS上安装... 目录前言安装 jenv添加 JDK 版本到 jenv切换 JDK 版本总结前言China编程在开发 Java

Java获取当前时间String类型和Date类型方式

《Java获取当前时间String类型和Date类型方式》:本文主要介绍Java获取当前时间String类型和Date类型方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录Java获取当前时间String和Date类型String类型和Date类型输出结果总结Java获取

Spring Boot Actuator应用监控与管理的详细步骤

《SpringBootActuator应用监控与管理的详细步骤》SpringBootActuator是SpringBoot的监控工具,提供健康检查、性能指标、日志管理等核心功能,支持自定义和扩展端... 目录一、 Spring Boot Actuator 概述二、 集成 Spring Boot Actuat

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont