关于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.
     * <p>
     * The general contract of {@code finalize} is that it is invoked
     * if and when the Java&trade; 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.
     * <p>
     * The {@code finalize} method of class {@code Object} performs no
     * special action; it simply returns normally. Subclasses of
     * {@code Object} may override this definition.
     * <p>
     * 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.
     * <p>
     * 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.
     * <p>
     * The {@code finalize} method is never invoked more than once by a Java
     * virtual machine for any given object.
     * <p>
     * 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
 * <code>OutOfMemoryError</code>.
 ...
 **/

也就是说,对一个对象的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.
 *
 * <p> Suppose that the garbage collector determines at a certain point in time
 * that an object is <a href="package-summary.html#reachability">weakly
 * reachable</a>.  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方法确实被调用了

猜你喜欢

转载自blog.csdn.net/weixin_33691598/article/details/87161883