深入理解Java虚拟机(三)——垃圾回收策略
所谓垃圾收集器的作用就是回收内存空间中不需要了的内容,需要解决的问题是回收哪些数据,什么时候回收,怎么回收。
Java虚拟机的内存分为五个部分:程序计数器、虚拟机栈、本地方法栈、堆和方法区。
其中程序计数器、虚拟机栈和本地方法栈是线程私有的,所以对于何时回收这三部分内存只需要根据线程的生存周期就可以了。
而堆和方法区是线程共享的,其诞生和销毁伴随的虚拟机的启动和停止,所以需要特定的算法来判断内存是否可以被回收。
堆内存的回收
判断那些对象需要回收
垃圾回收器在回收之前,需要判断那些对象已经不会被使用,那些是需要被使用的。对于那些无效的对象,有两种判定算法:
- 引用技术法:在对象中添加一个引用技术器,每当对象被引用一次时,计数器就加一;当一个引用失效时,计数器减一;当计数器的值为零时,对象是不可能再被使用的,就是无效的。
- 可达性分析算法:通过一列称为“GC Roots”的根对象作为起始节点,根据它们所包含的引用关系,进行向下搜索,对于搜索到的对象都是有效的,没有关联的对象就是无效的。
GC Roots包括:- 虚拟机栈中引用的对象(栈帧中的局部变量表中引用类型变量所引用的对象)
- 方法区中静态属性引用的对象
- 方法区中常量引用对象
- 本地方法栈中JNI(即通常所说的Native方法)引用的对象
- 虚拟机内部的引用(基本数据类型对象的Class对象,异常对象,系统类加载器)
- 所有被同步锁(synchronized)持有的对象
- 反映Java虚拟机内部情况的回调、本地代码缓存
比较:引用计数法虽然简单,但是无法解决循环引用的问题,目前主流的语言采用可达性方法。
回收无效对象的过程
要真正宣告一个对象的死亡,至少需要进行两次标记:当在可达性算法标记之后,发现对象没有引用链,就被第一次标记,随后进程筛选,如果对象的finalize方法没有被重写或者finalize方法被调用过了,就进行第二次标记,这样对象就死了。
如果finalize方法没被调用,虚拟机就会给对象一次重生机会:
- 将对象的finalize方法放进F-Queue队列中;
- 执行队列中的所有finalize方法,虚拟机会以较低的优先级执行这些方法,不会确保所有的方法都被执行了,如果执行的时候超时了,就把产生这个方法的对象直接干掉。
- 如果执行的时候重新将对象与GC Roots产生关联,则对象就被救活了,如果没有,就被干掉。
注意:
强烈不建议使用finalize()函数进行任何操作!如果需要释放资源,请使用try-finally。
因为finalize()不确定性大,开销大,无法保证顺利执行。
方法区的内存回收
由于方法区中存放的信息生命周期较长,每次回收的信息较少,回收的性价比较低,所以方法区就是堆的老年代。
方法区的垃圾收集主要分为两部分内容:
- 废弃的常量
- 不在使用的类型
废弃常量判断
和清除对象类似,如果常量没有被引用,则被清理出常量池。
判断废弃的类型
同时满足三个条件:
- 该类所有的实例被回收。
- 加载该类的类加载器被回收。
- 该类对应的Java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类。只要一个类被虚拟机加载进方法区,那么在堆中就会有一个代表该类的对象:java.lang.Class。这个对象在类被加载进方法区的时候创建,在方法区中该类被删除时清除。
垃圾回收算法
当我们知道内存中哪些区域需要被回收,那么就需要垃圾回收算法来清理这些数据。
分代收集理论
分代收集实质是一套符合大多数程序运行实际情况的经验法则,建立在两个分代假说之上:
- 弱分代假说:绝大多数对象都是朝生夕灭的。
- 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。
- 跨代引用假说,跨代引用相对于同代引用来说仅占极少数。
这两个分代假说奠定了垃圾收集的一致的原则设计:收集器应该将Java堆分为不同的区域,然后将对象依据其年龄,将其分配到不同的区域中进行存储。
Java虚拟机至少把Java堆分为新生代和老年代两个区域。新生代中,每次垃圾回收都会有大量的对象被死去,然后存活少量的对象,将会逐步晋升到老年代中。
新生代中的对象可能会被老年代中的所引用,为了找出新生代中存活的对象,不但要从GC Roots开始扫描,而且还需要遍历整个老年代来获得引用节点,这样为了少量跨代区扫描整个老年代,性价比太低。所以,只需要在新生代中建立一个全局的数据结构记忆集,这个结构把老年代分为若干块,并标识出那些块会存在跨代引用,在GC扫描的时候,扫描这一部分老年代就好了,这样节约时间。
分代收集定义
- 部分收集(Partical GC):指的是不对整个Java堆进行垃圾收集,其中又分为:
- 新生代收集(Minor )
- 老年代收集(Major GC/Old GC)
- 混合收集(Mixed GC)
- 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
标记-清除算法
首先标记所有需要回收的对象,然后统一回收被标记的对象,清除数据。
缺点:执行效率不稳定,标记和清除的效率都随着对象数量的增长而降低,而且会产生内存碎片,无法存储较大的对象。

