zoukankan      html  css  js  c++  java
  • 那些可作为GC Roots的对象

    原文: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

  • 相关阅读:
    33.数组声明方式(var构造函数) 、检测数组类型、数组的属性(封装好的就一个length)、数组的方法
    31.this指向(写出调用链,找最近对象) this的默认绑定 隐式绑定 显示绑定(call(绑定对象) apply(绑定对象) 当括号内没放绑定对象的时候恢复默认绑定) bind
    31.
    30.函数作用域链 (GO AO 也叫词法作用域链)、 调用栈、调用栈涉及this绑定
    29.包装类(构造函数) 包装类作用及调用栈
    916. Word Subsets
    246. Strobogrammatic Number
    445. Add Two Numbers II
    2. Add Two Numbers
    341. Flatten Nested List Iterator
  • 原文地址:https://www.cnblogs.com/shihaiming/p/11394682.html
Copyright © 2011-2022 走看看