目录
4. 虚引用(PhantomReference) 必须配合引用队列使用,
3.CMS收集器(Concurrent Mark-Sweep)(响应时间优先)
垃圾回收
如何判断对象可以回收
引用计数法
引用计数法是一种简单的垃圾回收算法,它通过计算对象被引用的次数来判断对象是否可以被回收。当对象的引用计数为0时,表示没有任何引用指向该对象,可以被回收。但是引用计数法无法处理循环引用的情况,因此在Java中很少使用。
循环引用:如下图所示,两个对象互相引用,但是这两个对象没有被其他任何对象引用
可达性分析算法
定义
可达性分析法是Java中主要采用的垃圾回收算法,它通过从一组称为"GC Roots"的根对象开始,递归遍历所有对象的引用关系图,标记所有能够被从GC Roots到达的对象为存活对象,而无法被到达的对象则被判定为可回收对象。这种方法可以有效处理循环引用的情况,是Java中主要的垃圾回收算法。
哪些对象可以作为GC roots?
system.class:系统类中的对象
native stack:本地方法引用的一些对象
Thread:正在活动的线程
Busy Monitor:正在加锁的对象
四种引用
1.强引用
只有所有GC Roots对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
2.软引用(SoftReference)
仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用 对象 可以配合引用队列来释放软引用自身
3. 弱引用(WeakReference)
仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象 可以配合引用队列来释放弱引用自身
4. 虚引用(PhantomReference) 必须配合引用队列使用,
主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队, 由 Reference Handler 线程调用虚引用相关方法释放直接内存(cleaner)
5. 终结器引用(FinalReference)
无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象 暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象
设置堆内存大小:VM Options:-Xmx20m
打印垃圾回收详细日志:VM Options:-XX:+PrintGCDetails -verbose:gc
弱引用示例
@Slf4j
public class Test5 {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) throws IOException {
soft();
}
public static void soft(){
//list--->SoftReference--->byte[]
// 强引用 软引用
List<SoftReference<byte[]>> list = new ArrayList<>();
//引用队列,用于回收不引用任何对象的软引用
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
//关联了引用队列,当软引用所关联的byte[]被回收时,软引用自己会加入到queue中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB],queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
Reference<? extends byte[]> poll = queue.poll();
while (poll != null){
list.remove(poll);
poll = queue.poll();
}
System.out.println("循环结束:"+list.size());
for (SoftReference<byte[]> softReference : list) {
System.out.println(softReference.get());
}
}
}
垃圾回收算法
1.标记清楚(Mark Sweep)
图示
定义
标记-清除算法是最早期的垃圾回收算法之一,它分为标记和清楚两个阶段
在标记阶段,从跟对象开始,递归遍历所有可达对象,并对它们进行标记。
在清除阶段,遍历整个堆内存,将未标记的对象进行清除
优点
速度快
缺点
会产生大量的不连续内存碎片,导致内存利用率降低
2.标记整理(Mark Compact)
图示
定义
标记整理算法先标记所有存货对象,然后将存活对象向一段移动,之后清理掉边界外的对象。
优点
没有内存碎片
缺点
需要移动对象,性能较差,速度慢
3.复制算法(Copying)
图示
定义
复制算法将堆内存分为两个大小相等的区域,每次只使用其中一半
当一半区域的对象存活下来后,将其复制到另一半区域中,并将原区域清空
优点
不会产生内存碎片
缺点
需要占用双倍内存空间
分代算法(Generational)
图示
定义
分代算法将堆内存分为新生代和老年代两部分
新生代中存放新创建的对象,使用复制算法进行垃圾回收。老年代中存放存活时间较长的对象,使用标记清除或者标记整理算法进行垃圾回收
过程
对象首先分配在伊甸园区域
- 新生代空间不足时,触发minor gc,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄加1并且交换from,to
- minor gc会引发stop the world(stw),暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行
- 当对象寿命超过阈值的时候,会晋升至老年代,最大寿命是15(4bit)
- 当老年代空间不足,会先尝试触发minor gc,如果之后空间仍不足,那么出发full gc,STW时间更长。
优点
分代算法充分利用了新对象生存时间短和老对象生存时间长的特点,提高了垃圾回收效率
相关VM参数
含义 | 参数 |
堆初始大小 | -Xms |
堆最大大小 | -Xmx 或 -XX:MaxHeapSize=size |
新生代大小 | -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size ) |
幸存区比例(动态) | -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy |
幸存区比例 | -XX:SurvivorRatio=ratio |
晋升阈值 | -XX:MaxTenuringThreshold=threshold |
晋升详情 | -XX:+PrintTenuringDistribution |
GC详情 | -XX:+PrintGCDetails -verbose:gc |
FullGC 前 MinorGC | -XX:+ScavengeBeforeFullGC |
垃圾回收器
1.串行(serial)
图示
定义
Serial收集器是一种单线程的垃圾回收器,它在新生代使用复制算法,在老年代使用标记整理算法。单线程完成垃圾回收,回收时,其他线程都暂停。
使用
-XX:+UseSerialGC = Serial + SerialOld
适用
小型应用或者客户端应用,单线程特点使它的暂停时间较长,不适合高并发场景。
2. Parallel收集器(吞吐量优先)
图示
定义
也称作多线程并行收集器,它在新生代使用复制算法,在老年代使用标记整理算法,通过使用多线程来提高垃圾回收的吞吐量,适用于多核服务器应用。
使用
-XX:+UseParallelGC ~ -XX:+UseParallelOldGC
3.CMS收集器(Concurrent Mark-Sweep)(响应时间优先)
图示
当内存空间不足时,触发新生代垃圾回收,进行初始标记,只标记根对象,所以时间很快,然后和用户线程并发执行,进行并发标记,标记出不使用的对象,并发标记完,进行重新标记,这个标记会标记出并发执行过程中用户线程新产生的垃圾,然后和用户线程并发执行,并发清理。只会在初始标记和重新标记会SWT。因为老年代使用标记清除算法,减少时间,所以会产生很多内存碎片,当老年代内存空间不足时,会退化SerialOld,串行老年代垃圾回收。时间效率大幅下降
浮动垃圾:并发标记过程中,用户线程产生的新垃圾
定义
CMS收集器是一种以最短暂的停顿时间为目标的收集器。它在新生代使用并行复制算法,在老年代使用标记清除算法。CMS收集器通过使用多线程来在垃圾回收和应用程序执行之间实现并发,减少了停顿时间,适用于对响应时间要求较高的应用。
使用
-XX:+UseConcMarkSweepGC
-XX:+CMSScavengeBeforeRemark:在重新标记之前,进行一次垃圾回收命令
拓展点
ConCurrent:并发->进行垃圾回收时,用户线程运行
Parallel:并行->进行垃圾回收时,其他线程暂停
Garbage First(G1)
- 2004 论文发布
- 2009 JDK 6u14 体验
- 2012 JDK 7u4 官方支持
- 2017 JDK 9 默认
定义
G1垃圾收集器是JAVA7引入的一款垃圾收集器,全称Garbage-First Garbage Collector。G1是一个分代的,增量的,并行与并发的标记-复制算法垃圾回收器。G1设计的目的是为了应对越来越大的内存以及越来越多的处理器数量,复制算法能很好的解决空间碎片化的问题,其分区与部分回收机制又使得它能有效减少大内存下复制算法所导致的单次GC停顿时间(清除算法可以并发处理,复制算法只能STW进行)。但是其分区特性也使得其不适合小内存场景,在小内存场景回收效率低下。
G1还有一个极其重要的特性-软实时(soft real-time):实时回收是指每一次垃圾回收所造成的停顿时间都在限定时间之内。软实时是指G1允许设置一个限定值,G1会努力控制每一次GC所造成的停顿都在限定时间之内,但是并不保证每一次GC造成的停顿都能满足要求。停顿时间的限定可以通过-XX:MaxGCPauseMillis参数设置。如果需要设置此参数,要慎重,过大的话没有意义,过小则会导致频繁触发回收,降低回收效率。
Region定义
不同于传统分代垃圾收集器,将堆空间固定的划分为新生代与老年代两个内存区域。G1采用分区的概念,将整个堆空间分为若干个大小相等的内存区域。每个内存区域就是一个Region(分区)。这些Region不再是固定的属于新生代或者老年代,它既可能是新生代,也可能是老年代。Region是G1的最小回收单元。
每一块Region的大小是固定的,可以通过参数XX:G1HeapRegionSize设置大小(1MB-32MB且必须是2的幂)。
使用中的Region根据其角色可以分为Eden Region, Survivor Region, Old Region以及Humongous Region。其中Eden Region,Survivor Region,Old Region顾名思义分别对应分代收集算法的中的Eden区,Survivor区,Old区,每个区都含有若干个Region。
适用场景
同时注重吞吐量(Throughput)和低延迟(low latency),默认的暂停目标是200ms
超大堆内存,会将堆划分为多个大小相等的Region
整体上是标记+整理算法,两个区域之间是复制算法
G1垃圾回收阶段
YoungCollection
- 新生代垃圾回收。初始化标记,会SWT,把存活下来的对象复制到幸存区
YoungCollection+CM
- 在YoungGC时会进行GC Root的初始标记
- 老年代占用堆空间比例达到阈值时,进行并发标记(不会SWT),由-XX:InitiatingHeapOccupancyPercent=percent命令决定比例
MixedCollection
- 会堆E,S,O进行全面垃圾回收
- 最终标记会SWT
- 拷贝存活会SWT