作者:ofLJli
链接:https://juejin.cn/post/7003213289425633287?searchId=20240709085629749958B21D886D4E67D4
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
概述
在JVM中主要的结构为:虚拟机栈、堆、方法区。其中虚拟机栈的栈帧在编译器就已经确定大小的,随着方法的结束或线程的技术,虚拟机栈的内存也随着回收。而Java堆和方法区这两个区域则有很显著的不确定性,这部分内存的分配和回收都是动态的,GC所关注的真是这部分内存该如何管理。
本篇文章就以下三方面GC所要完成的三件事:
- 什么时候回收?(触发GC的条件)
- 垃圾回收算法
- 垃圾回收器
什么时候回收
根据两个分代假说:绝大多数的对象都是朝生夕死
,熬过越多次的GC回收的对象就越难回收
。把堆进行了分代:新生代(Eden、From、To)、老年代,在GC时也进行了分代回收。
Minor GC: 回收新生代的无使用对象,新生代的对象的特性是大多数是朝生夕死的。触发时机有:
- Eden区空间不足,触发Minor GC 由于Eden空间大小有限,所以Minor GC触发的更加频繁,这就需要收集算法速度快、效率高,一般使用
标记-复制算法
对这一区域进行回收(后面讲)。
Major GC:回收老年代的无使用对象。一般使用标记-清除算法
或标记-整理算法
进行回收。
Full GC: 回收堆和方法区的无使用对象。Full GC回收范围比较大,执行的时间较长可能会造成卡顿,所以要尽量减少Full GC的次数。触发时机大致有:
-
老年代的空间不足 由新生代对象的进入老年代、大对象直接进入老年代等,如果在老年代的最大连续空间上无法存放这些对象时,就会进行一次Full GC回收。
-
方法区的空间不足 方法区主要存储类型信息和常量池,也有空间不足的风险,会进行Full GC回收
-
System.gc()被显示调用会Full GC回收
三种垃圾收集算法:
1. 标记-清除算法
原理:用可达性分析算法
将不可用的对象进行标记,然后对无用的对象进行清除。 缺点:在对象很多的情况下,标记的效率低。清除对象之后会产生内存碎片,内存不连续。 作用:在老年代回收中一些收集器会使用此算法
2. 标记-复制算法
原理:将内存空间一分为二,一半用于对象的存放,一半空闲。如果存放对象的区域满了,使用可达性分析算法
把存活的对象移动标记出来,然后复制到另一个空的区域,同时把之前的区域全部清空变成空的连续空间。 缺点:如果存活对象很多,要产品大量的内存复制开辟。内存空间只能用一半优点浪费资源。 作用:在新生代朝生夕死的对象中一般用此回收算法。但新生代中对复制算法进行了优化,但这种算法加入了分配担保机制防止存活对象过多分配不了的情况。使用了一种Appel式的回收算法:
3. 标记-整理算法
原理:标记的过程跟标记-清除算法
一样,然后整理存
活的对象往一端移动,然后存活边界之外的对象全部清除
。 缺点:移动对象有一定的风险。对象太多效率不高 作用:主要作用在老年代。
垃圾回收器
GC使用的垃圾收集器进行回收,随着不断的发展,垃圾收集器也越来越多,这里列举常规的垃圾收集器并进行分为三类:单线程收集器、多线程收集器、并发收集器。
单线程收集器
单线程的收集器的组合有:Serial/Serial Old收集器。它们不仅仅用一个收集线程去完成收集操作,而且在收集线程工作的时候,用户线程必须停止等待,直到收集完成为止。如图是Serial/Serial Old收集器示意图:
如果客户端的内存资源受限,处理器核心数较少或单核处理器来说,其简单高效的可以使收集器最快的工作完。
多线程并行收集器
多线程的收集器有:ParNew、Parallel Scavenge、Parallel Old,其中Parallel Scavenge/Parallel Old为组合收集器。这些多线程收集器仅仅是增加了垃圾收集线程,用户线程依然是停止等待垃圾收集的。
parNew收集器:其实就是Serial的多线程版本,目前能与Serial收集器和CMS收集器合作。
Parallel Scavenge收集器:一般配合Parallel Old收集器使用。相比于parNew收集器,它更加注重是吞吐量
的控制,吞吐量就是用户线程执行的时间占总CPU运行的时间,吞吐量当然是越大越好。
多线程一般用服务端,因为多线程的执行,有时间片轮转的消费时间,如果对于单处理器来说无疑处理效率更慢。但对于资源很好,不用与用户交互的分析运算的服务端却可以增加执行效率。
并发收集器
并发收集器有:CMS收集器,是一款以系统停顿的时间尽量较短,用户体验较好为目标的收集器。它的收集线程可以与用户线程并发执行。CMS有三次的标记
(初始标记、并发标记、重新标记)和一次清理
(并发清理),在三次的标记中有两次标记需要较短用户线程停止,一次较长的与用户线程并发的标记,和与用户线程并发的清除。
初始标记:标记GC Roots关联的第一个对象,时间很短 并发标记:和用户线程并发执行GC Roots的引用链(可达性分析算法),时间较长 重新标记:重新查找在并发标记阶段,用户线程运行生成的新的引用链。时间比初始标记长一点。 并发清除:用标记-清除算法
把无用对象进行清除。
三大缺点:一:CPU敏感,并发对核心数少的处理器对用户线程的运行可能会造成影响。二:浮动垃圾:在并发清理阶段产生的垃圾只能等下一次GC回收。三:内存碎片,标记-清理法会产品大量的不连续的内存空间。
小结
本文从那些内存需要回收
,什么时候回收
,如何回收
作为执行分别写出了两个对象存活判断算法、Class区回收的条件、回收的分代机制与收集时机、三个收集算法和常用的垃圾收集器。