Jvm的垃圾回收算法详解+第三版PDF分享

3 篇文章 0 订阅
2 篇文章 0 订阅

Jvm中垃圾回收算法(结合深入理解jvm第二版,第三版整理)

深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)PDF分享
链接:https://pan.baidu.com/s/1R1kyn4bfQfQEVvOwS7UHeg
提取码:txdv

下面我们来详细说说这几种算法

1.标记-清除算法

它是最基础的收集算法, 是因为后续的收集算法大多都是以标记-清除算法为基础, 对其缺点进行改进而得到的。
过程:首先从GCROOT开始,向下扫描标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
即 扫描—标记----清除
缺点:第一个是标记和清除两个过程的效率不稳定,堆中包含大量对象, 而且其中大部分是需要被回收的, 这时必须进行大量标记和清除的动作, 导致标记和清除两个过程的执行效率都随对象数量增长而降低;
第二个是内存空间的碎片化问题
还有就是标记清除之后会产生大量不连续的内存碎片,这样在分配大对象的时候,无法找到足够的连续内存提前触发一次GC
在这里插入图片描述

2.复制算法

复制算法是为了解决标记-清除算法面对大量可回收对象时执行效率低
的问题
过程:复制算法将内存分为两个区域,一部分用来为新对象分配内存,另一部分用来在GC的时候将还在存活的对象复制过去,将对象复制过去后,直接将另一部分的区域清空;复制算法主要适用于存活对象少,大量短期对象的情况。(因为复制大量存活的对象很花费时间),主要使用于新生代的垃圾回收。
在这里插入图片描述
现在的商用Java虚拟机大多都优先采用了这种收集算法去回收新生代, IBM公司曾有一项专门研究对新生代“朝生夕灭”的特点做了更量化的诠释——新生代中的对象有98%熬不过第一轮收集。 因此并不需要按照1∶ 1的比例来划分新生代的内存空间。

每次垃圾收集都发现有大批对象死去,只有少量存活,则使用复制算法;如果对堆内存有一定的了解的话,我们知道新生代内存被分为一个较大的Eden区和两个较小的Survivor区,每次只使用Eden区和一个Survivor区,当回收时将Eden区和Survivor还存活着的对象一次性的拷贝到另一个Survivor区上,最后清理掉Eden区和刚才使用过的Survivor区,Eden和Survivor的默认比例是8:1:1。当Survivor空间不足以容纳一次Minor GC之后存活的对象时, 就需要依赖其他内存区域(实际上大多就是老年代) 进行分配担保(Handle Promotion) 。

内存的分配担保也一样, 如果另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象, 这些对象便将通过分配担保机制直接进入老年代, 这对虚拟机来说就是安全的。

标记-复制算法在对象存活率较高时就要进行较多的复制操作, 效率将会降低。 更关键的是, 如果
不想浪费50%的空间, 就需要有额外的空间进行分配担保, 以应对被使用的内存中所有对象都100%存
活的极端情况, 所以在老年代一般不能直接选用这种算法。

3.标记—整理算法

标记—整理算法其实只是在优化后的标记清除算法,解决了标记—整理算法所带来的内存不规整,无法利用空闲区域来存放大对象,提前引起GC的问题。其实只是在标记—清除算法的基础上,多了一个整理内存的过程。
主要过程与标记-清除算法相同,都是先标记从GC Roots向下查询到的对象,然后对未标记的对象进行回收,但是不同的是标记-整理算法最后会将所有存活对象向到一端移动,减少了清除的过程带来的内存碎片。
在这里插入图片描述

4.可达性分析理论(算法)

在深入理解Jvm的第三版中对于可达性分析从算法上升到了理论层次,引入了三种假说
1) 弱分代假说(Weak Generational Hypothesis) : 绝大多数对象都是朝生夕灭的。
2) 强分代假说(Strong Generational Hypothesis) : 熬过越多次垃圾收集过程的对象就越难以消
亡。

其实对于1和2 来说,我们常用的垃圾收集器,在设计原则中收集器都将Jvm根据对象的生命周期将堆内存按"代"分为新生代和老年代,所有jvm可以在对象的不同生命周期可以根据实际情况使用不同的回收算法,这样有利于提高回收效率和成本

