使用JHSDB工具理解分代回收

JHSDB工具是一款基于服务性代理实现的进程外调试工具。服务性代理是HotSpot虚拟机中一组用于映射Java虚拟机运行信息的、主要基于Java语言实现的API集合。(简单来说,是可以在程序运行过程中监控程序状态的一款程序)。

启动JHSDB工具

保证在JDK的bin目录下也存在sawindbg.dll,没有的话可以从jre目录下面复制一份过来,只要保证两个文件夹都有就行了。

JHSDB是我们JDK自带的工具。虽说是自带,但我们依然需要保证..\jdk\bin;..\jdk\jre\bin中包含swingdbg.dll。

image.png

对象从新生代进入老年代的时机​

根据JVM分代回收理论,我们知道对内存被划分为新生代,老年代两大区域。新生代又根据"朝生夕死"再次被划分为Eden区,From区以及To区。

当一个对象被创建时,首先会被分配到新生代。那么对象什么时候会进入老年代呢?我们写以下代码进行测试。

参数设置:-XX:+UseConcMarkSweepGC(指定CMS垃圾回收器,因为部分垃圾回收器打破了分代回收理论)。

image.png

在本案例中,我们有两个对象,T1和T2。

T1在创建后,我们主动发起垃圾回收15次(注意,在自己的项目中切勿主动使用此方法)。

T2在创建后,我们不触发垃圾回收,让程序进入休眠。此时就可以使用JHSDB监测该程序的堆,栈等信息。

使用JHSDB查看相关进程信息

首先,使用JDK自带命令jps。查看我们的java程序对应的进程ID。

image.png

此时,把jps命令得到的进程ID号,在JHSDB工具中attach上去。

image.png

image.png

此时,会显示该进程对应的部分线程信息。

image.png

使用JHSDB查看堆中的地址布局

查看堆参数

image.png

image.png

上图中可以看到实际 JVM 启动过程中堆中参数的对照,可以看到,在不启动内存压缩的情况下。堆空间里面的分代划分都是连续的。

Eden的地址区域为12a00000~13200000;

From的地址区域为13200000~13300000;

To的地址区域为13300000~13400000;

Old的地址区域为13400000~14800000;

使用JHSDB查看对象在堆中的位置

image.png

image.png

在此处进行全路径名搜索

image.png

双击出现这个 Teacher 类的对象,两个,就是 T1 和 T2 对象

image.png


image.png

image.png

再次对比对象的地址分配。

Eden的地址区域为12a00000~13200000;

From的地址区域为13200000~13300000;

To的地址区域为13300000~13400000;

Old的地址区域为13400000~14800000;

可以看到,T1经过15次回收地址变为134c21c0,处于老年代。T2由于未经过垃圾回收,地址为12a28f98,处于新生代的Eden区。

image.png

使用JHSDB中查看栈信息

image.png

image.png

从上图中可以验证栈内存,同时也可以验证到虚拟机栈和本地方法栈在 Hotspot 中是合二为一的实现了,内存的区域是分离的,但确实连续的。

总结

image.png

当我们通过Java运行案例代码时,JVM的整个处理流程如下:

  1. JVM向操作系统申请内存,JVM第一步就是通过配置参数或者默认配置向操作系统申请内存空间。
  2. JVM获得内存空间后,会根据配置参数分配堆,栈以及方法区的内存大小。
  3. 完成以上步骤后,JVM首先会执行构造器,编译器会在.java文件被编译成.class文件时,收集所有类的初始化代码(此处在类加载篇章中会详细讲述)。包括静态变量赋值语句,静态代码块,静态方法,静态变量和常量放入方法区。
  4. 到了这一步,才开始正式运行我们的main线程。执行main方法。开始执行Teacher T1 = new Teacher()这一行代码。此时,堆内存中会创建一个Teacher对象。对象引用T1就存放在栈中。
  5. 对象产生后,会在对象头中记录该对象经历过的gc次数。T1在GC发生15次后,在对象头中记录的年龄会大于15。此时T1将会由新生代进入老年代。
  6. T2未经历过任何GC,因此根据朝生夕死原则,处于Eden区域。

最后附上本案例的所有信息在运行时数据区的分布图。

image.png

猜你喜欢

转载自blog.csdn.net/weixin_47184173/article/details/113574260