GC与垃圾回收分代


title: GC与垃圾回收分代

gc与finalize

网上很多博客简略的说确保对象死活需要经过两次标记这是错误的。
Object有个finalize,默认是空实现
被回收时会判断这个对象是否重写了finalize方法,如果没有就直接回收
否则会把该对象加入一个f队列(代表需要执行回收方法的队列)
jvm会分配一个执行finalize的线程去执行这个队列每个对象的finalize方法,如果在finalize方法中对象吧自己的this给不在finalize队列中的对象引用,则将其从finalize队列移除
jvm并不保证执行finalize的线程绝对会执行结束,而是超过一个固定时间以后就终止(防止死锁)。
finalize线程终止后,其中还保留的对象都会被回收

判断对象是否存活

以前采用过标记法,即每个对象加一个计数器,被引用了+1,取消引用-1.当为0代表可以回收
此种方法会导致无法处理循环引用(A引用B,B引用A且而GCroot没有任何对象引用A和B,则A和B永远不会被回收),因此被遗弃了
现在普遍使用GCroot扫描方法

GCRoot

以下对象可以作为GCroot
Class - 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。
//其他类加载器加载的对象,jvm默认不回收,除非进行特殊指定
Thread - 活着的线程
Stack Local - Java方法栈的local变量或参数
JNI Local - JNI方法的local变量或参数
JNI Global - 全局JNI引用
Monitor Used - 用于同步的监控对象(锁)
Held by JVM - 用于JVM特殊目的由GC保留的对象,但实际上这个与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。

扫描过程:
从GCroot开始扫描,如果可达的对象则为存活
即为一个连通图的判断,不在图里的其他元素默认为死亡

分代回收

jdk1.7以前
java把堆内存分为两块,新生代和老生代
还有一块保存静态变量,常量和Class对象的永生代保存在方法区

新生代采用复制算法
老生代使用标记整理算法
永生代一般不回收,只有当需要加载Class而内存不够才会触发fullgc进行回收,会和老年代一起回收(标记整理)

新生代又分为 一块eden 和 两块survivor

参数: 指定新生代大小 -XX:NewSize
指定老年代与新生代大小比例 -XX:NewRatio
指定新生代中伊甸园和存活地大小比例: -XX:SurvivorRatio

刚开始new出来的对象都在伊甸园(eden)
然后一块survivor标记为存活地

复制算法

触发minor gc以后,会把另一块没有标记的空的survivor标记为存活地
会把eden中存活的对象和旧存活地的还活着的对象放进这块新的存活地,然后把旧存活地和伊甸园清空
当然如果存活地大小不足以容纳时,多余的对象会直接进入老年代
对象第一次进入survivor时,标记年龄为1
每次在survivor之间转移时年龄会增长,达到某个值时,就不进入新的存活地而是直接进入老年代
这个年龄可以通过jvm参数设置:-XX:MaxTenuringThreshold

标记整理算法

首先会遍历所有对象,标记还存活的对象,并清理掉死亡的对象,然后将存活的对象整理(挨到一起)
整理的目的和磁盘碎片压缩的道理一样,整理后才能留出更大的空间留给大对象,避免出现大量碎片
因为标记整理算法相对复制算法更慢,但是不需要额外的备用空间,
而minor gc触发频繁,老年代的gc触发较少,但是可能会累计较大空间
因此minor gc使用复制算法,major gc使用标记整理算法

对新生代触发的gc称为minor gc,而对老生代触发的gc称为major gc
fullgc是指触发所有的回收(包括对永久代的回收)

垃圾回收器

新生代

Serial串行回收器

基于单线程的回收器,在运行时会导致Stop The World,但是效率高,使用的算法就是复制算法
可以和CMS搭配使用

ParNew回收器

Parallel New Generation
Serial回收器的多线程版本,因为是多线程版本,所以不用触发Stop the world
但是效率不太好,因为需要花开销在线程的切换调度销毁上
当然在多cpu电脑上会更有优势
可以和CMS搭配使用

Parallel Scavenge回收器

与ParNew类似,都是复制算法的并行收集器
但是Parallel Scavenge的目标是达到一个可控的吞吐量
吞吐量=程序运行时间/(程序运行时间+GC时间)
Parallel Scavenge提供了两个参数用以精确控制吞吐量
分别是用以控制最大GC停顿时间的-XX:MaxGCPauseMillis及直接控制吞吐量的参数-XX:GCTimeRatio.
同时可以设置垃圾自适应调配策略,由虚拟机自动调优
由于是Parallel Scavenge没有使用原本HotSpot其它GC通用的那个GC框架,所以不能跟使用了那个框架的CMS搭配使用。

老年代

SerialOld回收器

Serial的老年代版本,还是单线程也会触发Stop the world,不过使用的是标记整理算法

ParallelOld回收器

Parallel Scavenge的年老代版,使用标记整理算法,并行

CMS回收器

Concurrent Mark Sweep,并发回收器,旨在减少STW时间(通过两次标记,第二次只查看dirty的)
在G1出现前的主流一般server版本jvm就是ParNew+CMS
过程
初始标记:该阶段只标记GC Root节点直接引用的老年代节点,会造成STW,但是时间很短。(因为只标记被直接引用的)
并发标记:GC ROOT TRACING。遍历老年代,然后标记所有存活的对象(包括不是gcRoot直接引用的),它会根据上个阶段找到的 GC Roots 遍历查找
在并发标记阶段被修改的节点(新降临到老年代的对象以及在并发阶段被修改了的对象)的对象会被标记为dirty,加入Dirty队列
重新标记:stop the world
遍历DirtyCard,更新标记被修改的老年代对象
停顿时间比初始标记稍长,但远比并发标记短
并发清理:遍历老年代清理死亡对象,整理空间

请移步

个人主页: yangyitao.top

发布了35 篇原创文章 · 获赞 12 · 访问量 5039

猜你喜欢

转载自blog.csdn.net/weixin_44627989/article/details/88914553