垃圾回收策略

版权声明:转载请注明原文链接 https://blog.csdn.net/NVPS_wyj/article/details/82079683

JVM的内存模型分为5个部分:程序计数器、虚拟机栈、本地方法栈、堆以及方法区。

其中程序计数器、虚拟机栈和本地方法栈都是线程私有的,所以只要在线程结束的时候对其进行清理即可。然而,堆和方法区是属于线程共享的区域,何时回收以及如何回收就变成了一个棘手的问题。

堆内存的回收

1.确定需要被回收的对象:当一个对象不被任何一个对象或者变量引用时,那么这个对象就是无效对象,需要被回收。判断一个对象是否为无效对象的方式有两种:引用计数法和可达性分析法。

*引用计数法:给对象中添加一个引用计数器,当这个对象被引用时,引用计数器+1;当引用失效时,计数器-1。任何时刻计数器为0的对象就是不可能被引用的对象,即需要被回收的对象。Java中并没有使用引用计数法来管理JVM内存,因为这个算法很难解决对象之间互相循环引用的问题。举一个例子:

private Object instance = null;
    private static final int _1MB = 1024 * 1024;
    //该成员变量的作用仅仅是占点内存,以便在GC日志中查看对象是否被回收
    private byte[] bigSize = new byte[_1MB];


    public static void testGC(){
        ReferenceCountTest objA = new ReferenceCountTest();
        ReferenceCountTest objB = new ReferenceCountTest();

        objA.instance = objB;
        objB.instance = objA;

        objA = null;
        objB = null;

        //假设在这里进行了垃圾回收,objA和objB对象能否被回收?
        System.gc();
    }

输出GC日志如下:

[GC (System.gc()) [PSYoungGen: 7250K->712K(75776K)] 7250K->720K(249344K), 0.0013991 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 712K->0K(75776K)] [ParOldGen: 8K->636K(173568K)] 720K->636K(249344K), [Metaspace: 3422K->3422K(1056768K)], 0.0044875 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 75776K, used 1951K [0x000000076b580000, 0x0000000770a00000, 0x00000007c0000000)
  eden space 65024K, 3% used [0x000000076b580000,0x000000076b767c68,0x000000076f500000)
  from space 10752K, 0% used [0x000000076f500000,0x000000076f500000,0x000000076ff80000)
  to   space 10752K, 0% used [0x000000076ff80000,0x000000076ff80000,0x0000000770a00000)
 ParOldGen       total 173568K, used 636K [0x00000006c2000000, 0x00000006cc980000, 0x000000076b580000)
  object space 173568K, 0% used [0x00000006c2000000,0x00000006c209f088,0x00000006cc980000)
 Metaspace       used 3435K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 374K, capacity 388K, committed 512K, reserved 1048576K

由[PSYoungGen: 712K->0K(75776K)]可以看出,所有对象均被回收了,并没有因为objA和objB相互引用而不进行回收,也侧面印证了java虚拟机没有使用引用计数法作为GC的算法。

*根搜索算法:通过一系列的名为GC Roots的节点作为起始节点,从这些节点开始向下搜索,搜索走过的路径称为引用链。当一个对象与GC Roots之间没有任何的引用链相连时,证明此对象失效,可以被GC回收。在Java中,可作为GC Roots起始节点的对象包含以下几种:

1.JVM栈(栈帧中的本地变量表)中引用的对象
2.方法区中类静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中引用的对象

Java中使用根搜索算法作为GC判断对象是否需要回收的依据。

2.回收无效对象的过程:JVM通过根搜索算法确定无效的对象之后,将会对其进行回收。当JVM筛选出失效的对象之后并不会立即对该对象进行清除,而是会给其一次重生的机会。

方法区的内存回收

方法区中存放的是类信息、常量以及静态变量等生命周期较长的信息,每次GC只有少部分的信息被清理。垃圾回收对方法区的回收主要是两处:常量的回收和废弃类的回收。

*常量的回收:只要方法区常量池中的常量不被任何变量或者对象引用,那么该常量就会被回收。

*废弃类的回收:判定类是否为废弃类的条件比较复杂,主要为以下三点:

1.保证该类的所有对象都已被清除
2.该类的java.lang.Class对象没有被任何变量或者对象引用,只要一个类被加载进方法区,那么堆中就会产生一个该类的对象:java.lang.Class
3.加载该类的类加载器(ClassLoader)已被回收

猜你喜欢

转载自blog.csdn.net/NVPS_wyj/article/details/82079683