本文介绍: Java虚拟机中的垃圾回收采用可达性分析探索所有存活对象扫描堆中的对象,看是否能够沿着GC Root对象。在垃圾回收之前,会对堆内存所有的对象进行扫描。哪些对象可以作为GC Root。:一些肯定不能作为垃圾的对象。为起点的引用链找到该对象(如果不是,则可以被垃圾回收。如果是,则不能被垃圾回收查看一个对象是不是被。根对象直接或间接引用

1. 如何判断对象可以回收

1-1. 引用计数法

引用计数法

引用计数法弊端

在这里插入图片描述

1-2. 可达性分析算法

根对象:一些肯定不能作为垃圾的对象

可达性分析算法

  • 在垃圾回收之前,会对堆内存所有的对象进行扫描

  • 查看一个对象是不是被根对象直接或间接引用

  • 如果是,则不能被垃圾回收

  • 如果不是,则可以被垃圾回收

  • Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象

  • 扫描堆中的对象,看是否能够沿着GC Root对象 (堆中对象) 为起点的引用链找到该对象,找不到,表示堆中对象可以回收

  • 哪些对象可以作为GC Root

1-3. 四种引用

实线代表强引用

平时用的引用都是强引用例如赋值运算
在这里插入图片描述

强引用

软引用

  • 只有【软引用】引用该对象时,在垃圾回收后,内存仍不足 则会回收软引用对象
  • 可以配合【引用队列】来释放软引用自身,因为软引用自身也占用内存
  • 在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。

应用场景

弱引用

  • 只有【弱引用】引用该对象时,在垃圾回收时,无论内存是否充足都会回收弱引用对象
  • 可以配合【引用队列】来释放弱引用自身,因为弱引用自身也占用内存
  • 在 JDK1.2 之后,用 java.lang.ref.WeakReference 来表示弱引用。

虚引用

终结器引用

软引用使用

public class Demo1 {
	public static void main(String[] args) {
		final int _4M = 4*1024*1024;
		//list是强引用,byte数组是软引用
		List<SoftReference<byte[]&gt;> list = new ArrayList<>();
		SoftReference<byte[]> ref= new SoftReference<>(new byte[_4M]);
	}
}

软引用配合引用队列使用

public static void main(String[] args) throws IOException {
    ///使用引用队列,用于移除引用为空的软引用
    ReferenceQueue<byte[]> queue=new ReferenceQueue<>();
	
    List<SoftReference<byte[]>> list = new ArrayList<>();
    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("=============");
    System.out.println("循环结束:" + list.size());
    for (SoftReference<byte[]> ref : list) {
        System.out.println(ref.get());
    }
}

弱引用使用

弱引用的使用和软引用类似,只是将 SoftReference 换为了 WeakReference

public static void main(String[] args) {
	//list是强引用,byte数组是弱引用
    List<WeakReference<byte[]>> list=new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        WeakReference<byte[]> ref=new WeakReference<>(new byte[_4MB]);
        list.add(ref);
        for (WeakReference<byte[]> w : list) {
            System.out.print(w.get()+" ");
        }
        System.out.println();
    }
    System.out.println("循环结束:"+list.size());
}

2. 垃圾回收算法

2-1. 标记清除

在这里插入图片描述
标记清除

在垃圾回收的过程

注意:这里清除并不是将内存空间字节清零,而是记录这段内存的起始地址,下次分配内存的时候,会直接覆盖这段内存。

优点: 速度快

缺点: 容易产生内存碎片。一旦分配较大内存的对象,由于内存不连续,导致无法分配最后就会造成内存溢出问题

2-2. 标记整理

在这里插入图片描述

标记整理

优点:不会有内存碎片

缺点速度慢

2-3. 复制

在这里插入图片描述
在这里插入图片描述

复制算法

优点:不会有内存碎片

缺点:会占用双倍的内存空间速度慢

2-4. 总结

  • JVM会根据不同的情况来采用这3种算法
  • 不会只使用一种算法

3. 分代垃圾回收

堆内存分为新生代年代,新生代有划分为伊甸园幸存区To幸存区From

新生代

年代

新生代和老年代会进行不同的垃圾回收算法

在这里插入图片描述

3-1. 回收流程

新创建的对象分配在伊甸园

在这里插入图片描述