标记-复制算法
将内存分为两个大小相等的部分,每次使用其中一块,如果这一块使用完了,就将存活的对象复制到另一块,然后将这一块的数据清除。
优点:不会产生内存碎片。
缺点:浪费了一半的内存空间,而且每次要将可用数据复制一下,效率低。

现在绝大多数的虚拟机采用这一算法来回收新生代,因为新生代存活的对象少,每次需要复制的数据就比较少。
解决空间利用率低的问题:重新布局新生代,将其分为一块较大的Eden空间(伊甸园)和两块较小的Survivor1和Survivior2空间,HotSpot对其默认比例是8:1:1。分配内存时,只使用Eden和Survivor1,发生垃圾收集时,将Eden和Survivor1中任然存活的的对象复制到Survivor2上,然后直接清理原有的两个空间,然后将Survivor2 与Survivor1的空间引用互换,继续分配。
这样做,只有10%的新生代空间被浪费。
分配担保
当Survivor空间不足以容纳一次Minor GC之后的存活对象,就需要依赖其他区域进行分配担保。所谓分配担保,就是当Survivor区域内存不够,向老年代借一些空间从来存储对象。
标记-整理算法
在垃圾回收之前,将需要回收的垃圾进行标记,然后在垃圾回收的时候,将没有被标记的数据放到一端,最后清除另一端的数据。

