JVM GC(垃圾收集算法/垃圾收集器)
2021-03-02 11:09:37 4 举报
AI智能生成
GC垃圾收集,复制算法,标记清除算法,标记整理算法
作者其他创作
大纲/内容
标记垃圾对象
引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;
当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是可回收的
当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是可回收的
可达性分析算法
GC Roots
两个栈: Java栈 和 Native 栈中所有引用的对象
两个方法区:方法区中的常量和静态变量
所有跨代引用的对象
和已知 GCRoots 对象同属一个CardTable 的其他对象
引用类型
强引用:只要强引用还在,垃圾收集器就不会回收
软引用:在内存不足的情况下,发生内存溢出之前回收
弱引用:非必须对象,只要发生GC就会把它干掉
应用:ThreadLocal对象使用弱引用来防止内存泄露
虚引用:相当于没有引用,被jvm干掉会收到系统通知
作用:用于管理直接内存(堆外内存)
应用:Netty中零拷贝(Zero Copy)
不可达对象一定会被回收吗?
三色标记
点击跳转 👉
垃圾收集算法
复制算法
此算法把内存空间划为两个相等的区域,每次只使用其中的一个区域,
垃圾回收时,遍历当前使用区域,把还存活的对象复制到另外一个区域
垃圾回收时,遍历当前使用区域,把还存活的对象复制到另外一个区域
优缺点
存活对象少时效率高,不产生碎片
可用内存变为原来的一半,存活对象多时效率低
标记清除
此算法执行分两阶段:第一阶段从引用根节点开始标记所有被引用
的对象,第二阶段遍历整个堆,把未标记的对象清除
的对象,第二阶段遍历整个堆,把未标记的对象清除
优缺点
相比于复制算法不浪费内存
会产生碎片,效率低,因为要扫描两次
标记整理
分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个
堆,清除未标记的对象并且把存活对象“压缩”到堆的其中一块,按顺序排列
堆,清除未标记的对象并且把存活对象“压缩”到堆的其中一块,按顺序排列
优缺点
避免了碎片问题和空间占用问题
效率比复制算法低,因为要多维护一个链表使幸存对象连续
分代回收
新生代
复制算法
一个Eden区、两个Survival区(From Survival、To Survival)(8:1:1)
大多数情况下对象在Eden区中分配,当Eden区没有足够的空间时,发起一次Minor GC
内存分配
回收策略
回收策略
大对象直接进入老年代
为了降低大对象分配内存时造成的内存复制的开销
长期存活的对象进入老年代
对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁
动态对象年龄判断
Minor GC之后,如果检测到可能长期存活的对象,则让其尽早迸入老年代
老年代空间分配担保机制
Minor GC之前做风险判断,是否允许担保失败,如不允许则改为Full GC
老年代
标记-清除/整理-算法
Major GC
整个Java堆
方法区/永久代/元空间
对永久代的回收主要包括废弃的常量和无用的类
永久代和元空间的区别在于永久代位于JVM的方法区
中,元空间并不在虚拟机内存中,而是使用本地内存
中,元空间并不在虚拟机内存中,而是使用本地内存
Full GC
垃圾收集器
分代模型
新生代
Serial
简单高效,单线程,收集时暂停其他所有线程(STW)
ParNew
Serial的多线程版本,是首选新生代收集器,可配合CMS
Parallel Scavenge
以吞吐量为优先的多线程收集器,不支持CMS
老年代
Serial Old
Serial的老年代版本,可作为CMS的后备预案
Parallel Old
Parallel Scavenge的老年代版本,多线程,吞吐量优先
CMS
并发收集、低停顿
适用于当今互联网网站或基于浏览器的B/S系统的服务器上,这类应用通常都
较为关注服务的响应速度,尽量缩短系统的停顿时间,给用户更好的交互体验
较为关注服务的响应速度,尽量缩短系统的停顿时间,给用户更好的交互体验
流程
初始标记:标记GC Roots能直接关联到的对象,速度很快,STW
并发标记:从GC Roots的直接关联对象开始遍历整个对象图,耗时长
重新标记:修正并发标记期间可能造成的改动,相比初始标记STW略长
并发清除:清除标记阶段已经死亡的对象,此阶段和用户线程并发工作
缺点
对CPU敏感,并发阶段虽然不会导致用户线程停顿,但却由于占用资源导致应用程序变慢,降低总吞吐量
由于CMS在清理时与用户线程并发,运行期间还伴随有新垃圾产生,有可能触发担保机制而产生较大停顿
基于标记清除算法,造成大量的空间碎片,导致没有足够的连续空间存放大对象,不得不提前触发Full GC
组合使用
Parallel Scavenge + Parallel Old(JDK1.8默认)
分区模型
G1:逻辑分代
物理不分代
物理不分代
引入分区的思路,弱化了分代的概念
把连续的Java堆划分为多个大小相等的独立区域(Region)
专门用来存放大对象的区域(Humongous)
流程
初始标记:标记GC Roots能直接关联到的对象,需要停顿用户线程,但时间很短
STW
并发标记:从GC Roots开始对堆中对象进行可达性分析,找出要回收的对象,耗时较长
最终标记:标记那些在并发标记阶段发生变化的对象,将被回收,需要暂停用户线程
STW
筛选回收:对各个Regin的回收价值和成本进行排序,根据用户所期望的停顿时间来
制定回收计划,回收一部分Region(必须暂停用户线程,由多条收集器线程并行完成)
制定回收计划,回收一部分Region(必须暂停用户线程,由多条收集器线程并行完成)
STW
G1 收集器除了并发标记外,其余阶段也是要完全暂停用户线程的,也就是说,它并非
纯粹地追求低延迟,官方给它设定的目标是在延迟可控的情况下获得尽可能高的吞吐量
纯粹地追求低延迟,官方给它设定的目标是在延迟可控的情况下获得尽可能高的吞吐量
Young GC
Mixed GC
特点
目前在小内存应用上CMS的表现大概率仍然要会优于G1,而在大内存应用上G1则大多能发挥其优势
优点
采用标记整理算法,不会产生空间碎片
停顿时间可控,在不牺牲吞吐量的前提下,实现低停顿垃圾回收
充分利用CPU,多核条件下的硬核优势来缩短 STW
缺点
内存占用高
执行负载大
常见问题
G1如何实现停顿时间可控?
它将 Region 作为单次回收的最小单元,即每次收集到的内存空间都是 Region
大小的整数倍,这样可以有计划地避免在整个Java堆中进行全区域的垃圾收集
大小的整数倍,这样可以有计划地避免在整个Java堆中进行全区域的垃圾收集
在后台维护一个优先级列表,优先回收价值大的 Region(Garbage First的由来)
保证了 G1 收集器在有限的时间内获取尽可能高的收集效率
保证了 G1 收集器在有限的时间内获取尽可能高的收集效率
怎样建立起可靠的停顿预测模型?
G1 收集器的停顿预测模型是以衰减平均值(Decaying Average)为理论基础来实现的
衰减平均值更准确地代表“最近的”平均状态,Region的统计状态越新越能决定其回收的价值
Region里面存在的跨Region引用对象如何解决?
使用记忆集避免全堆作为GC Roots扫描
在并发标记阶段如何保证收集线程与用户线程互不干扰地运行?
CMS 收集器采用增量更新算法实现
G1 收集器则是通过原始快照(SATB)算法来实现的
ZGC
Shenandoah
其他收集器
Epsilon
PGC、C4、OpenJ9等
垃圾收集日志
日志级别从低到高
Trace、Debug、Info、Warning、Error、Off 共六种
默认为 Info
命令
查看GC基本信息
-XX:+PrintGC
JDK9之前使用
-Xlog:gc
JDK9之后使用
查看GC详细信息
-XX:+PrintGCDetails
JDK9之前
-Xlog:gc*
JDK9之后
查看GC前后 堆和方法区 可用容量的变化
-XX:+PrintHeapAtGC
-Xlog:gc+heap=debug
查看GC过程中用户线程并发时间以及停顿的时间
-XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCApplicationStoppedTime
-Xlog:safepoint
查看熬过收集后剩余对象的年龄分布信息
-XX:+PrintTenuring-Distribution
-Xlog:gc+age=trace
0 条评论
下一页