如果新生代空间不足时,触发 Minor GCminor gc 会引发 stop the world,暂停其它用户线程,等垃圾回收结束用户线程恢复运行)回收伊甸园幸存区from 中的垃圾,伊甸园from 存活的对象使用 copy 复制to 中,存活的对象年龄加 1并且交换 fromto,并让幸存区from中的对象寿命加1

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit 不同的垃圾回收器阈值会不同

在这里插入图片描述

如果老年代空间不足,会先尝试触发Minor GC,回收新生代的垃圾。如果之后空间仍不足,那么触发 Full GC,回收新生代和老年代的垃圾,stop the world时间更长

在这里插入图片描述

如果 Full GC之后,空间仍不足。则触发OutOfMemory – Java heap space0000000

3-2. VM参数分析

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

在这里插入图片描述

大对象处理策略

遇到一个较大的对象时,就算新生代的伊甸园为空,也无法容纳该对象时,会将该对象直接晋升为老年代

public class Main {
    private static final int _512KB = 512 * 1024;
    private static final int _1MB = 1024 * 1024;
    private static final int _6MB = 6 * 1024 * 1024;
    private static final int _7MB = 7 * 1024 * 1024;
    private static final int _8MB = 8 * 1024 * 1024;
    // -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
    public static void main(String[] args) throws InterruptedException {
        ArrayList<byte[]> list=new ArrayList<>();
        list.add(new byte[_8MB]);
    }

}

在这里插入图片描述

线程内存溢出

某个线程的内存溢出了而抛异常(out of memory),不会影响其他的线程结束运行。这是因为当一个线程抛出OOM异常后,它所占据的内存资源会全部被释放掉,从而不会影响其他线程的运行,其他进程依然正常运行

public class Main {
    private static final int _512KB = 512 * 1024;
    private static final int _1MB = 1024 * 1024;
    private static final int _6MB = 6 * 1024 * 1024;
    private static final int _7MB = 7 * 1024 * 1024;
    private static final int _8MB = 8 * 1024 * 1024;

    // -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            ArrayList<byte[]> list = new ArrayList<>();
            list.add(new byte[_8MB]);
            list.add(new byte[_8MB]);
        }).start();
		//主线还是会正常执行
        System.out.println("sleep....");
        Thread.sleep(1000L);
    }
}

在这里插入图片描述

4. 垃圾回收器

串行垃圾回收器

吞吐量优先垃圾回收器

响应时间优先垃圾回收器

  • 多线程
  • 适合堆内存较大多核cpu
  • 响应时间优先,即尽可能让单次STW的时间最短
  • 例如:1h内发生了5次垃圾回收,0.1 + 0.1 +0.1 + 0.1 + 0.1 = 0.5 总耗时0.4,单次STW 0.1

4-1. 串行

开启串行垃圾回收器参数

XX:+UseSerialGC = Serial + SerialOld

Serial 收集器

Serial Old收集器

在这里插入图片描述

安全:即发生垃圾回收时,让所有线程都在这个点停下来,以免垃圾回收时移动对象地址,使得其他线程找不到被移动的对象。

阻塞:因为是串行的,所以只有一个垃圾回收线程。且在该线程执行回收工作时,其他线程进入阻塞状态

4-2. 吞吐量优先

开启吞吐量优先回收器:

JDK1.8默认使用的垃圾回收器

在这里插入图片描述
在这里插入图片描述

Parallel 收集器

Parallel Old 收集器

4-3. 响应时间优先

在这里插入图片描述

CMS收集器:

ParNew 收集器:

  • 定义:ParNew收集器其实就是Serial收集器的多线程版本
  • 特点:工作在新生代基于复制算法的垃圾回收器。

4-4. G1

Garbage First

  • JDK 9以后默认使用,而且替代了CMS 回收器

适用场景

开启G1参数

在这里插入图片描述

G1垃圾回收阶段

Young Collection: 对新生代的垃圾回收
Young Collection + Concurrent Mark 对新生代的垃圾回收 同时执行并发标记
Mixed Collection: 对新生代老年代进行垃圾回收
在这里插入图片描述

Young Collection

在这里插入图片描述

  • 当伊甸园内存不足时,触发新生代垃圾回收
  • 存活的对象以复制算法放入到幸存区S

在这里插入图片描述

  • 当幸存区的内存不足时,并且幸存区对象的年龄超过阈值,触发新生代垃圾回收,将幸存区的一部分对象晋升老年代

