JVM_GC
2021-01-12 14:50:04 5 举报
AI智能生成
GC垃圾回收器相关
作者其他创作
大纲/内容
垃圾收集算法
回收区域
方法区
回收类型
1.废弃的常量
与堆中对象回收相似,比较容易
2.不再使用的类型
要判断一个类型为不再使用的类型需要满足三个条件
1.该类所有的实例都已经被回收,java堆中不存在该类以及派生子类的实例
2.加载该类的类加载器已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用
jvm规范中没有对方法区的回收做出明确的规定,在实际的方法区回收中回收性价比较低,回收条件也比较苛刻
堆(主要回收区域)
OutOfMemoryError
内存泄漏(Memory Leak)
定义:对象不再被使用,但是GC又无法对他们进行回收的情况(存在某些引用没有被释放的问题)
某些变量定义的生命周期过长,如:直接定义为类变量,导致不能及时被回收
单例模式中,单例对象所持有的外部对象的引用,因为单例对象生命周期较长,所以也会导致其持有的对象无法释放
某些连接资源的close:数据库连接,网络连接,io都需要手动进行资源的关闭close
内存溢出
1.标记阶段
算法实现
引用计数算法
对每个对象保存一个整型的引用计数器属性,用于记录对象的引用的情况
优点:实现简单,垃圾对象便于辨别,
可达性分析算法
通过根对象作为起始节点集(GC Roots)向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连, 或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不再被使用的。
GC Roots
虚拟机栈中的方法参数,局部变量,局部变量表中的对象引用
本地方法栈的JNI(本地的native方法)
方法区的静态常量,字符串常量池的引用
同步锁(synchronized关键字)所持有的对象
虚拟机中常驻的一些异常类对象,系统类加载器
分代收集与局部回收中,除要回收区域外的其他区域对象
java,c#中在对象的回收标记阶段使用的都是可达性分析算法
作用:标记区分哪些对象需要进行垃圾回收
2.清除阶段
算法实现
1.标记-清除(Mark-Sweep)
分为两个阶段:标记,清除;标记阶段,会在对象的Header中记录一个标记
关于“标记阶段”,在深入理解java虚拟机第二版中,标记的是需要回收的对象;在第三版中标记的可以是需要回收的,也可以是存活的
缺点:标记和清除两个阶段效率都比较低,因为是直接对对象进行清除,会产生内存空间的碎片化问题
因为会产生空间碎片化的问题,所以需要维护一个空闲列表来进行空间内存的管理,
“清除”,并不是真的置空,而是把需要清除的对象地址保存在空闲列表中,下次新对象分配内存时,会判断垃圾的位置是否足够,如果够,则进行存放
2.标记-复制(复制算法)
hotspot虚拟机的Serial,ParNew等新生代收集器就是采用的这种方式,但是在此基础上进行了优化(8:1:1)使用,可利用空间为90%
缺点:会有一定的空间浪费,复制的过程中对象的位置产生变化也会导致对象的引用产生变化
优点:实现简单,运行高效,不会产生空间碎片的问题,对空间的分配使用的是指针碰撞的方式
适用于要回收区域存活对象较少的情况(新生代朝生夕死,对象回收率在90%左右)
3.标记-整理(Mark-Compact)
从停顿时间,吞吐量两个方面的综合考虑,老年代的垃圾收集结合:CMS收集器(基于标记清除)和Serial OLd(基于标记整理)来实现
第一个阶段和标记清除一样对无用对象进行清除,之后将存活的对象都向空间的其中一侧进行移动整理
标记清除不需要移动存活的对象,标记整理需要移动存活的对象
使用指针碰撞来进行空间的分配;效率最低,但是综合性能较好
finalize方法(系统调用System.gc()会调用执行方法)
经过第一次被标记的回收对象还要进行再次的标记才能决定该对象是否要进行回收
第二次的标记过程是判断finalize方法是否执行的过程,如果没有重写该方法,或者该方法已经在该对象执行中调用过一次,则不会执行该方法
要执行该方法时,会先将该对象放置在一个F-Queue的队列中(统一处理),稍后虚拟机会在后台自动建立一个低优先级的finalizer线程就行调用执行该方法
在调用finalize方法时,对象可以进行一次自救行为,只要该对象能和任一引用连搭上,就可以避免被回收,从而复活,
finalize方法的调用只能进行一次,也就是该对象在这个阶段只能进行一次复活的行为
不建议主动调用finalize执行,因为该方法的运行代价高昂,不确定性大,无法保证各个对象的调用顺序,已被官方明确声明为不推荐使用的语法
对象引用关系
强引用(Strongly Reference)
只要引用关系还存在就不会进行回收,对象引用关系是可达的
在程序中创建对象99%都是使用的强引用,强引用是造成内存泄漏的根本原因之一
软引用(Soft reference)
内存不足时回收;当内存不足,即将发生内存溢出之前,会将软引用划分在要收集的对象之中,发生垃圾收集时进行二次回收(第一次回收的是不可达的)
可以用作缓存(mybatis中的缓存中有过使用)
弱引用(Weak Reference)
发现即回收,只要发生垃圾收集就会进行回收
有关类使用:WeakHashMap,该类中的内部类Entity继承自WeakReference
虚引用(Phantom reference)
根据虚引用不能获得一个对象的实例,设置虚引用的目的只是为了在这个对象在被收集器回收时收到一个系统的通知
对象回收跟踪,参数中附带有引用队列,当虚引用回收后,会在引用对列中进行一个通知的存入
性能评估指标
1.吞吐量
运行用户代码所用的时间占总运行时间的比例
2.暂停时间(低延迟)
执行垃圾收集,程序工作线程被暂停的时间(Stop the world)
3.内存占用
堆中所占用的内存大小(内存空间大,相应的延迟也会有所增加)
4垃圾收集开销
5.收集频率(收集频率并不是越快越好)
6.快速
现代垃圾回收标准:在最大吞吐量优先的情况下,降低停顿时间
收集器分类
并行/串行/并发
串行回收器
Serial 、Serial Old(适用于单核的CPU下使用)
并行回收器
ParNew、Parallel Scavenge、Parallel Old(适用于多核的CPU下使用)
并发回收器
CMS(Concurrent Mark Sweep)、G1
收集区域划分
年轻代收集器
Serial、ParNew、Parallel Scavenge
老年代收集器
Serial Old、Parallel Old、CMS
整堆收集器
G1
收集器介绍
Serial
串行回收(STW)工作时要停止用户线程,单核CPU环境不需要线程间的切换,性能较高,使用的是标记清除算法
参数设置:-XX:+UseSerialGC,不需要设置老年代,老年代会自动搭配Serial Old使用
Serial Old
串行回收(STW)工作时要停止用户线程,使用的是标记压缩算法,是Client模式下默认的老年代垃圾回收器
ParNew
并行回收,是Serial的多线程的版本,暂停时间较Serial短,适合处理新生代的垃圾回收器,可以搭配CMS/Serial Old老年代垃圾回收器使用
参数设置
jdk8:-XX:+UseParNewGC,老年代默认搭配Serial Old使用
jdk9:-XX:+UseParNewGC -XX:+UseConcMarkSweepGC,在jdk9以及之后只能搭配老年代回收器CMS使用
Parallel
并行回收,吞吐量优先(目标是达到一个可控制的吞吐量),自适应的调节策略(内存分配调节),适用于不需要与用户进行过多交互的服务端后台程序(订单处理等)
参数设置
-XX:+UseParallelGC,默认搭配老年代收集器Parallel Old使用
-XX:+UseParallelOldGC,使用老年代的收集器,JDK8是默认开启的
-XX:ParallelGCThreads,设置年轻代并行垃圾收集器的线程数,一般设置与CPU的核心数相同
-XX:MaxGCPauseMills,设置最大的垃圾回收时的停顿时间(STW),慎用
-XX:GCTimeRatio,设置垃圾回收占总时间的比例
-XX:+UseAdaptiveSizePolicy,自适应调节策略(堆内存分配),默认是开启的
Parallel Old
jdk6中出现,替换了Serial Old作为老年代的回收,提高了吞吐量,适用于Server服务端
CMS
响应时间(低延迟),JDK5出现是第一款真正意义上的并发的垃圾收集器,尽可能的缩短垃圾收集线程工作时用户线程停顿的时间,使用的是标记清除算法
工作原理
初始标记
仅仅只是标记出GC Roots能直接关联的对象,执行速度非常快,会出现很短的STW时间
并发标记
并发执行,根据GC Roots来遍历整个对象图关联可达对象的过程,耗时较长,因为是与用户线程并发执行的,所以会出现对象标记的变化
1.标记为可达的对象,但是在用户线程执行中对象引用被释放,由于这部分对象已经被标记,所以在本次垃圾回收将无法被回收,变成浮动垃圾
2.不可达的对象,但是在用户线程执行中又有了引用关系,这部分对象将会在重新标记阶段被标记出来,避免被回收
重新标记
修正第一次标记期间到此时间这一段时间内产生变化的标记对象,也会有较短的STW时间(比第一阶段时间长一点),使用的是增量更新算法
并发清理
并发执行,清理删除掉标记的已死亡的对象,释放内存空间(使用的是标记清理算法)
注意事项
在CMS进行垃圾收集的同时,用户线程也是在同时工作的,因此要确保有一定的内存空间可以供用户线程工作使用
所以CMS并不能像其他垃圾收集器一样等老年代爆满了才进行垃圾收集,而是当堆内存使用率达到一定的阈值时就开始进行垃圾收集工作,
以确保用户线程在CMS进行垃圾收集的时候还可以进行正常的工作。如果CMS在运行期间剩余的空间无法满足用户线程的工作,
就会出现一次“Concurrent Mode Failure”,这时虚拟机将启用备用方案Serial Old进行垃圾回收,会增加垃圾收集的时间开销,这也就是CMS+Serial Old组合的老年代收集
性能分析
优点
低延迟,并发处理
缺点
会产生内存碎片,CMS对处理器性能十分敏感,无法处理浮动垃圾(并发标记阶段产生的新的垃圾对象,将不能被标记,被称为浮动垃圾)
参数设置
-XX:+UseConcMarkSweepGC,使用CMS垃圾收集器,开启此参数时会自动开启-XX:+UseParNewGC作为新生代的垃圾回收器
-XX:CMSInitiatingOccupanyFraction,设置堆内存使用率的阈值,一旦达到该阈值就会触发CMS进行垃圾收集工作,JDK6以及以上默认是92%
-XX:ParallelCMSThreads,设置垃圾收集器的并发工作的线程数,默认情况是( (ParallelGCThreads+3)/4 )
G1
简述
JDK9以及之后默认的垃圾收集器,是一款面向服务端的垃圾收集器,主要针对多核CPU以及大容量内存的机器,以极高概率满足GC停顿时间的同时还具有较高的吞吐量
将堆空间分为一个个的Ragion,G1通过跟踪Ragion中垃圾回收价值的大小(回收所获得的空间大小以及回收所需要的时间),在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的Ragion
特点
并行与并发
并行性:G1在回收期间,可以有多个垃圾回收线程同时工作,有效利用多核CPU的性能,此时用户线程是STW的,(年轻代的收集)
并发性:G1拥有与用户线程交替执行的能力,部分工作可以和用户线程同时进行,因此,不会在整个回收阶段都发生阻塞用户线程的情况(堆内存占用达到堆使用阈值后开始并发标记,准备对老年代的回收,默认堆占用率是45%)
分代收集
G1依然是属于分代型的垃圾收集器,但是它不在要求年轻代老年代的固定大小与固定数量
将整个堆分为一个个的Ragion,这些Ragion包含了逻辑上的年轻代与老年代,同时兼顾老年代年轻代的收集
空间整合
Ragion之间使用的是“标记-复制”算法,整体上是“标记-压缩”算法,都避免了空间碎片的问题
当堆空间越大的时候,G1的优势就越加明显
可预测的停顿时间模型
每次根据允许的收集时间,有限回收价值最大的Ragion(一个或多个),获取尽可能高的收集效率
Ragion
将整个堆划分为约2048个Ragion区域,每个Ragion的大小根据堆空间的大小而定,整体被控制在1-32M,且是2的次幂,即可为1,2,4,8,16,32,可以通过参数进行设置,所有的Ragion大小相同,且在jvm生命周期内不会改变,堆空间大小(2G-64G)
还保留着新生代老年代的定义,但是他们不再是物理上连续的区域了,而是通过Ragion集合的形式存在
Ragion分为四类:E(Eden对象),S(Survivor对象),O(Old对象),H(Humongous当对象大小超过1.5个Ragion大小时被称为巨型对象或者大对象,分配空间可以占用多个连续的Ragion区域,如果找不到多个连续的Ragion区域,则触发full gc进行空间整理)
Ragion中解决隔代引用的问题是使用记忆集的方式,记录不同代之间的相互引用,记忆集是一个抽象的数据结构,Hotspot使用的实现是卡表来进行实现的(字节数组),占用约每个Ragion的10-20%的空间
参数设置
-XX:+UseG1GC,启用G1垃圾收集器
-XX:G1HeapRegionSize,设置Region的大小,值是2的次幂,范围在1M-32M,目标是根据最小的java堆划分出约2048个区域,默认是堆内存的1/2000
-XX:MaxGCPauseMillis,设置期望最大的GC停顿时间(JVM会尽力实现,但不保证),系统默认是200ms
-XX:ParallelGCThreads,设置GC并行回收垃圾时工作的线程数(用户线程有STW),最大设置为8
-XX:ConcGCThreads,设置并发标记的线程数,为ParallelGCThreads的1/4左右
-XX:InitiatingHeapOccupancyPercent,设置触发并发GC周期的java堆占用阈值,超过就触发GC,默认大小是45
工作原理
初始标记
标记出GC Roots,修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。
需要停顿用户线程,但停顿时间较短
并发标记
从GC Root开始对堆中对象进行可达性分析,扫描整个堆中的对象图,找出要回收的对象
这阶段耗时较长,但可与用户程序并发执行,当对象图扫描完成以 后,还要重新处理SATB(原始快照)记录下的在并发时有引用变动的对象
最终标记
对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录(原始快照SATB)
筛选回收
更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,
可以自由选择任意多个Region 构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧 Region的全部空间
这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成,停顿时间较短
Full GC
可能发生,当在一定的时间内回收的空间跟不上使用的空间时,空间即将耗尽,就会触发full GC(单线程,STW时间也较长),影响程序的性能
0 条评论
下一页