jvm笔记(三.垃圾收集背景及过程)
1. 概述
前面学习了java虚拟机的内存结构,我们可以发现对于程序计数器、虚拟机栈、本地方法栈这三个区域都是线程隔离的,它们随线程而生,也随线程而灭,所以不需要对这三个区域进行垃圾回收,
它们执行完自己的方法或者线程结束,它们所占用的内存就自然跟着回收了。我们主要关注的是java堆和方法区这两块内存区域存放的对象实例以及类信息的回收。
2. 怎么判断对象是否需要被回收
站在虚拟机的角度,我们需要回收什么样的对象呢,很显然是不再被使用的对象,或者一段时间内该对象的引用没有被别处调用,我们不妨称这些不再被引用的对象已经“死亡”,而还被引用着的对象不用回收。所以主要的条件是如何确定对象还有没有被其他地方引用。
常见的引用判断算法有两种:引用计数法和可达性分析算法。
引用计数法算法流程是这样的:给对象添加一个引用计数器,每当有一个地方引用它时,该计数器的值就加1;当引用失效时,计数器的值就减1;任何时刻计数器为0的对象就是不可能再被使用的对象。
可达性分析法的基本思路是通过一系列“GC Roots”对象作为起始点,从这些节点开时往下搜索,搜索所经过的路径称为引用链,就像一颗树结构一样,多个GC Roots构成的引用链可以称之为森林,当一个对象到GC Roots没有任何引用链相连,即该对象到GC Roots不可达时,它们就会被判定为时可以回收的对象。可以作为GC Roots的对象有:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象以及本地方法栈中JNI引用的对象。
引用的级别对垃圾回收器来说也很重要,级别越高的引用,被回收的可能性越低。引用的级别可以分为以下四种:
1.强引用:新new一个对象的这种代码就是强引用,比如Object obj=new Object();只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象;
2.软引用:用来描述一些还有用但是非必须的对象。比如在上面的代码之后再新加这一行就是软引用SoftReference
3.弱引用:弱引用的强度比软引用还要弱,将2里边的代码换成这个WeakReference
4.虚引用:虚引用是最弱的一种关系,无法通过一个虚引用来取得一个对象实例,该引用存在的目的只有一个就是能够在对象被收集器回收时取到一个系统通知。实现方法同样是在1的代码之后添加这样一行代码PhantomReference
3.对象死亡的过程
对象的一般死亡过程需要经历至少两次标记过程,也可以说有“缓刑”阶段。详细过程如下:如果对象在经过可达性分析后发现自身到GC Roots不可达,则它会被第一次标记并且第一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。如果这个对象被判定为有必要执行finalize方法,则将该对象放入一个F-Queue的队列中,并在之后通过一个finalizer线程去执行它,稍后垃圾收集器将会对该队列中的对象进行二次小规模标记,如果队列中的对象想要活下去,就必须在这个时候与引用链上的得对象关联上,那它就会被移除该队列。如果该对象这时候还没有逃脱,那就会被真的回收。
4.方法区的回收
方法区的回收主要包括两部分:废弃常量和无用的类。废弃常量就是常量池中虽然有该常量但是当前系统中没有任何一个地方在引用该常量,则该常量就会被回收。无用的类的要求很苛刻:该类的所有实例都被回收且加载该类的ClassLoader已经被回收,并且该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。