新生代垃圾回收算法

  public class kafka {

    public static void main(String[] args) throws InterruptedException {
            loadReplicasFromDisk();
    }
    private static void loadReplicasFromDisk(){
        ReplicaManager replicaManager = new ReplicaManager();
    }
}

当前main线程的内存图
ce

等该loadReplicasFromDisk方法执行完成以后方法出栈,堆内存中的replicaManager对象会失去引用,然后继续执行,接着这个时候,新生代内存那块被分配对象的内存区域基本都快满了,再次要分配对象的时候,发现里面内存空间不足了。
此时会触发 Minor GC去回收新生代那块被使用的内存空间中的垃圾对象

回收算法

普通算法

根据GC Roots标记出哪些对象是可以被垃圾回收的,然后就直接对那块内存区域中的对象进行垃圾回收,把内存空出来。

当前算法回收掉垃圾对象以后会保留一些存活对象,但是会造成大量的内存碎片

内存碎片多了以后会造成空间浪费,因为合在一起会有很大的内存空间,但是因为不连续,所以没有一块完整的足够内存来分配新的对象(mysql中对于碎片是使用链表和分页方式解决)

复制算法

将新生代内存分为两块,先将带有GC Roots标记的对象移到空的内存中,因为是重新移动的,所以在新的内存块中对象是紧凑的,然后新的对象进来以后直接在存活对象的内存块中创建,然后将原先的垃圾块全部回收

把新生代内存划分为两块内存区域,然后只使用其中一块内存,待那块内存快满的时候,就把里面的存活对象一次性转移到另外一块内存区域,保证没有内存碎片,然后循环使用

该方法有个缺点,对于1G分配的堆内存来说,只有512M的可用空间,内存使用率过低

复制算法优化:Eden区和Survivor区

该优化基于一个条件,即生产环境中绝大部分的对象都是生命周期很短的对象,可能一次回收以后99%的对象都被回收了,最后只留下1%的对象

方法:将新生代内存区域分为三块
1个Eden区,2个Survivor区,其中Eden区占80%内存空间,每一块Survivor区各占10%内存空间,比如说Eden区有800MB内存,每一块Survivor区就100MB内存,如下图。

平时可以使用的,就是Eden区和其中一块Survivor区,那么相当于就是有900MB的内存是可以使用的,如下图所示。

但是刚开始对象都是分配在Eden区内的,如果Eden区快满了,此时就会触发垃圾回收
此时就会把Eden区中的存活对象都一次性转移到一块空着的Survivor区。接着Eden区就会被清空,然后再次分配新对象到Eden区里,然后就会如上图所示,Eden区和一块Survivor区里是有对象的,其中Survivor区里放的是上一次Minor GC后存活的对象(即上次gc从Eden区转移过去的1%的存活对象)。
如果下次再次Eden区满,那么再次触发Minor GC,就会把Eden区和放着上一次Minor GC后存活对象的Survivor区内的存活对象,转移到另外一块Survivor区去。
比如Eden区+一块Survivor区有900MB的内存空间都占满了,但是垃圾回收之后,可能就10MB的对象是存活的。
此时就把那10MB的存活对象转移到另外一块Survivor区域就可以,然后再一次性把Eden区和之前使用的Survivor区里的垃圾对象全部回收掉,如下图。

接着新对象继续分配在Eden区和另外那块开始被使用的Survivor区,然后始终保持一块Survivor区是空着的,就这样一直循环使用这三块内存区域。
这样做会使得只有10%的空间是闲置的,相比刚才的复制算法,多40%的空间利用率

posted @ 2021-07-23 18:08  灰原二  阅读(352)  评论(0)    收藏  举报