-在新生代区域中,每次垃圾收集都发现有大批对象死去,只有少量存活,则使用复制算法;如果对堆内存有一定的了解的话,我们知道新生代内存被分为一个较大的Eden区和两个较小的Survivor区,每次只使用Eden区和一个Survivor区,当回收时将Eden区和Survivor还存活着的对象一次性的拷贝到另一个Survivor区上,最后清理掉Eden区和刚才使用过的Survivor区,Eden和Survivor的默认比例是8:1:1。

  • 老年代中因为对象存活率高、没有足够大的额外空间对存活的对象进行复制,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

3) 跨代引用假说( Intergenerational Reference Hypothesis) : 跨代引用相对于同代引用来说仅占极少数。

但是如果现在只进行一次新生代区域内的收集(Minor GC) , 但新生代中的对象是完全有可
能被老年代所引用的, 为了确认在老年代区域的引用对象是否存活, 就不得不在固定的GC Roots之外, 再额外遍历整个老年代中所有对象来,来确保可达性分析结果的正确性( 反过来也是一样) 遍历整个老年代所有对象
的方案虽然理论上可行, 但无疑会为内存回收带来很大的性能负担。
其实根据前两条假说我们可以逻辑推理一个推论: 存在互相引用关系的两个对象, 是应该同时生存或者同时消亡的。
如果某个新生代对象存在跨代引用, 由于老年代对象难以消亡, 该引用会使得新生代对象在收集时同样得以存活, 进而在年龄增长之后晋升到老年代中, 这时跨代引用也随即被消除了。

依据这条假说, 我们就不应再为了少量的跨代引用去扫描整个老年代, 也不必浪费空间专门记录每一个对象是否存在及存在哪些跨代引用, 只需在新生代上建立一个全局的数据结构( 该结构被称为“记忆集”, Remembered Set) , 这个结构把老年代划分成若干小块, 标识出老年代的哪一块内存会存在跨代引用。 此后当发生Minor GC时, 只有包含了跨代引用的小块内存里的对象才会被加入到GCRoots进行扫描。 虽然这种方法需要在对象改变引用关系( 如将自己或者某个属性赋值) 时维护记录数据的正确性, 会增加一些运行时的开销, 但比起收集时扫描整个老年代来说仍然是划算的。

