借助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

相关文章

Redis中6种缓存更新策略详解

《Redis中6种缓存更新策略详解》Redis作为一款高性能的内存数据库,已经成为缓存层的首选解决方案,然而,使用缓存时最大的挑战在于保证缓存数据与底层数据源的一致性,本文将介绍Redis中6种缓存更... 目录引言策略一:Cache-Aside(旁路缓存)策略工作原理代码示例优缺点分析适用场景策略二:Re

Flutter实现文字镂空效果的详细步骤

《Flutter实现文字镂空效果的详细步骤》:本文主要介绍如何使用Flutter实现文字镂空效果,包括创建基础应用结构、实现自定义绘制器、构建UI界面以及实现颜色选择按钮等步骤,并详细解析了混合模... 目录引言实现原理开始实现步骤1:创建基础应用结构步骤2:创建主屏幕步骤3:实现自定义绘制器步骤4:构建U

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决

《IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决》:本文主要介绍IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决,本文分步骤结合实例给大... 目录步骤 1:创建 Maven Web 项目步骤 2:添加 Spring MVC 依赖1、保存后执行2、将新的依赖

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

如何为Yarn配置国内源的详细教程

《如何为Yarn配置国内源的详细教程》在使用Yarn进行项目开发时,由于网络原因,直接使用官方源可能会导致下载速度慢或连接失败,配置国内源可以显著提高包的下载速度和稳定性,本文将详细介绍如何为Yarn... 目录一、查询当前使用的镜像源二、设置国内源1. 设置为淘宝镜像源2. 设置为其他国内源三、还原为官方

最详细安装 PostgreSQL方法及常见问题解决

《最详细安装PostgreSQL方法及常见问题解决》:本文主要介绍最详细安装PostgreSQL方法及常见问题解决,介绍了在Windows系统上安装PostgreSQL及Linux系统上安装Po... 目录一、在 Windows 系统上安装 PostgreSQL1. 下载 PostgreSQL 安装包2.

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

MySql match against工具详细用法

《MySqlmatchagainst工具详细用法》在MySQL中,MATCH……AGAINST是全文索引(Full-Textindex)的查询语法,它允许你对文本进行高效的全文搜素,支持自然语言搜... 目录一、全文索引的基本概念二、创建全文索引三、自然语言搜索四、布尔搜索五、相关性排序六、全文索引的限制七

python中各种常见文件的读写操作与类型转换详细指南

《python中各种常见文件的读写操作与类型转换详细指南》这篇文章主要为大家详细介绍了python中各种常见文件(txt,xls,csv,sql,二进制文件)的读写操作与类型转换,感兴趣的小伙伴可以跟... 目录1.文件txt读写标准用法1.1写入文件1.2读取文件2. 二进制文件读取3. 大文件读取3.1

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效