11_JVM 中的垃圾回收算法
1018字约3分钟
2024-08-10
新生代使用的复制算法
复制算法
复制算法就是按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉
假设 JVM
的新生代直接划分为两个大小相等的内存区域,随着系统的不断运行,其中一块内存空间会被耗尽,再有新的对象分配内存时,发现内存空间不足了,此时就触发 Young GC
将存活对象转移到另外一块空白内存中,随后再一次性把原来使用的那块内存区域全部回收掉,这样就空出来一整块内存区域,如下图所示

可能会有疑问,为什么进行转移,而不是直接在内存中回收垃圾对象释放内存呢?
由于存活对象在内存区域中不是相邻排列的,东一个西一个的,如果直接回收内存中的垃圾对象,就会造成大量的内存碎片,很多内存碎片是没法使用的,会造成内存浪费,所以回收垃圾对象,保留存活对象的方法是行不通的
JVM 中新生代复制算法
复制算法的缺点是内存使用率不高,只有原来的一半
所以实际上在 JVM
中,将新生代内存区域划分为三块:1
个 Eden
区、2
个 Survivor
区,其中 Eden
区占 80%
内存空间,每一块 Survivor
区各占 10%
之前我们分析过 JVM
的内存使用模型,绝大多数对象的存活周期都非常短,可能创建出来很快就没人引用了,可能一次新生代垃圾回收之后,99%
的对象都被垃圾回收了,就 1%
的对象存活了下来
比如说 Eden
区有 800MB
内存,每一块 Survivor
区 100MB
内存,就会有下面的流程
1、一开始对象都分配在
Eden
区内,如果Eden
区快满了就会触发垃圾回收2、将
Eden
区中的存活对象都一次性转移到一块空着的Survivor
区,接着清空Eden
区3、继续分配新对象到
Eden
区,下次再触发垃圾回收,就会把Eden
区以及上一次Young GC
后存放对象的Survivor
区的存活对象,转移到另外一块Survivor
区4、接着回收
Eden
区和上一次被使用的Survivor
区,始终保持一块Survivor
区是空着的,就这样一直循环使用这三块区域
这样做最大的好处就是只有 10%
的内存空间是闲置的,90%
的内存都被使用上了,无论是垃圾回收的性能、内存碎片的控制、内存使用效率都非常好

老年代使用的标记整理算法
老年代采取的是标记整理算法
1、标记:标记垃圾对象
2、整理:将存活对象向一端移动
3、清除:清除存活对象端边界以外的内存

需要注意的是,老年代的垃圾回收算法速度至少比新生代的垃圾回收算法速度慢
10
倍,如果频繁出现老年代Full GC
,对系统性能有严重影响,出现频繁卡顿的情况
如果理解了JVM
运行原理,就会知道,所谓JVM
优化,就是尽可能让对象在新生代分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时也分配合理的内存,避免新生代频繁的垃圾回收