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