JVM的垃圾回收对于Java开发人员来说是比较透明的,本文采用问答的形式进行展开,希望能够解释下垃圾回收的一些问题。那么首先第一个问题
问:什么样的对象会被回收。
答:已经死亡的对象,不可达的对象,肯定会被回收。
接着问:那么怎么判定对象的死亡与不可达?
答:判定的算法有两种:引用计数法和可达性分析算法。
引用计数法:给对象中添加一个引用计数器,每当有一个地方引用他时,计数器值就+1,;当引用失效时,计数器值就-1;任何时刻计数器为0的对象就是不可能在被使用。 无法解决对象之间互相引用的情况。
可达性分析算法:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的.
问:那么什么又是GC Roots对象呢?
答:
1.System Class ----------Class loaded by bootstrap/system class loader. For example, everything from the rt.jar like java.util.* (系统类)
2.JNI Local ----------Local variable in native code, such as user defined JNI code or JVM internal code.
3.JNI Global ----------Global variable in native code, such as user defined JNI code or JVM internal code.
4.Thread Block ----------Object referred to from a currently active thread block. (一个对象存活在一个阻塞的线程中)
5.Thread ----------A started, but not stopped, thread.(一个正在运行的线程)
7.Busy Monitor ----------Everything that has called wait() or notify() or that is synchronized. For example, by calling synchronized(Object) or by entering a synchronized method. Static method means class, non-static method means object.(用于同步的监控对象)
8.Java Local ----------Local variable. For example, input parameters or locally created objects of methods that are still in the stack of a thread.(java 本地变量,输入参数,在线程方法本地创建的对象)
9.Native Stack ----------In or out parameters in native code, such as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots. For example, parameters used for file/network I/O methods or reflection.( 方法区中的类静态属性引用的对象 ,方法区中的常量引用的对象 )
10.Finalizable ----------An object which is in a queue awaiting its finalizer to be run.(存在finalizer 对象的列表中)
11.Unfinalized ----------An object which has a finalize method, but has not been finalized and is not yet on the finalizer queue.(对象有finalize方法,但是没有被执行finalize方法,并且没有在finalizer的队列中)
12.Unreachable ----------An object which is unreachable from any other root, but has been marked as a root by MAT to retain objects which otherwise would not be included in the analysis.(不可达对象)
13.Java Stack Frame ----------A Java stack frame, holding local variables. Only generated when the dump is parsed with the preference set to treat Java stack frames as objects.
14.Unknown ----------An object of unknown root type. Some dumps, such as IBM Portable Heap Dump files, do not have root information. For these dumps the MAT parser marks objects which are have no inbound references or are unreachable from any other root as roots of this type. This ensures that MAT retains all the objects in the dump.[1]
就是说,被以上这些对象引用的对象,都是可达的
一般来说,System Class(系统类),Thread Block(一个对象存活在一个阻塞的线程中) ,Thread(线程),Busy Monitor (例如 synchronized(Object)),Java Local (本地变量),Finalizable (存在finalizer 对象的列表中),这几个我们较为常见。
问:java中的主流虚拟机HotSpot采用可达性分析算法来判断一个对象是否需要进行回收。那么,它是如何实现可达性算法的呢?
答:HotSpot首先需要枚举所有的GC Roots根节点,虚拟机栈的空间不大,遍历一次的时间或许可以接受,但是方法区的空间很可能就有数百兆,遍历一次需要很久。更加关键的是,当我们遍历所有GC Roots根节点时,我们需要暂停所有用户线程,因为我们需要一个此时此刻的”虚拟机快照”,找到此时此刻的可达性分析关系图。基于这种情况,HotSpot实现了一种叫做OopMap的数据结构,存储GC Roots对象,同时并不是每个指令都会对OopMap进行修改,这样OopMap很庞大,这里Hotspot引入了安全点,safePoint,只会在Safe Point处记录GC Roots信息
问:如何让用户线程在接近SafePoint的时候完成停顿?
答:有两种方案,一种抢先式中断(Preemptive Suspension)和主动式中断(Voluntary Suspension),其中抢先式中断不需要线程的执行代码主动去配合,在GC发生时,首先把所有线程全部中断,如果发现有线程中断的地方不在安全点上,
就恢复线程,让它“跑”到安全点上。这种方式比较粗暴,现在基本没有采用。现在主要采用的是主动式中断:当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起。
关于GC roots的一些理解介绍到这里