Young Collection + CM

  • CM:并发标记
  • 在 Young GC 时会对 GC Root 进行初始标记
  • 当老年代占用堆内存的比例达到阈值时,对进行并发标记(不会STW),阈值可以根据用户来进行设定
  • 默认45%,当老年代占堆内存的45%则会进行并发标记
    在这里插入图片描述

Mixed Collection

会对E S O 进行全面的回收

为什么有的老年代被拷贝了,有的没拷贝

  • 因为指定最大停顿时间,如果对所有老年代都进行回收(复制算法),耗时可能过高。为了保证时间不超过设定的停顿时间,会回收最有价值的老年代(回收后,能够得到更多内存)
    在这里插入图片描述

Full GC

  • SerialGC

    • 新生代内存不足发生的垃圾收集 – minor gc
    • 老年代内存不足发生的垃圾收集 – full gc
  • ParallelGC

    • 新生代内存不足发生的垃圾收集 – minor gc
    • 老年代内存不足发生的垃圾收集 – full gc
  • CMS

    • 新生代内存不足发生的垃圾收集 – minor gc
    • 老年代内存不足
      • 如果垃圾产生速度慢于垃圾回收速度,不会触发Full GC,还是并发地进行清理
      • 如果垃圾产生速度快于垃圾回收速度,便会触发Full GC
  • G1

    • 新生代内存不足发生的垃圾收集 – minor gc
    • 老年代内存不足(老年代所占内存超过阈值)
      • 如果垃圾产生速度慢于垃圾回收速度,不会触发Full GC,还是并发地进行清理
      • 如果垃圾产生速度快于垃圾回收速度,便会触发Full GC

如果触发了Full GC打印日志中会显示Full GC (开启GC参数的前提下)

Young Collection 跨代引用

新生代回收的跨代引用(老年代引用新生代)问题

在这里插入图片描述

remark

黑色:已被处理,有引用在引用,即存活对象

灰色:正在处理中的对象

白色:还未处理的对象

在并发标记过程中,有可能A被处理了以后未引用C,但该处理过程还未结束,在处理过程结束之前A引用了C,这时就会用到remark

  • 之前C未被引用,这时A引用了C,就会给C加一个写屏障,写屏障指令会被执行,将C放入一个队列当中,并将C变为 处理中 状态
  • 在并发标记阶段结束以后,重新标记阶段会STW,然后将放在该队列中的对象重新处理,发现有强引用引用它,就会处理它

在这里插入图片描述

JDK 8u20 字符串去重

  • 优点:节省大量内存
  • 缺点:略微多占用cpu 时间,新生代回收时间略微增加

案例

String s1 = new String("hello"); // char[]{'h','e','l','l','o'}
String s2 = new String("hello"); // char[]{'h','e','l','l','o'}

JDK 8u40 并发标记类卸载

所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类

-XX:+ClassUnloadingWithConcurrentMark 默认启用

JDK 8u60 回收巨型对象

  • JDK 8u60 回收巨型对象一个对象大于 region 的一半时,称之为巨型对象
  • G1 不会对巨型对象进行拷贝
  • 回收时被优先考虑回收巨型对象
  • G1 会跟踪老年代所有 incoming 引用,这样老年代 incoming 引用为0的巨型对象就可以在新生代垃圾回收时处理掉

在这里插入图片描述

JDK 9 并发标记起始时间的调整

5. 垃圾回收调优

5-1 调优领域

5-2 确定目标

5-3 最快的 GC

查看Full GC前后的内存占用考虑以下几个问题:

5-4 新生代调优

  • 新生代的特点:

  • 新生代内存越大越好么?

    • 不是

      • 新生代内存太小:频繁触发Minor GC,会STW,会使得吞吐量下降
      • 新生代内存太大:老年代内存占比有所降低,会更频繁地触发Full GC。而且触发Minor GC时,清理新生代所花费的时间会更长
    • 新生代内存设置为能容纳 [并发量x(请求-响应)] 的数据为宜

    • 幸存区大到能保留【当前活跃对象+需要晋升对象】

    • 晋升阈值配置得当,让长时间存活对象尽快晋升

    • 吞吐量 = 程序运行时间 / 程序运行时间 + 垃圾回收时间

原文地址:https://blog.csdn.net/weixin_44147535/article/details/134584488

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_26364.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注