关于GC之三-gc roots & finalize

对象标识

一个对象什么时候应该被回收呢?现在常用的gc算法有两种方式:

引用计数法

这个是最古老,也是最简单的实现,为每个对象记录其对应的引用数。不过主要问题是对于循环引用的情况,无法识别及回收。解决的办法是:对引用进行分类,比如强strong引用,引用数+1,soft/weak引用,引用数不变,这样可以基本上解决循环引用的问题。

可达性分析

从GC Root开始扫描,所有能到达的节点对象都是有用的,不能被回收。而所有不可到达的对象,均可标记为被回收。

GC Root主要有4类:

  1. 在虚拟机栈(准确的说,是栈帧中的本地变量表)中引用的对象,但是虚拟机栈是线程内部的,所以这种需要扫描所有线程的内存空间,从其中的本地变量表Slot中获取所有reference类型所引用的对象 —— 也就是被线程直接使用到的
  2. 在方法区中的类静态属性引用的对象 ------static属性引用的对象,一般是公用的共享变量
  3. 在方法区中的常量引用的对象 ——— final修饰的属性引用的对象,也是公用的共享变量
  4. 在本地方法栈(其实也在线程内部)中JNI(即一般所说的native方法)引用的对象 ——被线程直接使用到的native方法引用的对象

关于finalize()方法

  • gc只能清除heap上分配的内存,无法处理栈(指java虚拟机栈+native栈,hotspot中合一)中分配的内存,当使用JNI时,可能会在栈中分配内存。
  • 比如java调用C,会使用malloc分配内存。这时候就是分配在栈中。
  • 栈中的内存回收要靠finalize()方法,还是以上面的例子说明,如果JNI调用的C方法自己使用了free()方法,用于释放malloc()申请到的内存,那么还好。否则就要靠finalize(),它会调用free()方法.
  • Object类中的finalize()方法描述如下:
    /**
     * Called by the garbage collector on an object when garbage collection
     * determines that there are no more references to the object.
     * A subclass overrides the {@code finalize} method to dispose of
     * system resources or to perform other cleanup.
     * 

* The general contract of {@code finalize} is that it is invoked * if and when the Java™ virtual * machine has determined that there is no longer any * means by which this object can be accessed by any thread that has * not yet died, except as a result of an action taken by the * finalization of some other object or class which is ready to be * finalized. The {@code finalize} method may take any action, including * making this object available again to other threads; the usual purpose * of {@code finalize}, however, is to perform cleanup actions before * the object is irrevocably discarded. For example, the finalize method * for an object that represents an input/output connection might perform * explicit I/O transactions to break the connection before the object is * permanently discarded. *

* The {@code finalize} method of class {@code Object} performs no * special action; it simply returns normally. Subclasses of * {@code Object} may override this definition. *

* The Java programming language does not guarantee which thread will * invoke the {@code finalize} method for any given object. It is * guaranteed, however, that the thread that invokes finalize will not * be holding any user-visible synchronization locks when finalize is * invoked. If an uncaught exception is thrown by the finalize method, * the exception is ignored and finalization of that object terminates. *

* After the {@code finalize} method has been invoked for an object, no * further action is taken until the Java virtual machine has again * determined that there is no longer any means by which this object can * be accessed by any thread that has not yet died, including possible * actions by other objects or classes which are ready to be finalized, * at which point the object may be discarded. *

* The {@code finalize} method is never invoked more than once by a Java * virtual machine for any given object. *

* Any exception thrown by the {@code finalize} method causes * the finalization of this object to be halted, but is otherwise * ignored. * * @throws Throwable the {@code Exception} raised by this method * @see java.lang.ref.WeakReference * @see java.lang.ref.PhantomReference * @jls 12.6 Finalization of Class Instances */ protected void finalize() throws Throwable { }

上面提到的Reference,这里补充以下:

关于SoftReference,可以在代码里面看到如下描述:

/**
 * Soft reference objects, which are cleared at the discretion of the garbage
 * collector in response to memory demand.  Soft references are most often used
 * to implement memory-sensitive caches.
 ...
 * All soft references to softly-reachable objects are guaranteed to have
 * been cleared before the virtual machine throws an
 * OutOfMemoryError.
 ...
 **/

