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 {
    }
}
相关文章
|
1月前
|
算法 Java
JVM垃圾回收机制
JVM垃圾回收机制
15 0
|
2月前
|
Java 程序员
探讨JVM垃圾回收机制与内存泄漏
探讨JVM垃圾回收机制与内存泄漏
|
3月前
|
算法 Java 关系型数据库
掌握这3个技巧,你也可以秒懂JAVA性能调优和jvm垃圾回收
JVM 是一个虚拟化的操作系统,类似于 Linux 和 Window,只是他被架构在了操作系统上进行接收 class 文件并把 class 翻译成系统识别的机器码进行执行,即 JVM 为我们屏蔽了不同操作系统在底层硬件和操作指令的不同。
25 0
|
16天前
|
缓存 监控 Java
深入理解Java虚拟机(JVM)性能调优
【4月更文挑战第18天】本文探讨了Java虚拟机(JVM)的性能调优,包括使用`jstat`、`jmap`等工具监控CPU、内存和GC活动,选择适合的垃圾回收器(如Serial、Parallel、CMS、G1),调整堆大小和新生代/老年代比例,以及代码优化和JIT编译策略。通过这些方法,开发者能有效提升应用性能并应对复杂性挑战。性能调优是持续过程,需伴随应用演进和环境变化进行监控与优化。
|
17天前
|
监控 Java 调度
探秘Java虚拟机(JVM)性能调优:技术要点与实战策略
【4月更文挑战第17天】本文探讨了JVM性能调优的关键技术,包括内存模型调优(关注堆内存和垃圾回收),选择和优化垃圾收集器,利用JVM诊断工具进行问题定位,以及实战调优案例。强调了开发者应理解JVM原理,善用工具,结合业务场景进行调优,以应对高并发和大数据量的挑战。调优是持续的过程,能提升系统稳定性和效率。
|
21天前
|
存储 前端开发 安全
JVM内部世界(内存划分,类加载,垃圾回收)(上)
JVM内部世界(内存划分,类加载,垃圾回收)
52 0
|
26天前
|
存储 缓存 算法
深度解析JVM世界:垃圾判断和垃圾回收算法
深度解析JVM世界:垃圾判断和垃圾回收算法
|
2月前
|
存储 算法 Java
精华推荐 | 【JVM深层系列】「GC底层调优专题」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)
精华推荐 | 【JVM深层系列】「GC底层调优专题」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)
57 0
|
2月前
|
Java Serverless 对象存储
Serverless 应用引擎常见问题之jvm在进行垃圾回收的时候会导致重启如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
24 0
|
2月前
|
算法 Java UED
【JVM】分代收集算法:提升Java垃圾回收效率
【JVM】分代收集算法:提升Java垃圾回收效率
22 0