JVM02——JVM垃圾回收与性能调优(上)(二)

简介: 6.垃圾回收6.1 判断垃圾6.1.1 引用计数法
public static void soft() {
        // list --> SoftReference --> byte[]
        List<SoftReference<byte[]>> list = new ArrayList<>();
        ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], referenceQueue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }
        Reference<? extends byte[]> poll = referenceQueue.poll();
        while (poll != null) {
            list.remove(poll);
            poll = referenceQueue.poll();
        }
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }

结果如下。

[B@7f31245a
1
[B@6d6f6e28
2
[B@135fbaa4
3
[B@45ee12a7
4
[B@330bedb4
5
循环结束:1
[B@330bedb4
Process finished with exit code 0

6.4 弱引用

与软引用十分类似。

/**
 * 演示弱引用
 * -Xmx20m -XX:+PrintGCDetails 
 */
public class Demo2_5 {
    private static final int _4MB = 4 * 1024 * 1024;
    public static void main(String[] args) {
        //  list --> WeakReference --> byte[]
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 10; 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());
    }
}

打印的结果如下。其中第10 次循环时,由于弱引用本身也占有一定的内存,触发Full GC。

[B@7f31245a 
[B@7f31245a [B@6d6f6e28 
[B@7f31245a [B@6d6f6e28 [B@135fbaa4 
[GC (Allocation Failure) [PSYoungGen: 2209K->504K(6144K)] 14497K->13139K(19968K), 0.0023913 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@7f31245a [B@6d6f6e28 [B@135fbaa4 [B@45ee12a7 
[GC (Allocation Failure) [PSYoungGen: 4712K->496K(6144K)] 17347K->13326K(19968K), 0.0025155 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@7f31245a [B@6d6f6e28 [B@135fbaa4 null [B@330bedb4 
[GC (Allocation Failure) [PSYoungGen: 4704K->504K(6144K)] 17534K->13350K(19968K), 0.0020861 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[B@7f31245a [B@6d6f6e28 [B@135fbaa4 null null [B@2503dbd3 
[GC (Allocation Failure) [PSYoungGen: 4711K->504K(6144K)] 17557K->13382K(19968K), 0.0017767 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@7f31245a [B@6d6f6e28 [B@135fbaa4 null null null [B@4b67cf4d 
[GC (Allocation Failure) [PSYoungGen: 4710K->456K(6144K)] 17588K->13334K(19968K), 0.0017985 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@7f31245a [B@6d6f6e28 [B@135fbaa4 null null null null [B@7ea987ac 
[GC (Allocation Failure) [PSYoungGen: 4775K->504K(5120K)] 17653K->13398K(18944K), 0.0011480 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@7f31245a [B@6d6f6e28 [B@135fbaa4 null null null null null [B@12a3a380 
[GC (Allocation Failure) [PSYoungGen: 4735K->256K(5632K)] 17629K->13531K(19456K), 0.0016537 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 256K->0K(5632K)] [ParOldGen: 13275K->782K(8192K)] 13531K->782K(13824K), [Metaspace: 3345K->3345K(1056768K)], 0.0108212 secs] [Times: user=0.17 sys=0.00, real=0.01 secs] 
null null null null null null null null null [B@29453f44 
循环结束:10


当然,弱引用与软引用的区别是,只要触发垃圾回收,无论内存是否充足都会回收其引用对象。

public class WeakReferenceDemo {
    public  static  void  main(String[] args) {
        WeakReference<String> sr =  new  WeakReference<String>( new  String( "hello" ));
        System.out.println(sr.get());
        System.gc();                 //通知JVM的gc进行垃圾回收
        System.out.println(sr.get());
    }
}

输出结果。


 
         

6.5 回收算法

6.5.1 标记清除算法

先标记(将不可被GC Root直接或者间接访问的内存标记),再清除(并不是做清零操作,而是被空闲的内存起始地址放入空闲内存表,下次分配内存时就可以使用)。这种方式的优点是速度快,缺点是容易产生内存碎片,比如存储一个数组对象,总的内存空间足够,但是内存不连续,依然会导致内存溢出问题。


6.5.2 标记整理算法

先标记,再整理(移动对象)。优点是内存连续,缺点是消耗一定的时间,(对象移动,同时对象的地址发生变化,如果对象有引用,那么引用中保存的地址也需要随之发生改变。)

6.5.3 复制算法

先标记,后复制。将对象从from移动到to区域,在移动过程中就完成了内存整理工作。同时交换from和to区。优点是空间连续,缺点是需要使用双倍的内存空间。

6.6 分代回收机制

JVM同时综合使用了三种垃圾回收算法。这就是分代回收机制。内存空间可以分为新生代和老年代,新生代又可以分为伊甸园和幸存者from,幸存者to。之所以采用分代回收机制,是为了使不同的垃圾回收策略。新生代用于存放朝生夕死的对象,会频繁的进行垃圾清理。

一个对象被创建后,首先会放入新生代的伊甸园中。

当新生区内存无法放入新的对象时,会触发一次Minor GC,将根据根可达算法判断伊甸园和幸存区From中哪些对象可以被回收,对于没有被垃圾回收的对象,根据复制算法将其复制到幸存区to中,交换幸存区From和幸存区To,并将未被回收对象寿命增1。Minor GC会引发STW(Stop the world),即进行垃圾回收时其他用户线程会被暂停。之所以要触发STW是因为垃圾回收的过程中会改变对象的地址,如果不暂停其他线程,当其他线程找不到对象会发生混乱。因为大部分对象都会被垃圾回收,需要通过复制算法改变内存地址的对象并不多,Minor GC的SWT较短。

当幸存区中的对象寿命到了阈值(最大为15{4bit}),说明这些对象的生命周期较长,这些对象将会被移到老年代中,当内存资源较为紧张,新生代存放不下更多对象,也可能将对象移到老年代中。老年代的垃圾回收频率较低。

如果堆中新生代快满了,放不进新的对象,同时老年代也快满了,会先尝试触发Minor GC,空间仍然不足就会触发Full GC。对整个堆进行垃圾回收。因为Full GC时老年代的回收算法耗时,同时要回收的对象数量较多,Full GC的SWT时间较长。如果Full GC后内存仍然不足就会触发Out of Memory。

6.7 GC分析

下面我们通过实例对GC的过程分析。开始GC分析之前,先了解一些GC常用的一些参数。其中上表中的晋升是指新生代晋升到老年代。

参考下面代码,设置参数并运行。其中参数-XX:+UserSerialGC是将垃圾回收器设置为UserSerialGC,这种垃圾回收器的幸存区不会进行自动调整,有助于我们观察现象。

/**
 *  演示内存的分配策略
 */
public class Demo2_1 {
    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 {
    }
}
目录
打赏
0
0
0
0
4
分享
相关文章
CMS圣经:CMS垃圾回收器的原理、调优,多标+漏标+浮动垃圾 分析与 研究
本文介绍了CMS(Concurrent Mark-Sweep)垃圾回收器的工作原理、优缺点及常见问题,并通过具体案例分析了其优化策略。重点探讨了CMS的各个阶段,包括标记、并发清理和重标记
CMS圣经:CMS垃圾回收器的原理、调优,多标+漏标+浮动垃圾 分析与 研究
JVM实战—4.JVM垃圾回收器的原理和调优
本文详细探讨了JVM垃圾回收机制,包括新生代ParNew和老年代CMS垃圾回收器的工作原理与优化方法。内容涵盖ParNew的多线程特性、默认线程数设置及适用场景,CMS的四个阶段(初始标记、并发标记、重新标记、并发清理)及其性能分析,以及如何通过合理分配内存区域、调整参数(如-XX:SurvivorRatio、-XX:MaxTenuringThreshold等)来优化垃圾回收。此外,还结合电商大促案例,分析了系统高峰期的内存使用模型,并总结了YGC和FGC的触发条件与优化策略。最后,针对常见问题进行了汇总解答,强调了基于系统运行模型进行JVM参数调优的重要性。
JVM实战—4.JVM垃圾回收器的原理和调优
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
JVM实战—3.JVM垃圾回收的算法和全流程
本文详细介绍了JVM内存管理与垃圾回收机制,涵盖以下内容:对象何时被垃圾回收、垃圾回收算法及其优劣、新生代和老年代的垃圾回收算法、Stop the World问题分析、核心流程梳理。
JVM实战—3.JVM垃圾回收的算法和全流程
JVM实战—5.G1垃圾回收器的原理和调优
本文详细解析了G1垃圾回收器的工作原理及其优化方法。首先介绍了G1通过将堆内存划分为多个Region实现分代回收,有效减少停顿时间,并可通过参数设置控制GC停顿时长。接着分析了G1相较于传统GC的优势,如停顿时间可控、大对象不进入老年代等。还探讨了如何合理设置G1参数以优化性能,包括调整新生代与老年代比例、控制GC频率及避免Full GC。最后结合实际案例说明了G1在大内存场景和对延迟敏感业务中的应用价值,同时解答了关于内存碎片、Region划分对性能影响等问题。
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
130 29
JVM简介—1.Java内存区域
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
678 166
JVM实战—2.JVM内存设置与对象分配流转
本文详细介绍了JVM内存管理的相关知识,包括:JVM内存划分原理、对象分配与流转、线上系统JVM内存设置、JVM参数优化、问题汇总。
JVM实战—2.JVM内存设置与对象分配流转
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
882 1