书围绕着动态内存自动回收的话题,介绍了垃圾收集机制,详细分析了各种算法和相关技术。   本书共12章。第1章首先介绍计算机存储器管理的演化和自动内存回收的需求,并引入了本书所使用的术语和记法。第2章介绍了3种“经典”的垃圾收集技术:引用计数(reference counting)、标记-清扫(mark-sweep)和节点复制(copying)。 随后的4章更详细地讨论了上述这些垃圾收集方式和标记-缩并(mark-compact)收集。第7章和第8章分别介绍了在现代垃圾收集实现中具有重要地位的分代式(generational)垃圾收集和渐进式(incremental)垃圾收集。第9章和第10章扩展了垃圾收集的领域,讨论了如何让垃圾收集能够在无法得到来自语言编译器的支持的环境(分别是C和C++)中运行。第11章讨论了一个相对较新的研究领域 -- 垃圾收集和硬件数据cache的相互作用。第12章简要地考察了用于分布式系统的垃圾收集。   本书适合对动态内存管理感兴趣的读者阅读,可供专业的研究人员参考。 目录: 第1章 简介 1.1 内存分配的历史 1.1.1 静态分配 1.1.2 栈分配 1.1.3 堆分配 1.2 状态、存活性和指针可到达性 1.3 显式堆分配 1.3.1 一个简单的例子 1.3.2 垃圾 1.3.3 悬挂引用 1.3.4 共享 1.3.5 失败 1.4 为什么需要垃圾收集 1.4.1 语言的需求 1.4.2 问题的需求 1.4.3 软件工程的课题 1.4.4 没有银弹 1.5 垃圾收集的开销有多大 1.6 垃圾收集算法比较 1.7 记法 1.7.1 堆 1.7.2 指针和子女 1.7.3 伪代码 1.8 引文注记 第2章 经典算法 2.1 引用计数算法 2.1.1 算法 2.1.2 一个例子 2.1.3 引用计数算法的优势和弱点 2.1.4 环形数据结构 2.2 标记——清扫算法 2.2.1 算法 2.2.2 标记——清扫算法的优势和弱点 2.3 节点复制算法 2.3.1 算法 2.3.2 一个例子 2.3.3 节点复制算法的优势和弱点 2.4 比较标记——清扫技术和节点复制技术 2.5 需要考虑的问题 2.6 引文注记 第3章 引用计数 3.1 非递归的释放 3.1.1 算法 3.1.2 延迟释放的优点和代价 3.2 延迟引用计数 3.2.1 Deutsch-Bobrow算法 3.2.2 一个例子 3.2.3 ZCT溢出 3.2.4 延迟引用计数的效率 3.3 计数域大小受限的引用计数 3.3.1 “粘住的”计数值 3.3.2 追踪式收集恢复计数值 3.3.3 仅有一位的计数值 3.3.4 恢复独享信息 3.3.5 “Ought to be two”缓冲区 3.4 硬件引用计数 3.5 环形引用计数 3.5.1 函数式程序设计语言 3.5.2 Bobrow的技术 3.5.3 弱指针算法 3.5.4 部分标记——清扫算法 3.6 需要考虑的问题 3.7 引文注记 第4章 标记——清扫垃圾收集 4.1 与引用计数技术的比较 4.2 使用标记栈 4.2.1 显式地使用栈来实现递归 4.2.2 最小化栈的深度 4.2.3 栈溢出 4.3 指针反转 4.3.1 Deutsch-Schorr-Waite算法 4.3.2 可变大小节点的指针反转 4.3.3 指针反转的开销 4.4 位图标记 4.5 延迟清扫 4.5.1 Hughes的延迟清扫算法 4.5.2 Boehm-Demers-Weriser清扫器 4.5.3 Zorn的延迟清扫器 4.6 需要考虑的问题 4.7 引文注记 第5章 标记——缩并垃圾收集 5.1 碎片现象 5.2 缩并的方式 5.3 “双指针”算法 5.3.1 算法 5.3.2 对“双指针”算法的分析 5.3.3 可变大小的单元 5.4 Lisp2算法 5.5 基于表的方法 5.5.1 算法 5.5.2 间断表 5.5.3 更新指针 5.6 穿线方法 5.6.1 穿线指针 5.6.2 Jonkers的缩并算法 5.6.3 前向指针 5.6.4 后向指针 5.7 需要考虑的问题 5.8 引文注记 第6章 节点复制垃圾收集 6.1 Cheney的节点复制收集器 6.1.1 三色抽象 6.1.2 算法 6.1.3 一个例子 6.2 廉价地分配 6.3 多区域收集 6.3.1 静态区域 6.3.2 大型对象区域 6.3.3 渐进的递增缩并垃圾收集 6.4 垃圾收集器的效率 6.5 局部性问题 6.6 重组策略 6.6.1 深度优先节点复制与广度优先节点复制 6.6.2 不需要栈的递归式节点复制收集 6.6.3 近似于深度优先的节点复制 6.6.4 层次分解 6.6.5 哈希表 6.7 需要考虑的问题 6.8 引文注记 第7章 分代式垃圾收集 7.1 分代假设 7.2 分代式垃圾收集 7.2.1 一个简单例子 7.2.2 中断时间 7.2.3 次级收集的根集合 7.2.4 性能 7.3 提升策略 7.3.1 多个分代 7.3.2 提升的阈值 7.3.3 Standard ML of New Jersey收集器 7.3.4 自适应提升 7.4 分代组织和年龄记录 7.4.1 每个分代一个半区 7.4.2 创建空间 7.4.3 记录年龄 7.4.4 大型对象区域 7.5 分代间指针 7.5.1 写拦截器 7.5.2 入口表 7.5.3 记忆集 7.5.4 顺序保存缓冲区 7.5.5 硬件支持的页面标记 7.5.6 虚存系统支持的页面标记 7.5.7 卡片标记 7.5.8 记忆集还是卡片 7.6 非节点复制的分代式垃圾收集 7.7 调度垃圾收集 7.7.1 关键对象 7.7.2 成熟对象空间 7.8 需要考虑的问题 7.9 引文注记 第8章 渐进式和并发垃圾收集 8.1 同步 8.2 拦截器方案 8.3 标记——清扫收集器 8.3.1 写拦截器 8.3.2 新单元 8.3.3 初始化和终止 8.3.4 虚存技术 8.4 并发引用计数 8.5 Baker的算法 8.5.1 算法 8.5.2 Baker算法的延迟的界限 8.5.3 Baker的算法的局限 8.5.4 Baker算法的变种 8.5.5 动态重组 8.6 Appel-Ellis-Li收集器 8.6.1 各种改进 8.6.2 大型对象 8.6.3 分代 8.6.4 性能 8.7 应变复制收集器 8.7.1 Nettle的应变复制收集器 8.7.2 Huelsbergen和Larus的收集器 8.7.3 Doligez-Leroy-Gonthier收集器 8.8 Baker的工作环收集器 8.9 对实时垃圾收集的硬件支持 8.10 需要考虑的问题 8.11 引文注记 第9章 C语言的垃圾收集 9.1 根不确定收集的一个分类 9.2 保守式垃圾收集 9.2.1 分配 9.2.2 寻找根和指针 9.2.3 内部指针 9.2.4 保守式垃圾收集的问题 9.2.5 识别错误 9.2.6 效率 9.2.7 渐进式、分代式垃圾收集 9.3 准复制式收集 9.3.1 堆的布局 9.3.2 分配 9.3.3 垃圾收集 9.3.4 分代式垃圾收集 9.3.5 无法精确识别的数据结构 9.3.6 准复制式收集的效率 9.4 优化的编译器是“魔鬼” 9.5 需要考虑的问题 9.6 引文注记 第10章 C++语言的垃圾收集 10.1 用于面向对象语言的垃圾收集 10.2 对C++垃圾收集器的需求 10.3 在编译器中还是在库中 10.4 保守式垃圾收集 10.5 准复制式收集器 10.6 智能指针 10.6.1 在没有智能指针类层次的情况下进行转换 10.6.2 多重继承 10.6.3 不正确的转换 10.6.4 某些指针无法“智能化” 10.6.5 用const和volatile修饰的指针 10.6.6 智能指针的“泄漏” 10.6.7 智能指针和引用计数 10.6.8 一个简单的引用计数指针 10.6.9 用于灵活的垃圾收集的智能指针 10.6.10 用于追踪式垃圾收集的智能指针 10.7 为支持垃圾收集而修改C++ 10.8 Ellis和Detlefs的建议 10.9 终结机制 10.10 需要考虑的问题 10.11 引文注记 第11章 垃圾收集与cache 11.1 现代处理器体系结构 11.2 cache的体系结构 11.2.1 cache容量 11.2.2 放置策略 11.2.3 写策略 11.2.4 特殊的cache指令 11.3内存访问的模式 11.3.1 标记——清扫技术,使用标记位图和延迟清扫 11.3.2 节点复制垃圾收集 11.3.3 渐进式垃圾收集 11.3.4 避免读取 11.4 改进cache性能的标准方法 11.4.1 cache的容量 11.4.2 块大小 11.4.3 相联度 11.4.4 特殊指令 11.4.5 预取 11.5 失误率和总体cache性能 11.6 专用硬件 11.7 需要考虑的问题 11.8 引文注记 第12章 分布式垃圾收集 12.1 需求 12.2 虚拟共享存储器 12.2.1 共享虚拟存储器模型 12.2.2 共享数据对象模型 12.2.3 分布式共享存储器之上的垃圾收集 12.3 与分布式垃圾收集有关的课题 12.3.1 分类原则 12.3.2 同步 12.3.3 鲁棒性 12.4 分布式标记——清扫 12.4.1 Hudak和Keller 12.4.2 Ali的算法 12.4.3 Hughes的算法 12.4.4 Liskov-Ladin算法 12.4.5 Augusteijn的算法 12.4.6 Vestal的算法 12.4.7 Schelvis-Bledoeg算法 12.4.8 Emerald收集器 12.4.9 IK收集器 12.5 分布式节点复制 12.6 分布式引用计数 12.6.1 Lermen-Maurer协议 12.6.2 间接引用计数 12.6.3 Mancini-Shrivastava算法 12.6.4 SPG协议 12.6.5 “Garbage collecting the world” 12.6.6 网络对象 12.6.7 带权引用计数 12.6.8 世代引用计数 12.7 对actor进行垃圾收集 12.7.1 Halstead算法 12.7.2 标记算法 12.7.3 逻辑上集中式的收集器 12.8 引文注记
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值