关于GC之三-gc roots & finalize
2019-01-28 本文已影响5人
AlanKim
对象标识
一个对象什么时候应该被回收呢?现在常用的gc算法有两种方式:
引用计数法
这个是最古老,也是最简单的实现,为每个对象记录其对应的引用数。不过主要问题是对于循环引用的情况,无法识别及回收。解决的办法是:对引用进行分类,比如强strong引用,引用数+1,soft/weak引用,引用数不变,这样可以基本上解决循环引用的问题。
可达性分析
从GC Root开始扫描,所有能到达的节点对象都是有用的,不能被回收。而所有不可到达的对象,均可标记为被回收。
GC Root主要有4类:
- 在虚拟机栈(准确的说,是栈帧中的本地变量表)中引用的对象,但是虚拟机栈是线程内部的,所以这种需要扫描所有线程的内存空间,从其中的本地变量表Slot中获取所有reference类型所引用的对象 —— 也就是被线程直接使用到的
- 在方法区中的类静态属性引用的对象 ------static属性引用的对象,一般是公用的共享变量
- 在方法区中的常量引用的对象 ——— final修饰的属性引用的对象,也是公用的共享变量
- 在本地方法栈(其实也在线程内部)中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™ 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方法确实被调用了