原文:https://blog.csdn.net/u010798968/article/details/72835255

一、名词解释

根搜索算法是JVM用来的判断对象是否存活的算法,此算法基本思路为通过一系列的“GC Roots”对象作为起始点,从这些节点往下搜索,当一个对象和GC Roots不可达时,则该对象是无用的,可被回收的。如下图所示:object5、object6、object7虽然互相有关联,但是他们到GC Roots是不可达的,因此他们都可以被回收。

在java中,可作为GC Roots的对象有:

1.虚拟机栈(栈帧中的本地变量表)中引用的对象;

2.方法区中的类静态属性引用的对象;

3.方法区中常量引用的对象;

4.本地方法栈中JNI(即一般说的Native方法)中引用的对象

二、验证以上可作为GC Roots的对象(此处只做最简单的验证,不涉及很复杂的GCRoots引用链)。

1.验证虚拟机栈(栈帧中的局部变量)中引用的对象 作为GC Roots

/**

* GCRoots 测试:虚拟机栈(栈帧中的局部变量)中引用的对象作为GCRoots

* -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails

*

* 扩展:虚拟机栈中存放了编译器可知的八种基本数据类型,对象引用,returnAddress类型(指向了一条字节码指令的地址)

* @author ljl

*/

public class TestGCRoots01 {

private int _10MB = 10 * 1024 * 1024;

private byte[] memory = new byte[8 * _10MB];

public static void main(String[] args) {

method01();

System.out.println("返回main方法");

System.gc();

System.out.println("第二次GC完成");

}

public static void method01() {

TestGCRoots01 t = new TestGCRoots01();

System.gc();

System.out.println("第一次GC完成");

}

}

控制台打印日志:

[GC [PSYoungGen: 105513K->616K(458752K)] 105513K->82536K(983040K), 0.0945986 secs] [Times: user=0.17 sys=0.06, real=0.09 secs]

[Full GC [PSYoungGen: 616K->0K(458752K)] [ParOldGen: 81920K->82430K(524288K)] 82536K->82430K(983040K) [PSPermGen: 2547K->2546K(21504K)], 0.0273364 secs] [Times: user=0.06 sys=0.01, real=0.03 secs]

第一次GC完成

返回main方法

