JVM学习-分代收集算法

1.分代回收算法概述

1.1.分代回收算法简介

我们在前面讲了三种垃圾回收算法,实际上,虚拟机结合这三种算法,让它们协同工作,具体的实现使用分代垃圾回收机制。
在这里插入图片描述

我们将堆内存区域划分为新生代和老年代。
新生代中又划分为伊甸园、幸存区From、幸存区To

因为Java中有的对象需要长时间使用,长时间使用的对象,我们将之放在老年代中,而那些用完了就可以丢弃的对象,我们将之放在新生代中,这样我们就可以根据对象生命周期的不同特点进行不同的垃圾回收策略。老年代的垃圾回收很久才一次,新生代的垃圾回收则比较频繁。针对不同的区域我们采用不同的算法来对我们的垃圾回收进行一个管理
我们可以做一个形象的比喻,比如保洁工人而言,垃圾箱每天清理一次,一栋楼的话我们清理的次数可能就少一些。

1.2.回收流程

新创建的对象都被放在了新生代的伊甸园中
伊甸园是一个很形象的比喻,是西方国家神话传说中人类始祖亚当夏娃诞生的地方。
在这里插入图片描述
当伊甸园中的内存不足时,就会进行一次垃圾回收,这时的回收叫做 Minor GC
Minor:小的动作
Minor采用可达性分析算法,沿着GCRoot链查找,看这些对象是有用还是作为垃圾。
Minor GC 会将伊甸园和幸存区FROM存活的对象先复制到 幸存区 TO中, 并让其寿命加1,再交换两个幸存区
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
再次创建对象,若新生代的伊甸园又满了,则会再次触发 Minor GC(会触发 stop the world, 暂停其他用户线程,只让垃圾回收线程工作),这时不仅会回收伊甸园中的垃圾,还会回收幸存区中的垃圾,再将活跃对象复制到幸存区TO中。回收以后会交换两个幸存区,并让幸存区中的对象寿命加1

在这里插入图片描述
在这里插入图片描述
如果幸存区中的对象的寿命超过某个阈值(最大为15,4bit),就会被晋升老年代中

在这里插入图片描述
如果新生代老年代中的内存都满了,就会先触发Minor Gc,再触发Full GC,扫描新生代和老年代中所有不再使用的对象并回收
Full GC当老年代内存不足,即老年代的垃圾回收。

1.3.回收流程总结

对象首先分配在伊甸园区域
新生代空间不足时,触发minor gc,伊甸园和from存活的对象使用copy赋值到to中,存活的对象年龄加1并且交换from to
minor gc会引发stop the world(即在发生垃圾回收的时候,必须暂停其他的用户线程,当垃圾回收线程的任务做完了,其它线程才能运行,因为牵扯到内存的复制).暂停其它用户线程,等垃圾回收结束,用户线程才恢复运行。
当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)。(但是在新生代内存紧张时可能会变小)
当老年代空间不足,会先尝试触发minor gc,如果之后空间仍不足,那么会触发full gc,STW的时间更长(因为老年代的存活数量比较多)。
如果老年代的内存还是不足,就会触发堆内存溢出。

2.相关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

3.GC分析

/**
 *  演示内存的分配策略
 */
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;
	//堆初始20M,最大20M,新生代10M,UseSerialGC垃圾回收器。最后两个参数打印详细
    // -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
    public static void main(String[] args) throws InterruptedException {
    
    
    }
}

在这里插入图片描述
new generation新生代,tenured generation老年代 Meta元空间,它并不属于堆的一部分。
新生区这里有9M,8M划分给了伊甸园,幸存区from和to各占用1M,幸存区to要始终空着,所以计算空间时把1M给去掉了。
老年代没有被使用。

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[_7MB]);
        list.add(new byte[_512KB]);
        list.add(new byte[_512KB]);
}

在这里插入图片描述

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

扫描二维码关注公众号,回复: 12440879 查看本文章
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 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 {
    
    
        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);
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_39736597/article/details/113479284