深入理解java虚拟机之判别Java对象存活算法
优采云 发布时间: 2020-08-18 12:33深入理解java虚拟机之判别Java对象存活算法
1、深入理解java虚拟机之java显存区域
在前一篇博客中早已早已详尽剖析了java显存运行时各个区域,其中程序计数器、虚拟机栈、本地方式栈随着线程而生,随着线程而战败,操作数栈中的栈帧随着方式的执行的有条不紊地进行着入栈和出栈的操作,每个栈帧中分配多少显存基本上在类结构确定出来时就早已确定了大小了,因此这几个区域的显存分配和回收都具备确定性,在这个几个区域中就不需要过多的考虑显存分配的问题了,因为随着线程的结束,内存在然而然地就早已被回收了。而java堆和技巧县不一样,一个插口中多个实现类所须要的显存可能不一样,一个方式中多个分支须要的显存的大小也有可能是不同的,我们在程序的运行期能够确定什么对象须要创建,创建所需显存是多大,这些显存都是动态分配的,垃圾回收器考虑的就是这部份的显存,接下来我们将围绕以下3个问题来展开描述java垃圾搜集器是怎样手动回收显存的:
1、哪些显存须要回收?
2、什么时候回收?
3、如何回收?
本片博客就围绕这第一个问题展开说明,其余连个问题将在前面的博客中一一解读;
首先,Java垃圾回收器的一个想要解决的问题是什么样的显存须要被回收呢?Java垃圾搜集器觉得,当一个对象再无被其它对象引用时可以觉得这个对象可以回收了。那么垃圾搜集器是如何晓得对象的死亡的还是存活的呢?目前,Java虚拟机有两种算法来确定什么对象是无用的,需要被回收的。
1.引用计数法
该算法的思路是给每位对象都添加一个引用计算器,每当有其它对象引用时计数器就+1,当引用失效时计数器-1,任何时刻当该对象的引用数为0的时侯,则判断这个对象不会再被使用了,可以将该对象回收了。这种算法实现上去很简单,效率也十分高,但是并没有被Java所采用,原因是这些算法很难解决对象互相引用的问题。看一下下边反例的代码:
public class Test {
private Object instance = null;
private static final int _1MB = 1024 * 1024;
/** 这个成员属性唯一的作用就是占用一点内存 */
private byte[] bigSize = new byte[2 * _1MB];
public static void main(String[] args)
{
Test A= new Test();
Test B = new Test();
A.instance = B;
B.instance = A;
A = null;
B = null;
System.gc();
}
}
运行结果:
[GC (System.gc()) 9299K->720K(249344K), 0.0010947 secs]
[Full GC (System.gc()) 720K->627K(249344K), 0.0075042 secs]
上面的事例中A和B互相引用,它们的引用计数并不为0,但是执行System.gc()方法后一直被回收了,这说明了Java垃圾搜集器并不是使用引用计数法的。
2.可达性剖析算法
可达性剖析算法就是Java垃圾搜集器判定‘垃圾’对象的算法。基本思路是通过一系列的”GC Roots“ 的对象作为起点,从那些节点开始向上搜索,搜索所走过的路程称作引用链,当一个对象没有任何引用链与”GC Roots“有链接时,那么可以判断这个对象是无用的对象。可以作为GC Roots对象的包括一下几种:
1)Java虚拟机栈中局部变量表引用的对象;
2)本地方式栈中JNI所引用的对象;
3)方法县中的静态变量;
4)方法县中常量引用的对象;
下图为GC Roots:
图中obj1 ~ obj7都还能直接或间接地与GC Roots有联接,因此她们是存活对象,不会被GC回收。obj8基本上可以被回收了,obj9和obj10其实有互相引用,但是她们的引用链中并没有达到GC Roots,因此也会判断为非存活对象。
3.引用的四种状态
在jdk1.2之前,Java的引用类型是比较简单的,只有被引用和未引用两种状态,类型过分简单的话不利于垃圾采集器的回收,当早已将未被引用的对象都被回收以后显存一直比较紧张时,垃圾搜集器将难以确认被引用的对象是否须要回收,哪些对象可以被回收等。jdk1.2后Java对引用类型进行的拓展,包括:强引用、软引用、弱引用和虚引用四种情况,四者的引用硬度依次递减。这样在虚拟机中显存使用不同的情况下,分别回收不容引用类型的对象。
1)强引用
在程序中我们直接new下来的对象的引用都属于强引用,比如:Object obj = new Object();只要强引用还在,就不会被GC回收。
2)软引用
描述部份部份有用但非必须的对象。在系统即将发生显存溢出的时侯,虚拟机会尝试从这部份的引用类型中进行二次回收,如果二次回收后的显存仍不够用就会抛出显存溢出异常。Java中的类SoftReference表示软引用。
3)弱引用
描述被必须对象。被弱引用关联的对象只能生存到下一次回收之前,在垃圾采集器工作以后,无论显存是否够用,这类的对象就会被回收掉。Java中的类WeakReference表示弱引用。
4)虚引用
被虚引用关联的对象在被回收时会收到系统的通知。被虚引用关联的对象,和其生存时间完全没关系。Java中的类PhantomReference表示虚引用。
对于可达性剖析算法,对于这些没有与GC Roots关联的对象并非是立刻回收的,而是经历过两次标记后依然没有与GC Roots关联上,此时就会回收该对象。对象在进行可达性剖析后若果没有与GC Roots关联,则会进行第一次标记和第一次筛选,筛选条件为是否有必要执行finalize方式,比如假如finalize方式是否被覆盖,或是否已被执行一次。如果没有被覆盖或已被执行了,基本可以确认该对象会被回收了。否则,将这个对象放在F-Queue队列中。对F-Queue队列二次标记,如果在此次标记中对象成功关联上GC Roots,则该对象挽救了自己,将从”即将回收“的集合中移除,否则将会被回收。然而,对象只能挽救自己一次,第二次都会被回收了。