文档章节

java虚拟机之垃圾回收器

firepation
 firepation
发布于 2018/08/18 16:53
字数 1883
阅读 6
收藏 0

1. 引言

垃圾回收器主要需要完成 3 件事:

  • 哪些内存需要回收
  • 什么时候回收
  • 如何回收

上一篇博客已经介绍了 java 内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈 3 个区域随线程而生,随线程而灭。在这几个区域内就不用考虑回收的问题,因为方法结束或线程结束时,内存自然就随着回收了。

而 java 堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间才能知道会创建哪些对象,这部分内存的分配都是动态的,垃圾收集器所关注的是这部分内存。

2. 对象已死吗

在堆内存里面存放着 java 世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事就是要确定这些对象之中哪些还「存活」着,哪些已经「死去」(即不可能再被任何途径使用的对象)。

2.1 引用计数算法

引用计数算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加 1;当引用失效时,计数器就减 1;任何时刻计数器为 0 的对象就是不可能再被使用的。客观地说,引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,但是,至少主流的 java 虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题

2.2 可达性分析算法

在主流的商用程序语言(Java、C#,甚至包括古老的 List)的主流实现中,都是通过可达性分析来判定对象是否存活的。这个算法的基本思想就是通过一些列的称为「GC Roots」的对象作为起始点,从这个节点开始向下搜索,搜索所走过的路径称为引用链,当一个对向到 GC Root 没有任何引用链相连时,则说明此对象是不可用的,如下图所示。

GC

2.3 再谈引用

判断对象是否存活都与「引用」有关,java 将引用分为强引用、软引用、弱引用、虚引用。四种引用的强度依次逐渐减弱。当内存空间还足够时,这些引用则能保留在内存之中;如果内存空间在进行垃圾收集后还非常紧张,则可以抛弃这些对象。

2.4 生存还是死亡

即使在可达性分析算法中不可达的对向,也并非是「非死不可」,这时候它们暂时处于「缓刑」状态,要真正宣告一个对向死亡,至少要经历两次标记的过程。第一次是进行可达性分析之后发现没有与 GC Roots 相连接的引用链,那么它将被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法或者 finalize 方法已经被虚拟机调用过了,虚拟机将这种情况视为「没有必要执行」。

如果这个对象被判定为有必要执行 finalize 方法,接下来就会被放置一个叫 F-Queue 的队列中,调用对象的 finalize 方法,如果该对象在 finalize 方法中「拯救了自己」--- 只要重新与引用链上的任何一个对象建立连接即可,那么该对象在第二次标记中就会被移除「即将回收」集合。如果这时该对象没有逃脱,那基本上它就真的被回收了。

这里需要注意的是,finalize 方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,因此,使用 try-finally 或者其他方式都可以做得更好、更及时,所以建议大家完全可以忘掉 java 中有这个方法存在。

3. 垃圾收集算法

3.1 标记-清除算法

最基础的垃圾收集算法是「标记-清除」算法,算法分为「标记」和「清除」两个阶段。之所以说这是最基础的算法是因为后续的算法都是在这个算法的基础上改进的。它主要由两个不足:1. 效率问题,标记和清除两个过程的效率都不高,2. 空间问题,清除之后产生大量不连续的内存碎片,空间碎片太多导致以后再内存中需要分配较大对象时,不得不提前触发另一次垃圾收集

3.2 复制算法

为了解决效率问题,「复制」算法出现了。它将内存分为两块,每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到另一块内存上,然后再把已经使用过的内存空间一次清理掉。执行过程如下图所示:

复制算法

只是这种算法的代价太昂贵了,只能使用一半的内存空间。不过问题就是用来解决的,根据 IBM 公司的专门研究表明,新生代中的对象 98% 是「朝生夕死」的,所以不需要按照 1:1 的比例来划分内存空间,而是按照 8:1:1 的比例来划分,每次只使用其中的 9 份,剩下的 10% 作为保留空间在垃圾回收时进行留存对象的复制。这就很好的解决了空间利用的问题。不过,如果留存对象比较多时,就需要依赖其他内存(这里指老年代)进行分配担保了。

3.3 标记-整理算法

复制收集算法在对象留存率较高时就要进行较多的复制操作,效率将会变低。根据老年代的特点,「标记-整理」算法就出现了。标记过程仍然和「标记-清除」算法一样,但是后续步骤不是直接对可回收对象进行整理,而是让所有存活的对象向一端移动,然后直接清理掉端边界外的内存,「标记-整理」算法执行过程如下图所示:

标记整理

3.4 分代收集算法

这种算法没有什么新的思想,只是根据对象存活周期的不同将对向划分为几块。一般是把 java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

以上就是 java 虚拟机中垃圾收集器的介绍,主要摘自《深入理解java虚拟机》一书。

© 著作权归作者所有

共有 人打赏支持
firepation
粉丝 3
博文 29
码字总数 34081
作品 0
福州
程序员
私信 提问
聊聊JAVA虚拟机中的垃圾收集器

前言 JAVA虚拟机的垃圾收集器是虚拟机内存的清道夫,它的存在让JAVA开发人员能将更多精力投入到业务研发上。了解垃圾收集器,并利用好这个工具,能更好的保障服务稳定性。这篇文章通过分析J...

lilugoodjob
2018/07/02
0
0
JVM系列第9讲:JVM垃圾回收器

前面文章中,我们介绍了 Java 虚拟机的内存结构,Java 虚拟机的垃圾回收机制,那么这篇文章我们说说具体执行垃圾回收的垃圾回收器。 总的来说,Java 虚拟机的垃圾回收器可以分为四大类别:串...

陈树义
2018/11/22
0
0
Java虚拟机基础——4内存回收机制

Java虚拟机整体篇幅如下: Java虚拟机基础——1Java的内存模型 Java虚拟机基础——2JVM运行时数据区 Java虚拟机基础——3类加载机制 Java虚拟机基础——4内存回收机制 本篇文章的内容如下: ...

隔壁老李头
2018/10/03
0
0
面试中关于Java虚拟机(jvm)的问题看这篇就够了

最近看书的过程中整理了一些面试题,面试题以及答案都在我的文章中有所提到,希望你能在以问题为导向的过程中掌握虚拟机的核心知识。面试毕竟是面试,核心知识我们还是要掌握的,加油~~~ 下面...

snailclimb
2018/05/12
0
0
12-《深度拆解JVM》之垃圾回收(上)

一、问题引入 你应该听说过这么一句话:免费的其实是最贵的。 Java 虚拟机的自动内存管理,将原本需要由开发人员手动回收的内存,交给垃圾回收器来自动回收。不过既然是自动机制,肯定没法做...

飞鱼说编程
2018/10/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 我都想和他们组成一个家庭了

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @Sharon啊 :分享王菲的单曲《流年》有生之年狭路相逢终不能幸免,手心突然忽然长出纠缠的曲线。 《流年》- 王菲 手机党少年们想听歌,请使劲...

小小编辑
今天
176
6
CentOS7利用systemctl添加自定义系统服务

CentOS7的服务systemctl脚本存放在:/usr/lib/systemd/,有系统(system)和用户(user)之分,需要开机不登陆就能运行的程序,存在系统服务里,即:/usr/lib/systemd/system目录下. CentOS7的每...

linuxprobe16
今天
1
0
RabbitMQ入门

RabbitMQ是一个由erlang开发的基于AMQP(Advanced Message Queue)协议的开源实现。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面都非常的优秀。是当前最主流的消息中间...

watermelon11
今天
19
0
今天的学习

自动加载:方法一 function __autoload( $className ){在这里,完成加载B这个类文件的工作。}class A{} //这是一个类$a1 = new A(); //这里没有自动加载的发生,因为A这个类...

墨冥
今天
4
0
印刷工艺步骤

印刷厂从收到订单到交付整个流程,一般涉及到以下步骤 1.设计(经过软件如cdr,psd,ai等等设计需要印刷的名片,宣传单,画册等物料); 2.排版拼版(在电脑软件这区域完成); 3.出版、出硫...

focusone
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部