Java基础

JVM(二) GC算法与分代回收策略

2020-11-14  本文已影响0人  Timmy_zzh
  1. 可达性分析

  2. GCRoot场景

  3. 垃圾回收算法

  4. 分代回收策略

  5. 引用

垃圾回收

什么是垃圾

可达性分析

可达性分析算法是从离散数学中的图论引入的,jvm把内存中所有的对象之间的引用关系看作一张图,通过一组名为GC Root的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,最后通过判断对象的引用链是否可达来决定对象是否可以被回收。如下图所示:

1.可达性分析.png

GC Root对象

在java中,有以下几种对象可以作为GC Root:

  1. Java虚拟机栈(局部变量表)中引用的对象

  2. 方法区中静态引用指向的对象

  3. 仍处于存活状态中的线程对象

  4. Native方法中JNI引用的对象

GC回收时机
  1. Allocation Failure:在堆内存中分配时,如果因为可用剩余空间不足导致对象内存分配失败,这时系统会触发一次GC

  2. System.gc():在应用层,Java开发工程师可以主动调用此API来请求一次GC

垃圾回收算法

1.标记清除算法

从GC Roots集合开始,将内存整个遍历一次,保留所有可以被GC Roots直接或间接引用到的对象,而剩下的对象都当作垃圾对待并回收,过程分为两步:

如下图所示:

2.标记清除算法.png
2.复制算法

将现有的内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中存活对象复制到未被使用的内存块中。之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

3.复制算法1.png 3.复制算法2.png
3.标记-压缩算法

需要先从根节点开始对所有可达对象做一次标记,之后,并不是简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一段。最后,清理边界外所有的空间。

4.标记压缩算法.png

JVM分代回收策略

年轻代
  1. 绝大多数刚刚被创建的对象会存放在Eden区,如图
5.分代策略-新生成对象存放在Eden区1.png
  1. 当Eden区第一次满的时候,会进行垃圾回收。首先将Eden区的垃圾对象回收清除,并将存活的对象复制到S0,此时S1是空的。如图
5.分代策略2.png
  1. 下一次Eden区满时,再执行一次垃圾回收,此次会将Eden和S0区中所有垃圾对象清除,并将存活对象复制到S1,此时S0变为空。如图
5.分代策略3.png
  1. 如此反复在S0和S1之间切换几次(默认15次)之后,如果还有存活对象。说明这些对象的生命周期较长,则将他们转移到老年代中。如图
5.分代策略4.png
老年代

GC Log分析

JVM提供了相应的GC日志,在GC执行垃圾回收事件的时候,会有各种相应的log被打印出来,其中新生代与老年代的打印日志是有区别的。

GC Log分析相关的Java命令参数:

6.GCLog分析参数.png

Java引用

判断对象是否存活可以通过GC Roots的引用可达性来判断,JVM中的引用关系有四种,根据引用强度由强到弱,分别是:强引用,软引用,弱引用,虚引用

7.Java引用2.png
软引用隐藏问题
/**
 * 软引用:只会在内存不足的情况下被回收
 * 在有集合持有的情况下处理
 */
public class _6SoftReferenceTest {

    static class SoftObject {
        byte[] data = new byte[1024];  //1kb
    }

    public static int removeSoftRefs = 0;
    public static int CACHE_INITL_CAPACITY = 100 * 1024;
    //静态集合保存软引用,会导致这些软引用对象本身无法被垃圾回收器回收
    public static Set<SoftReference<SoftObject>> cache = new HashSet<>();
    public static ReferenceQueue<SoftObject> referenceQueue = new ReferenceQueue<>();

    public static void main(String[] args) {
        for (int i = 0; i < CACHE_INITL_CAPACITY; i++) {
            SoftObject obj = new SoftObject();
            cache.add(new SoftReference<SoftObject>(obj, referenceQueue));

            clearUselessReference();
            if (i % 10000 == 0) {
                System.out.println("size of cache:" + cache.size());
            }
        }
        System.out.println("end removeSoftRefs:" + removeSoftRefs);
    }

    private static void clearUselessReference() {
        Reference<? extends SoftObject> reference = referenceQueue.poll();
        while (reference != null) {
            if (cache.remove(reference)) {
                removeSoftRefs++;
            }
            reference = referenceQueue.poll();
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读