也就是说,对一个对象的softReference,应用于内存敏感的缓存场景,只有在JVM抛OutOfMemoryError之前才会被回收,也就是会在内存彻底满之前,做Full GC的时候才会被回收掉。

关于WeakReference,同样看代码描述:

/**
 * Weak reference objects, which do not prevent their referents from being
 * made finalizable, finalized, and then reclaimed.  Weak references are most
 * often used to implement canonicalizing mappings.
 *
 * 

Suppose that the garbage collector determines at a certain point in time * that an object is weakly * reachable. At that time it will atomically clear all weak references to * that object and all weak references to any other weakly-reachable objects * from which that object is reachable through a chain of strong and soft * references. At the same time it will declare all of the formerly * weakly-reachable objects to be finalizable. At the same time or at some * later time it will enqueue those newly-cleared weak references that are * registered with reference queues. ... **/

对一个对象的WeakReference 并不会阻止它被回收,只是这个回收并不是马上,而是一个特定时间点。一般是下次GC的时间。

finalize方法实战
普通gc代码

首先写一个普通的调用gc代码

public class FinalizeCase {

    private static Block holder = null;

    // 分配一个200M的内存对象
    static class  Block{

        byte[] _200M = new byte[200 * 1024 * 1024];
    }

    /**
     * JVM args: -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
     * @param args
     */
    public static void main(String[] args){
        holder = new Block();

        holder = null;

        System.gc();
    }
}

加上对应的jvm args,执行后如下:

0.347: [GC (System.gc()) [PSYoungGen: 2674K->496K(38400K)] 207474K->205304K(331264K), 0.0035476 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
0.350: [Full GC (System.gc()) [PSYoungGen: 496K->0K(38400K)] [ParOldGen: 204808K->428K(292864K)] 205304K->428K(331264K), [Metaspace: 3304K->3304K(1056768K)], 0.0098861 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 38400K, used 333K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000)
  eden space 33280K, 1% used [0x0000000795580000,0x00000007955d34a8,0x0000000797600000)
  from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000)
  to   space 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000)
 ParOldGen       total 292864K, used 428K [0x0000000740000000, 0x0000000751e00000, 0x0000000795580000)
  object space 292864K, 0% used [0x0000000740000000,0x000000074006b000,0x0000000751e00000)
 Metaspace       used 3310K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 366K, capacity 388K, committed 512K, reserved 1048576K

可以看到,其中ParOldGen相关的日志如下:

[ParOldGen: 204808K->428K(292864K)]
ParOldGen       total 292864K, used 428K [0x0000000740000000, 0x0000000751e00000, 0x0000000795580000)

在执行System.gc()后,200M的Block对象被回收掉了。

加入finalize

在Block静态内部类中overridefinalize方法,如下:

... 
    // 分配一个200M的内存对象
    static class  Block{

        byte[] _200M = new byte[200 * 1024 * 1024];

        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("invoke finalize");
        }
    }
...

再执行,日志如下:

0.487: [GC (System.gc()) [PSYoungGen: 2674K->528K(38400K)] 207474K->205336K(331264K), 0.0020731 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.489: [Full GC (System.gc()) [PSYoungGen: 528K->0K(38400K)] [ParOldGen: 204808K->205228K(292864K)] 205336K->205228K(331264K), [Metaspace: 3304K->3304K(1056768K)], 0.0109845 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
invoke finalize
Heap
 PSYoungGen      total 38400K, used 998K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000)
  eden space 33280K, 3% used [0x0000000795580000,0x0000000795679b20,0x0000000797600000)
  from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000)
  to   space 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000)
 ParOldGen       total 292864K, used 205228K [0x0000000740000000, 0x0000000751e00000, 0x0000000795580000)
  object space 292864K, 70% used [0x0000000740000000,0x000000074c86b060,0x0000000751e00000)
 Metaspace       used 3312K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 366K, capacity 388K, committed 512K, reserved 1048576K

可以看到,finalize方法确实被调用了

你可能感兴趣的:(关于GC之三-gc roots & finalize)