[GC [PSYoungGen: 15728K->64K(458752K)] 98159K->82494K(983040K), 0.0014739 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[Full GC [PSYoungGen: 64K->0K(458752K)] [ParOldGen: 82430K->510K(524288K)] 82494K->510K(983040K) [PSPermGen: 2546K->2546K(21504K)], 0.0118484 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]

第二次GC完成

第一次GC:

t为局部变量,引用了new出的对象(80M),作为GC Roots,在Minor GC后被转移到老年代中,且Full GC也不会回收该对象,仍保留在老年代中。

第二次GC:

method01方法执行完后,局部变量t跟随方法消失,不再有引用类型指向该对象,该对象在Full GC后,被完全回收,老年代腾出该对象之前所占的空间。

2.验证方法区中的静态变量引用的对象作为GC Roots

/**

* 测试方法区中的静态变量引用的对象作为GCRoots

* -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails

*

* 扩展:方法区存与堆一样,是各个线程共享的内存区域,用于存放已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

* @author ljl

* */

public class TestGCRoots02 {

private static int _10MB = 10 * 1024 * 1024;

private byte[] memory;

private static TestGCRoots02 t;

public TestGCRoots02(int size) {

memory = new byte[size];

}

public static void main(String[] args) {

TestGCRoots02 t2 = new TestGCRoots02(4 * _10MB);

t2.t = new TestGCRoots02(8 * _10MB);

t2 = null;

System.gc();

}

}

控制台打印日志:

[GC [PSYoungGen: 138608K->632K(458752K)] 138608K->82552K(983040K), 0.0684508 secs] [Times: user=0.19 sys=0.06, real=0.07 secs]

[Full GC [PSYoungGen: 632K->0K(458752K)] [ParOldGen: 81920K->82427K(524288K)] 82552K->82427K(983040K) [PSPermGen: 2513K->2512K(21504K)], 0.0162803 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

t2被置为null,Minor GC后t2之前引用的对象(40M)被完全回收;t为静态变量,存放于方法区中,引用了对象(80M),在Minor GC后,被转移到老年代中,且在Full GC后,也不会被回收,继续保留在老年代中。

3.验证方法区中常量引用对象作为GC Roots

/**

* 测试常量引用对象作为GCRoots

* 注意:t修饰符如果只是final会被回收,static final不会被回收,所以static final 才是常量的正确写法

* -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails

* @author ljl

*/

public class TestGCRoots03 {

private static int _10MB = 10 * 1024 * 1024;

private static final TestGCRoots03 t = new TestGCRoots03(8 * _10MB);

private byte[] memory;

public TestGCRoots03(int size) {

memory = new byte[size];

}

public static void main(String[] args) {

TestGCRoots03 t3 = new TestGCRoots03(4 * _10MB);

t3 = null;

System.gc();

}

}

控制台打印日志:

[GC [PSYoungGen: 138608K->688K(458752K)] 138608K->82608K(983040K), 0.0514407 secs] [Times: user=0.13 sys=0.02, real=0.05 secs]

[Full GC [PSYoungGen: 688K->0K(458752K)] [ParOldGen: 81920K->82428K(524288K)] 82608K->82428K(983040K) [PSPermGen: 2515K->2514K(21504K)], 0.0153884 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]

t3被置为null,Minor GC后t3之前引用的对象(40M)被完全回收;t为常量,存放于方法区中,引用了对象(80M),在Minor GC后,被转移到老年代中,且在Full GC后,也不会被回收,继续保留在老年代中。

4.测试成员变量是否可作为GC Roots

/**

* 测试成员变量引用对象是否可作为GCRoots

* -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails

*

* @author ljl

*/

public class TestGCRoots04 {

private static int _10MB = 10 * 1024 * 1024;

private TestGCRoots04 t;

private byte[] memory;

public TestGCRoots04(int size) {

memory = new byte[size];

}

public static void main(String[] args) {

TestGCRoots04 t4 = new TestGCRoots04(4 * _10MB);

t4.t = new TestGCRoots04(8 * _10MB);

t4 = null;

System.gc();

}

}

控制台打印日志:

[GC [PSYoungGen: 138608K->600K(458752K)] 138608K->600K(983040K), 0.0015591 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[Full GC [PSYoungGen: 600K->0K(458752K)] [ParOldGen: 0K->507K(524288K)] 600K->507K(983040K) [PSPermGen: 2513K->2512K(21504K)], 0.0144441 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]

t4被置为null,Minor GC后t4之前引用的对象(40M)被完全回收;t为成员变量,也叫实例变量,不同于类变量(静态变量),前面讲到类变量是存储在方法区中,而成员变量是存储在堆内存的对象中的,和对象共存亡,所以是不能作为GC Roots的,从日志中也可看出t在MinorGC后,跟随t4一起被完全回收。不再占用任何空间。

以上为一个非常简单的可作为GC Roots的对象的验证,不涉及较复杂的GC Roots引用链,其实作为使用者来讲,我们只要知道,哪些对象是可作为GC Roots的,在实际开发过程中要特别注意这些对象,不要让无谓的大对象消耗了资源,拖累了性能。

一、名词解释根搜索算法是JVM用来的判断对象是否存活的算法,此算法基本思路为通过一系列的“GC Roots”对象作为起始点,从这些节点往下搜索,当一个对象和GC Roots不可达时,则该对象是无用的,可被回收的。如下图所示:object5、object6、object7虽然互相有关联,但是他们到GC Roots是不可达的,因此他们都可以被回收。
       
在java中,可作为GC Roots的对象有:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象;
2.方法区中的类静态属性引用的对象;
3.方法区中常量引用的对象;
4.本地方法栈中JNI(即一般说的Native方法)中引用的对象
二、验证以上可作为GC Roots的对象(此处只做最简单的验证,不涉及很复杂的GCRoots引用链)。
1.验证虚拟机栈(栈帧中的局部变量)中引用的对象 作为GC Roots
/** * GCRoots 测试:虚拟机栈(栈帧中的局部变量)中引用的对象作为GCRoots  * -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails *  * 扩展:虚拟机栈中存放了编译器可知的八种基本数据类型,对象引用,returnAddress类型(指向了一条字节码指令的地址) * @author ljl */public class TestGCRoots01 {private int _10MB = 10 * 1024 * 1024;private byte[] memory = new byte[8 * _10MB]; public static void main(String[] args) {method01();System.out.println("返回main方法");System.gc();System.out.println("第二次GC完成");} public static void method01() {TestGCRoots01 t = new TestGCRoots01();System.gc();System.out.println("第一次GC完成");}}
控制台打印日志:[GC [PSYoungGen: 105513K->616K(458752K)] 105513K->82536K(983040K), 0.0945986 secs] [Times: user=0.17 sys=0.06, real=0.09 secs] [Full GC [PSYoungGen: 616K->0K(458752K)] [ParOldGen: 81920K->82430K(524288K)] 82536K->82430K(983040K) [PSPermGen: 2547K->2546K(21504K)], 0.0273364 secs] [Times: user=0.06 sys=0.01, real=0.03 secs] 第一次GC完成返回main方法[GC [PSYoungGen: 15728K->64K(458752K)] 98159K->82494K(983040K), 0.0014739 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC [PSYoungGen: 64K->0K(458752K)] [ParOldGen: 82430K->510K(524288K)] 82494K->510K(983040K) [PSPermGen: 2546K->2546K(21504K)], 0.0118484 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 第二次GC完成第一次GC:
t为局部变量,引用了new出的对象(80M),作为GC Roots,在Minor GC后被转移到老年代中,且Full GC也不会回收该对象,仍保留在老年代中。
第二次GC:
method01方法执行完后,局部变量t跟随方法消失,不再有引用类型指向该对象,该对象在Full GC后,被完全回收,老年代腾出该对象之前所占的空间。

2.验证方法区中的静态变量引用的对象作为GC Roots
/** * 测试方法区中的静态变量引用的对象作为GCRoots * -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails *  * 扩展:方法区存与堆一样,是各个线程共享的内存区域,用于存放已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。 * @author ljl * */public class TestGCRoots02 {private static int _10MB = 10 * 1024 * 1024;private byte[] memory; private static TestGCRoots02 t; public TestGCRoots02(int size) {memory = new byte[size];} public static void main(String[] args) {TestGCRoots02 t2 = new TestGCRoots02(4 * _10MB);t2.t = new TestGCRoots02(8 * _10MB);t2 = null;System.gc();}}

控制台打印日志:[GC [PSYoungGen: 138608K->632K(458752K)] 138608K->82552K(983040K), 0.0684508 secs] [Times: user=0.19 sys=0.06, real=0.07 secs] [Full GC [PSYoungGen: 632K->0K(458752K)] [ParOldGen: 81920K->82427K(524288K)] 82552K->82427K(983040K) [PSPermGen: 2513K->2512K(21504K)], 0.0162803 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
t2被置为null,Minor GC后t2之前引用的对象(40M)被完全回收;t为静态变量,存放于方法区中,引用了对象(80M),在Minor GC后,被转移到老年代中,且在Full GC后,也不会被回收,继续保留在老年代中。

3.验证方法区中常量引用对象作为GC Roots
/** * 测试常量引用对象作为GCRoots  * 注意:t修饰符如果只是final会被回收,static final不会被回收,所以static final 才是常量的正确写法 * -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails * @author ljl */public class TestGCRoots03 {private static int _10MB = 10 * 1024 * 1024;private static final TestGCRoots03 t = new TestGCRoots03(8 * _10MB);private byte[] memory; public TestGCRoots03(int size) {memory = new byte[size];} public static void main(String[] args) {TestGCRoots03 t3 = new TestGCRoots03(4 * _10MB);t3 = null;System.gc();}}
控制台打印日志:[GC [PSYoungGen: 138608K->688K(458752K)] 138608K->82608K(983040K), 0.0514407 secs] [Times: user=0.13 sys=0.02, real=0.05 secs] [Full GC [PSYoungGen: 688K->0K(458752K)] [ParOldGen: 81920K->82428K(524288K)] 82608K->82428K(983040K) [PSPermGen: 2515K->2514K(21504K)], 0.0153884 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] 
t3被置为null,Minor GC后t3之前引用的对象(40M)被完全回收;t为常量,存放于方法区中,引用了对象(80M),在Minor GC后,被转移到老年代中,且在Full GC后,也不会被回收,继续保留在老年代中。

4.测试成员变量是否可作为GC Roots
/** * 测试成员变量引用对象是否可作为GCRoots * -Xms1024m -Xmx1024m -Xmn512m -XX:+PrintGCDetails * * @author ljl */public class TestGCRoots04 {private static int _10MB = 10 * 1024 * 1024;private TestGCRoots04 t;private byte[] memory; public TestGCRoots04(int size) {memory = new byte[size];} public static void main(String[] args) {TestGCRoots04 t4 = new TestGCRoots04(4 * _10MB);t4.t = new TestGCRoots04(8 * _10MB);t4 = null;System.gc();}}
控制台打印日志:[GC [PSYoungGen: 138608K->600K(458752K)] 138608K->600K(983040K), 0.0015591 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC [PSYoungGen: 600K->0K(458752K)] [ParOldGen: 0K->507K(524288K)] 600K->507K(983040K) [PSPermGen: 2513K->2512K(21504K)], 0.0144441 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]t4被置为null,Minor GC后t4之前引用的对象(40M)被完全回收;t为成员变量,也叫实例变量,不同于类变量(静态变量),前面讲到类变量是存储在方法区中,而成员变量是存储在堆内存的对象中的,和对象共存亡,所以是不能作为GC Roots的,从日志中也可看出t在MinorGC后,跟随t4一起被完全回收。不再占用任何空间。

以上为一个非常简单的可作为GC Roots的对象的验证,不涉及较复杂的GC Roots引用链,其实作为使用者来讲,我们只要知道,哪些对象是可作为GC Roots的,在实际开发过程中要特别注意这些对象,不要让无谓的大对象消耗了资源,拖累了性能。

———————————————— 版权声明:本文为CSDN博主「Etyero」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/u010798968/article/details/72835255

最新文章

  1. mysql 命令行还原备份数据库
  2. c++unsigned char的输出问题
  3. 导入Eclipse工程 到 Android Studio
  4. mongodb(一) NoSQL简介
  5. [转] - QThread应用探讨
  6. "淘宝推荐系统简介"分享总结
  7. pcduino+opencv实现人脸追踪摄像头
  8. 20151221jqueryUI---日历UI代码备份
  9. ASP.NET 生命周期(原文翻译)
  10. 如何创建C++程序
  11. C# 基于DotRas的VPN管理
  12. C++学习的心路历程之心理障碍
  13. zoj 1013 Great Equipment DP
  14. 201521123067 《Java程序设计》第7周学习总结
  15. Django 内置分页器
  16. MVC 下拉列表三级联动
  17. VC.遍历文件夹中的文件
  18. Shell IF条件判断解析
  19. java abstract构造函数调用
  20. Servlet笔记8--乱码解决方案

热门文章

  1. PyTorch之DataLoader杂谈
  2. [LeetCode] 60. Permutation Sequence 序列排序
  3. dubbo调用外网接口 注册外网ip到zookeeper 暴露外网ip
  4. 【Tools】UltraISO官网最新板+注册码
  5. 【神经网络与深度学习】【计算机视觉】图解YOLO
  6. Mac和window实现双向数据传输
  7. Fiddler抓包工具如何可以抓取HTTPS
  8. 16.Python略有小成(内置模块Ⅱ)
  9. homebrew 使用代理
  10. java实现rabbitMQ消息收发方式