jvm中有各种的垃圾收集器,每个收集器都有各自的算法。但是一切的根本都需要找到找到应该被消除的对象,理解如何找到死亡对象才是理解垃圾收集器的基础。
01两个基本算法
a、引用记数法:对象中加一个引用计数器,每次被引用计数器加一,引用失效减一,当减到0的时候就不会在被再引用了,就可以回收了。
优点:原理简单,效率高。
缺点:有很多例外情况要用大量额外的处理,比如两个对象相互引用。
b、可达性分析:通过一系列“GC Roots”的根对象为起始,根据引用关系向下搜索,搜索路径形成引用链,而那些没有在任何引用链上的叫做不可达对象,都是不可能被再次使用的。如下图,红色部分就是需要回收的。
两种算法对比如下:
02GC Roots包含哪些
可达性分析首先要确认的是GC Roots,只有选择合适的GC Roots才能真正的找到应该存在的对象和排除不应该存在的对象。GC Roots主要包含以下:
a、虚拟机栈中的对象,就是各个线程的方法里面的方法参数、局部变量、临时变量。
b、方法区中类的静态属性引用的对象。
c、方法区中常量引用的对象,比如字符串常量池的引用。
d、本地方法栈引用的对。
e、虚拟机内部的引用,基本类型对应的class对象,常驻异常对象,系统类加载器。
f、所有被同步锁(synchronized)持有的对象。
g、Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存。
可以把上面的大概分成几个方面,如下图:
GC Roots实际上就是当前JVM必须要的对象,可以分成三类。
第一类是JVM线程里面直接引用的对象,相当于正在执行的方法里面的对象,这些肯定是必须存活的。
第二类是设置的一些静态常量,比如我们在类里面用static final修饰的一些对象,这类对象至jvm启动到结束都会一直存在。
第三类就是JVM本身所需要的对象,这类对象肯定不能被回收。
03Java的引用
可达性分析就是根据引用来判断的,那么Java中有哪些引用呢?一共分4类引用:
a、强引用:常见的引用赋值,垃圾收集器不回收。ObjectA a=new ObjectA();
b、软引用:还有用,但非必须。在系统将要发生内存溢出前,把他们列为回收范围进行二次回收,JDK1.2后SoftReference类实现。
c、弱引用:非必须,比软引用更弱。下次垃圾收集器无论内存是否足够,均回收。JDK1.2后WeakReference类实现。
d、虚引用:最弱引用关系,虚引用不影响对象的生存,也无法通过虚引用来取得对象实例。唯一作用是可以在垃圾收集器回收前收到一个系统通知。JDK1.2后PhantomReference类实现。
对比如下图:
后面三个引用我们平时使用几乎没有,多在一些源码中出现,依靠3个类实现:SoftReference、WeakReference、PhantomReference。在看到这三个类的时候能知道对应的含义。
04两次标记
当一个对象被标记为不可达对象的时候并不一定是马上被回收的。如果对象重写了finalize()方法,并且finalize()方法还没有被执行过(finalize()方法只会被执行一次),那么这个对象会被放入F-Queue队列里面。会有一个Finalizer线程去执行队列里面对象的finalize()。如果finalize()方法把这个对象重新赋值给了其他变量,就叫做逃脱成功。
垃圾收集器会对F-Queue队列里的对象进行第二次标记,如果在上一步中成功逃脱的就会移除即将回收的集合。
对于逃脱的对象,在下次被标记成不可达对象时,就会被直接回收,因为finalize()方法已经执行过一次了。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!