这是一种老年代的垃圾收集算法。
标记-清除算法与标记-整理算法的本质差异在于前者是一种非移动式的回收算法,而后者是移动式的。移动和不移动都会有优缺点,移动会耗时,不移动造成内存碎片,降低吞吐量。移动则内存回收时会更复杂,不移动则内存分配时会更复杂。
关注吞吐量就使用标记整理算法,关注延迟就使用标记清除算法。
吞吐量是指对网络、设备、端口、虚电路或其他设施,单位时间内成功地传送数据的数量。
算法混合使用
让虚拟机平时多数时间都采用标记-清除算法,暂时容忍内存碎片的存在,直到内存空间的碎片化程度已经大到影响对象分配时,再采用标记-整理算法收集一次,以获得规整的内存空间。前面提到的基于标记-清除算法的CMS收集器面临空间碎片过多时采用的就是这种处理办法。
分代收集策略
对象优先再Eden区中分配
主流的垃圾收起器都会采用分代回收算法,将堆内存区分为新生代和老年代。新生代中为了提高内存利用率,减少碎片,采用标记复制算法,将新生代内存空间再细分为Eden、survivor1区和survivor2区。
每次对象创建时,首先会分配到Eden区。
如果Eden区满了,则将对象分配到survivor1区。
如果Eden 和 survivor1区内存满了,就会进行Minor回收,将前者的可用对象标记复制到survivor2区,并清理原有空间,然后将survivor2区的名称与survivor1互换,继续分配新对象。
如果survivor2区域在复制存活对象的时候内存不足,则会启用分配担保,借用老年代的空间,暂时存放存活对象,当标记复完成之后,在将借用空间内的对象复制到Eden区。
大对象直接进入老年代
大对象的定义就是占用连续空间较多的队形,比如数组。
当大对象在新生代中存不下的之后,直接分配到老年代中,这样就能避免大量复制操作。
-XX:PretrnureSizeThreshold参数设置大对象的下限值
该参数用于设置大小超过该参数的对象被认为是“大对象”,直接进入老年代。
注意:该参数只对Serial和ParNew收集器有效。
生命周期较长的对象进入老年代
对象头中具有该对象的年龄计数器,每次新生代中发生一次Minor GC后,存活下来的对象年龄加一,当年龄超过一定的值,就将该对象放入老年代。
-XXMaxTenuringThreshold设置新生代的最大年龄
相同年龄的对象内存超过Survivor区一半的对象进入老年代
在Survivor区中,相同年龄的对象内存超过了Surviror区的一般大小,那么所有相同年龄的对象和超过年龄的对象被放到老年代中,这样就无需等到年龄超过一定值。
Java中引用的种类
- 强引用:平时直接用引用就是强引用,只要存在强引用,则对象不会被回收。
- 软引用:只要内存足够就不会被回收,当出现OOM,就会被回收。通过SoftRerefence实现。
- 弱引用:只要发生垃圾回收,就会被回收。通过WeakReference类实现。
- 虚引用:无法通过虚引用访问到对象的任何属性和方法,唯一的作用时检测垃圾收集器时候进行回收。通过PhantomReference类实现。
深入理解Java虚拟机(三)——垃圾回收策略的更多相关文章
- 深入理解Java虚拟机之垃圾回收篇
垃圾回收简介 Java 会对内存进行自动分配与回收管理,使上层业务更加安全,方便地使用内存实现程序逻辑.在不同的 JVM 实现及不同的回收机制中,堆内存的划分方式是不一样的. 简要地介绍下垃圾 ...
- 深入理解java虚拟机---3垃圾回收机制GC
本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...
- 深入理解java虚拟机【垃圾回收算法】
Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...
- 深入理解java虚拟机(二)-----垃圾回收
做一个java程序员很是幸福,不用管不用的对象如何被回收,但是我认为了解一下也不是坏事. 一.如何判断对象已经死亡? 在进行垃圾回收之前,第一件事肯定是判断对象是否已经死亡.1.引用计数算法给对象添加 ...
- 【深入理解Java虚拟机】垃圾回收
引用计数算法 给对象加一个计数器,引用一次+1,引用时效就-1,当计数器=0时对象就不能再被使用: 实现简单,判定效率高:Java虚拟接没有使用,主要原因是很难解决对象之间循环引用问题: GC算法: ...
- 每日一问:讲讲 Java 虚拟机的垃圾回收
昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...
- Java虚拟机之垃圾回收详解一
Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这 ...
- Java虚拟机之垃圾回收
简述 Java与那些较传统的语言比如C++有个很大不同就是垃圾回收策略了.前者通常是虚拟机自动帮我们做了,而后者就需要我们手动来完成. Java虚拟机帮我们完成了垃圾回收,是不是意味着我们就不用完全去 ...
- Java 虚拟机 - GC 垃圾回收机制分析
Java 垃圾回收(Garbage Collection,GC) Java支持内存动态分配.垃圾自动回收,而 C++ 不支持.我想这可能也是 为什么 Java 脱胎于 C++ 的一个原因吧. GC 的 ...
随机推荐
- Java的强引用、软引用、弱引用、虚引用
背景 工程中用到guava的本地缓存.它底层实现和API接口上使用了强引用.软引用.弱引用.所以温故知新下,也夯实下基础. 预备知识 先来看下GC日志每个字段的含义 Young GC示例解释 [GC ...
- Tomcat配置管理员用户的账户和密码之快速访问系统。
技巧前提:当我们在Tomcat服务器下部署项目后(运行中),有时候不知道项目的访问路径或者不想去server.xml里面找对应的访问路径时. 我们可以用以下方法来快速找到对应访问路径: 步骤一 ...
- ceph的rbd备份软件ceph-backup
teralytics是一家国外的大数据公司,这个是他们开源的ceph的备份的工具,在twitter上搜索相关信息的时候看到,觉得不错就拿来试用一番 这是个什么软件 一个用来备份 ceph 的 rbd ...
- 《Machine Learning in Action》—— Taoye给你讲讲决策树到底是支什么“鬼”
<Machine Learning in Action>-- Taoye给你讲讲决策树到底是支什么"鬼" 前面我们已经详细讲解了线性SVM以及SMO的初步优化过程,具体 ...
- 阿里巴巴已offer:Java实习五面详细面经(附解答)
1.岗位 Java后台开发实习生 2.时间表 2020/3/18 提交简历 & 测评 2020/3/23 笔试 2020/3/26 简历面 2020/4/11 技术一面 2020/4/14 技 ...
- Python:利用Entrez库筛选下载PubMed文献摘要
一个不是学生物的孩子来搞生物,当真是变成了一块废铁啊,但也是让我体会到了一把生物信息的力量. 废话不多说,开整! 任务:快速高效从PubMed上下载满足条件的文献PMID.标题(TI).摘要(AB). ...
- Mac系统应该用什么软件进行清理?
作为一枚资深的Windows系统使用者,小编刚刚转向Mac系统的怀抱时,各种不适应,Windows系统中普遍使用的360清理软件目前暂时没有Mac版本的,这就让小编很是头疼了,大家的Mac都是用的什么 ...
- Guitar Pro小课堂——如何进行消音
在我们弹吉他时,消音技术是必须掌握的一项吉他技能.在我们遇到休止符时.乐曲结束时.乐段,乐句中止时.吉他旋律的分句,呼吸处:变换和弦时的低音(尤其是空弦低音).断奏.弹奏强音时其他空弦被激起的共鸣音( ...
- CorelDRAW:油漆滚轮及LOGO设计
小马坐在电脑前,看着自己画的油漆滚轮Logo,既生气又无奈.为了这个油漆Logo,小马用了四.五个不同的设计软件,也画了不下10个图案,就没有一个满意的."明天就要交稿了,现在都11点多了, ...
- FL Studio通道窗口和步进音序器知识讲解
FL Studio中通道窗口是以样本为概念的音乐制作基础.通道窗口包含了步进音序器并结合了各种丰富的功能,可以通过通道窗口打开各通道的设置窗口.钢琴卷轴等.步进音序器在节奏制作方便表现出很好的优势,它 ...