前言
呵呵 很久之前看到这样的两篇文章
[讨论] HotSpot VM Serial GC的一个问题
新生代回收调试的一些心得
在第一篇帖子中 R大 详细的讲述了 cheney 算法, 以及自己编写的 cheney 算法, 以及 DefNewGeneration 的具体的一些细节, 以及 和现有的例子的对比
另外还有一些 关于 rember set 的一些 讨论
呵呵 可以看出 R大 讨论到一些细节的时候, 在这篇文章中 似乎是真的有些兴奋了
找了一下 记录, mac 上面能找到的是 今年3月, 但是 实际上应该还有更早的记录才对
然后 当时写了一个 测试用例, 稍微调试了一下
但是 最近重新看了一下 新生代回收调试的一些心得, 在原作者的基础上面 重新整理了一下 测试用例, 呵呵
作为 本文的测试用例, 因为 原作者的测试用例, 我觉得 那个循环没有多大意义, 并且 我这里需要模拟出 跨代引用 的场景
在原作者的 测试用例 上面做了一些调整
以下代码, 截图 基于 jdk9
另外就是 一下截图 可能来自于多次调试, 可能地址 会对不上
测试用例
package com.hx.test05;
import java.util.ArrayList;
/**
* DefNewGc
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2020-05-02 18:15
*/
public class Test20DefNewGc implements Cloneable {
// static vars
private static Test20DefNewGc objInOldGen;
private static Test20DefNewGc objInYoungGen;
private static Test20DefNewGc objWillGc;
private static ArrayList list;
// instance vars
private String identStr;
private Object refObj;
public Test20DefNewGc(String identStr){
this.identStr = identStr;
}
// Test20DefNewGc
// refer : https://hllvm-group.iteye.com/group/topic/35798
// vmOpts : -Xint -server -Xmx600m -Xms600m -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -XX:-UseTLAB -XX:+UseSerialGC -XX:CMSInitiatingOccupancyFraction=50 -XX:+PrintGCDetails
// vm调试 : -Xint -server -Xmx600m -Xms600m -XX:PermSize=128M -XX:MaxPermSize=128M -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -XX:-UseTLAB -XX:+UseSerialGC -XX:CMSInitiatingOccupancyFraction=50 -XX:+PrintGCDetails com.hx.test05.Test20DefNewGc
public static void main(String[] args) throws Exception {
// objInOldGen in old gen
objInOldGen = new Test20DefNewGc("objInOldGen");
for(int i=0; i<16; i++) {
touchMinorGc();
}
// objInOldGen.refObj in young gen
objInOldGen.refObj = new Object();
// objInYoungGen in young gen
objInYoungGen = new Test20DefNewGc("objInYoungGen");
objInYoungGen.refObj = new Object();
// objWillGc in young gen
objWillGc = new Test20DefNewGc("objWillGc");
objWillGc.refObj = new Object();
objWillGc = null;
// list in young gen
list = new ArrayList();
list.add(new Test20DefNewGc("objInList"));
list.add(new String("this is second element"));
// objRefByStackVar in youngGen
Test20DefNewGc objRefByStackVar = new Test20DefNewGc("objRefByStackVar");
objRefByStackVar.refObj = new Object();
// invoke final gc
doClone(objInOldGen);
System.out.println(" before final gc ");
touchMinorGc();
}
/**
* touchMinorGc
*
* @return void
* @author Jerry.X.He<970655147@qq.com>
* @date 2020-05-02 21:20
*/
public static void touchMinorGc() {
int _1K = 1 * 1000;
int _10M = 10 * 1000 * _1K;
for(int i=0; i<12; i++) {
byte[] bytes = new byte[_10M];
}
}
/**
* doClone
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2020-04-05 11:01
*/
public static void doClone(Test20DefNewGc obj) {
try {
obj.clone();
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先 在 clone 方法里面打上一个断点, 我们看一下 各个对象的一些情况吧
// Universe::heap()
def new generation total 118016K, used 19532K [0x000000079a800000, 0x00000007a2800000, 0x00000007a2800000)
eden space 104960K, 18% used [0x000000079a800000, 0x000000079bb13070, 0x00000007a0e80000)
from space 13056K, 0% used [0x00000007a1b40000, 0x00000007a1b40000, 0x00000007a2800000)
to space 13056K, 0% used [0x00000007a0e80000, 0x00000007a0e80000, 0x00000007a1b40000)
tenured generation total 483328K, used 1088K [0x00000007a2800000, 0x00000007c0000000, 0x00000007c0000000)
the space 483328K, 0% used [0x00000007a2800000, 0x00000007a29101f0, 0x00000007a2910200, 0x00000007c0000000)
Metaspace used 5504K, capacity 5628K, committed 5760K, reserved 1056768K
class space used 544K, capacity 577K, committed 640K, reserved 1048576K
// com.hx.test05.Test20DefNewGc.class
java.lang.Class
{0x00000007a2800eb0} - klass: 'java/lang/Class'
- ---- fields (total size 16 words):
- private volatile transient strict 'cachedConstructor' 'Ljava/lang/reflect/Constructor;' @12 NULL (0 0)
- private volatile transient strict 'newInstanceCallerCache' 'Ljava/lang/Class;' @16 NULL (0 f450476a)
- private transient 'name' 'Ljava/lang/String;' @20 "com.hx.test05.Test20DefNewGc"{0x00000007a2823b50} (f450476a f4500159)
- private transient 'module' 'Ljava/lang/Module;' @24 a 'java/lang/Module'{0x00000007a2800ac8} (f4500159 f4500148)
- private final 'classLoader' 'Ljava/lang/ClassLoader;' @28 a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x00000007a2800a40} (f4500148 f4504ffe)
- private transient 'packageName' 'Ljava/lang/String;' @32 "com.hx.test05"{0x00000007a2827ff0} (f4504ffe 0)
- private final strict 'componentType' 'Ljava/lang/Class;' @36 NULL (0 f4505c34)
- private volatile transient strict 'reflectionData' 'Ljava/lang/ref/SoftReference;' @40 a 'java/lang/ref/SoftReference'{0x00000007a282e1a0} (f4505c34 0)
- private volatile transient 'genericInfo' 'Lsun/reflect/generics/repository/ClassRepository;' @44 NULL (0 0)
- private volatile transient strict 'enumConstants' '[Ljava/lang/Object;' @48 NULL (0 0)
- private volatile transient strict 'enumConstantDirectory' 'Ljava/util/Map;' @52 NULL (0 0)
- private volatile transient 'annotationData' 'Ljava/lang/Class$AnnotationData;' @56 NULL (0 0)
- private volatile transient 'annotationType' 'Lsun/reflect/annotation/AnnotationType;' @60 NULL (0 0)
- transient 'classValueMap' 'Ljava/lang/ClassValue$ClassValueMap;' @64 NULL (0 f450486c)
- private volatile transient 'classRedefinedCount' 'I' @96 0
- signature: Lcom/hx/test05/Test20DefNewGc;
- fake entry for mirror: 'com/hx/test05/Test20DefNewGc'
- fake entry for array: NULL
- fake entry for oop_size: 16
- fake entry for static_oop_field_count: 4
- private static 'objInOldGen' 'Lcom/hx/test05/Test20DefNewGc;' @112 a 'com/hx/test05/Test20DefNewGc'{0x00000007a282e1c8} (f4505c39 f37625a6)
- private static 'objInYoungGen' 'Lcom/hx/test05/Test20DefNewGc;' @116 a 'com/hx/test05/Test20DefNewGc'{0x000000079bb12d30} (f37625a6 0)
- private static 'objWillGc' 'Lcom/hx/test05/Test20DefNewGc;' @120 NULL (0 f37625e3)
- private static 'list' 'Ljava/util/ArrayList;' @124 a 'java/util/ArrayList'{0x000000079bb12f18} (f37625e3 80ec6079)
// Test20DefNewGc.objInOldGen
com.hx.test05.Test20DefNewGc
{0x00000007a282e1c8} - klass: 'com/hx/test05/Test20DefNewGc'
- ---- fields (total size 3 words):
- private 'identStr' 'Ljava/lang/String;' @12 "objInOldGen"{0x00000007a2825728} (f4504ae5 f37625a4)
- private 'refObj' 'Ljava/lang/Object;' @16 a 'java/lang/Object'{0x000000079bb12d20} (f37625a4 0)
// Test20DefNewGc.objInYoungGen
com.hx.test05.Test20DefNewGc
{0x000000079bb12d30} - klass: 'com/hx/test05/Test20DefNewGc'
- ---- fields (total size 3 words):
- private 'identStr' 'Ljava/lang/String;' @12 "objInYoungGen"{0x000000079bb12d48} (f37625a9 f37625b0)
- private 'refObj' 'Ljava/lang/Object;' @16 a 'java/lang/Object'{0x000000079bb12d80} (f37625b0 0)
// Test20DefNewGc.objWillGc
{0x000000079bb12d90} - klass: 'com/hx/test05/Test20DefNewGc'
- ---- fields (total size 3 words):
- private 'identStr' 'Ljava/lang/String;' @12 "objWillGc"{0x000000079bb12da8} (f37625b5 f37625bc)
- private 'refObj' 'Ljava/lang/Object;' @16 a 'java/lang/Object'{0x000000079bb12de0} (f37625bc 0)
// Test20DefNewGc.list
java.util.ArrayList
{0x000000079bb12f18} - klass: 'java/util/ArrayList'
- ---- fields (total size 3 words):
- protected transient 'modCount' 'I' @12 2
- private 'size' 'I' @16 2
- transient 'elementData' '[Ljava/lang/Object;' @20 a 'java/lang/Object'[10] {0x000000079bb12f80} (f37625f0 1)
// Test20DefNewGc.list.elementData
[Ljava.lang.Object;
{0x000000079bb12f80} - klass: 'java/lang/Object'[]
- length: 10
- 0 : a 'com/hx/test05/Test20DefNewGc'{0x000000079bb12f30}
- 1 : "this is second element"{0x000000079bb12fb8}
- 2 : NULL
- 3 : NULL
- 4 : NULL
- 5 : NULL
- 6 : NULL
- 7 : NULL
- 8 : NULL
- 9 : NULL
// Test20DefNewGc.list.elementData[0]
com.hx.test05.Test20DefNewGc
{0x000000079bb12f30} - klass: 'com/hx/test05/Test20DefNewGc'
- ---- fields (total size 3 words):
- private 'identStr' 'Ljava/lang/String;' @12 "objInList"{0x000000079bb12f48} (f37625e9 0)
- private 'refObj' 'Ljava/lang/Object;' @16 NULL (0 0)
// objRefByStackVar
{0x000000079bb13010} - klass: 'com/hx/test05/Test20DefNewGc'
- ---- fields (total size 3 words):
- private 'identStr' 'Ljava/lang/String;' @12 "objRefByStackVar"{0x000000079bb13028} (f3762605 f376260c)
- private 'refObj' 'Ljava/lang/Object;' @16 a 'java/lang/Object'{0x000000079bb13060} (f376260c 0)
我们先看一下 各个对象的一个情况, 再来看下 各个引用的情况
1. 首先是 Test20DefNewGc.class 结合 Universe::heap() 的信息, 可以看到 它是在 oldGen 里面
2. 然后是 Test20DefNewGc.objInOldGen, 结合 Universe::heap() 的信息, 可以看到 它是在 olGen 里面, 当然结合 程序来推断 也可以这样判断, Test20DefNewGc.objInOldGen.refObj 在 youngGen
3. Test20DefNewGc.objInYoungGen 在 youngGen, Test20DefNewGc.objInYoungGen.refObj 在 youngGen
4. Test20DefNewGc.objWillGc 在 youngGen, Test20DefNewGc.objWillGc.refObj 在 youngGen
5. Test20DefNewGc.list 在 youngGen, Test20DefNewGc.list[0] 在 youngGen, Test20DefNewGc.list[1] 在 youngGen
6. objRefByStackVar 在 youngGen
然后 我们再来看下 各个引用的情况
以上对象 除了 Test20DefNewGc.objWillGc 对应的对象, 其他的对象 都有强引用 引用该对象
Test20DefNewGc.objInOldGen, Test20DefNewGc.objInYoungGen, Test20DefNewGc.list 对应的对象引用 来自于 Test20DefNewGc.class
objRefByStackVar 对应的对象 引用来自于 objRefByStackVar(栈帧中)
所以 我们期望的结果 实际上是 gc 之后, 除了 objWillGc 会被回收之外, 其他的 对象应该 都还是存活的
接下来 我们便来 调试一下 各个对象 在这次 minor gc 中的一些细节吧
Test20DefNewGc.class
因为 Test20DefNewGc.class 持有跨代引用, 因此 它本身也是属于 需要遍历的强引用
Test20DefNewGc.class 本身在 oldGen 里面, 不需要处理
在遍历的过程中 会发现 objInOldGen, objInYoungGen, objWillGc, list 这几个引用(class的其他字段对于本文而言不重要, 暂时忽略)
然后 objInOldGen 因为是在 oldGen 里面处理的时候 会过滤掉(不处理)
另外几个 都会复制到 to space
Test20DefNewGc.objInOldGen
上面 Test20DefNewGc.class 的时候, 并没有处理 Test20DefNewGc.objInOldGen
因为 Test20DefNewGc.objInOldGen 本身也持有 跨代引用, 因此 它本身也是属于 需要遍历的强引用
Test20DefNewGc.objInOldGen 里面又两个 引用, 一个是 identStr, 一个是 refObj
identStr 是出于 oldGen, 处理的时候 会过滤掉(不处理)
refObj 是在 youngGen, 因此 会走 FastScanClosure 的相关业务处理
查看一下 Test20DefNewGc.objInOldGen 的相关数据
(lldb) x 0x7a282f478
0x7a282f478: 79 00 00 00 00 00 00 00 86 1f 01 f8 e5 4a 50 f4 y............JP.
0x7a282f488: a4 25 76 f3 00 00 00 00 79 00 00 00 00 00 00 00 .%v.....y.......
// 定位一下 identStr 的数据 -> objInOldGen
(lldb) x 0x00000007a2825728
0x7a2825728: 79 00 00 00 00 00 00 00 e7 02 00 f8 ae 73 50 f4 y............sP.
0x7a2825738: 00 00 00 00 00 00 00 00 79 00 00 00 00 00 00 00 ........y.......
(lldb) x 0x7A2839D70
0x7a2839d70: 79 00 00 00 00 00 00 00 fa 00 00 f8 0b 00 00 00 y...............
0x7a2839d80: 6f 62 6a 49 6e 4f 6c 64 47 65 6e 00 00 00 00 00 objInOldGen.....
Test20DefNewGc.objInOldGen.refObj 的地址为 0xf37625a4 * 8 = 0x79BB12D20
Test20DefNewGc.objInOldGen.refObj 被复制到 to_space
Test20DefNewGc.objInYoungGen
Test20DefNewGc.objInYoungGen 的引用来自于 Test20DefNewGc.class
Test20DefNewGc.objInYoungGen.identStr 的引用来自于 StringTable, 遍历 Test20DefNewGc.objInYoungGen 的引用列表的时候, 发现 identStr 已经被复制到了 to space, 更新下引用就行
遍历 Test20DefNewGc.objInYoungGen 的引用列表的时候, 发现 identStr 已经被复制到了 to space, 更新下引用就行, 遍历到 Test20DefNewGc.objInYoungGen.refObj 的时候, 将 Test20DefNewGc.objInYoungGen.refObj 复制到 to space
Test20DefNewGc.objWillGc
因为没有引用 引用 这个对象, 因此 这个引用不会被 复制到 to space, 逻辑上 被清理掉了
Test20DefNewGc.list
Test20DefNewGc.list 的引用来自于 Test20DefNewGc.class
遍历 Test20DefNewGc.list 的引用列表的时候, 复制 ArrayList 中的 elementData(Object[])
然后 遍历 Test20DefNewGc.list.elementData 的引用列表的时候, 复制 list[0], list[1]
Test20DefNewGc.list[0]
Test20DefNewGc.list[0].identStr 的引用来自于 StringTable, 遍历 Test20DefNewGc.list[0] 的引用列表的时候, 发现 identStr 已经被复制到了 to space, 更新下引用就行
refObj 为 null
objRefByStackVar
是直接作为 GCROOTS, 复制到了 to space
objRefByStackVar.identStr 的引用来自于 StringTable, 遍历 objRefByStackVar 的引用列表的时候, 发现 identStr 已经被复制到了 to space, 更新下引用就行
遍历 objRefByStackVar 的引用列表的时候, 发现 identStr 已经被复制到了 to space, 更新下引用就行, 遍历到 objRefByStackVarrefObj 的时候, 将 objRefByStackVar.refObj 复制到 to space
完
参考
[讨论] HotSpot VM Serial GC的一个问题
新生代回收调试的一些心得