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使用标记整理算法

扫描二维码关注公众号,回复: 10589085 查看本文章

对新生代触发的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