JVM内存回收过程

版权声明:欢迎转载评论~哈哈哈哈请标明出处呀 https://blog.csdn.net/legendaryhaha/article/details/88606666


上一篇Java内存分配

前言

在上一篇的内存分配中,我们可以知道,在JVM中,堆和方法区是线程共享的,程序计数器、JVM栈和本地方法栈是线程私有的,共享的一般都不能随意的消失,而私有的可以,即这些线程私有的区域可以随着线程的创建而创建,随线程的消亡而消亡。

设想一个实例方法,编译后我们就可以知道它的类结构,要给它多少内存也是确定了的。当它被加载时,JVM就创建一个差不多大小的栈帧来存放它,当方法调用结束后,内存将自动被JVM回收,即它们的入栈和出栈的时间都是确定的,所以我们的GC几乎不用搭理这部分。

而关于线程共享的部分,即堆和方法区,这部分区域一般都是动态加载的,只有程序运行时才能知道哪些对象被创建,另外,它可能被其他对象所引用,这更是增加了回收时间的不确定性。如果不加以打理,久而久之,这些无用对象将极大地占用资源,造成浪费,所以我们的GC管理的是这部分内存区域。


执法者

Java的内存回收,不再依靠程序猿了,所以在JVM启动时,就会自动创建一个守护线程来监测内存是否需要进行清理,它就像一个执法者,随时监测内存状况。注意,即使是Java提供的System.gc()方法,也不能改变它执法者的角色,当我们运行这个方方法时,只是提醒JVM可以进行一次FULL GC(清理整个堆空间和永久代),当什么时候执行还是由守护线程确定的,我们并不能像C/C++那样指定时间的回收内存。


死缓

根据前言所述,我们的GC回收的目标已经确定了,但是我们要怎样确定对象是否能够回收呢?在当今主流的设计中,我们一般是通过一个图的搜索算法来确定的。如图:每个圆圈代表着一个对象,它们之间的连线代表着引用关系,其中,某些对象在创建后,就会被一个GC Roots的对象盯上,并和它建立关系。每一次对 对象是否可用进行分析时,我们都从GC Roots节点出发,而它走过的路则称为引用链,当遍历这些对象时,发现有某个对象不能到达GC Roots,如图中的红圈,它会被判定为不可用的对象,虽然被判处死刑了,但也是缓期执行,表现良好,可能还会被释放。

在这里插入图片描述


死刑

判处死缓后,为了确定死刑的执行与否,我们需要进行一次筛选。筛选的条件就是此对象是否有必要执行finalize()方法,当对象没有重写finalize()或者该对象被finalize()方法执行过了,那么就没救了,它被打上第二次标记,下一次GC之时,便是它的消亡之日,而这期间,再无得到量刑的机会了。

当对象重写了finalize()方法且没有被该方法调用过,那么不会被立刻打上第二次标记,而是放置在一个F-Queue队列中,稍后,JVM会创建一个低优先级的Finalize线程来执行这个方法,这里强调低优先级和执行,是指JVM会触发这个方法,但不提供等待执行完成的保证。这是出于安全的考虑,因为执行finalize时,可能非常缓慢,甚至发生死循环,而队列可能还有人等着判刑呢,不能让他们等太久。

执行finalize()方法的对象有着最后得到救赎的机会,如果此时,它能被引用链上的任何对象重新连接上,那么,恭喜它,它将被移入白名单,脱离即将回收的群体。

整个流程图解

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/legendaryhaha/article/details/88606666