垃圾回收区域:
Java内存运行时,运行区域包括程序计数器、虚拟机栈、本地方法栈、堆、方法区,其中程序计数器、虚拟机栈、本地方法栈是线程私有的,随线程而生,随线程而灭。栈中的栈帧随着方法的进入及退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存在类结构确定下来时就已知。因此这几个区域的内存分配和回收都具备确定性,在这几个区域就不必过多考虑回收的问题,因为方法结束时或者线程结束时,内存自然就跟着回收了。
堆与方法区不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序运行期间才知道创建哪些对象,这部分的内存分配和回收是动态的,垃圾收集器所关注的也是这部分内存。
对象存活判断:
垃圾收集器回收对象,首先要判断对象是否“存活”,即被其它途径使用的对象。一般的算法是引用计数法,给对象添加一个引用计数器,当有地方引用这个对象时,计数器就加一,当引用失效时,计数器减一,任何时候计数器为零的对象不可能再被使用的。引用计数算法简单,但其无法解决互相引循环用的问题。即两个对象不可能再被访问,但它们相互引用对方,导致引用计数器不为零,无法被垃圾收集器回收。
在主流的商用程序语言中的主流实现中,都是通过可达性分析来判定对象是否存活。这个算法是通过一系列称为“GC ROOT”的作为起点,从这些起始点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC ROOT之间不存在引用链时,则此对象不可用。一个不可用的对象并非“非死不可”,这时候处于“缓刑”阶段,要真正宣告死要经历两次标记阶段。
如果一个对象和GC ROOT之间不存在引用链,则将会被标记一次并且进行一次筛选,筛选的条件是此对象是否有finalize()方法,当对象没有覆盖finalize()方法时或者finalize()已经被调用过,虚拟机将这两种情况都视为“没有必要执行”。
当被虚拟机认为有必要执行finalize()时,那么对象会被放入F-Queue队列之中,并且之后由虚拟机自动建立的F-Queue线程执行它。GC会对F-Queue中的对象进行第二次标记。finalize()是对象逃离“死亡”的最后一次机会,只需将自己与引用链上的任何对象建立关联,即可拯救自己。如果第二次标记后没有拯救成功,那基本就真的被回收了。
一般作为GC ROOT 的对象有下面几种:
- 虚拟机栈中引用的对象。
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(一般说Native)引用的对象。