JVM GC
JVM的垃圾回收算法
垃圾回收(Garbage Collection,简称:GC )
主要是清理存在内存中不会再被使用的对象。如果当大量不使用的对象占用着内存空间,需要内存的空间时,就无法使用这些被垃圾对象所占用的内存空间,当此时要存储大量的对象时,则可能导致内存溢出。
常用垃圾回收的算法
1.引用计数法(Reference Counting)
- 通过获取给对象配备的引用计数器的值,来判断对象是否处于使用中。对象被引用时,计数器+1;引用失效时,计数器-1。当引用计数器为0时,则说明当前的对象已经处于不在被使用的状态。
- 优点:实现简单,只需要给每个对象配备一个整型的计数器即可。
- 缺点:
- 无法处理被循环引用或者互相引用的情况。这样会使GC无法识别,导致内存泄露。
- 引用计数器在每次对象被引用和消除引用的时候,伴随着一次加法操作和减法操作。这样对系统性能会有一定的影响。
- 结论:由于单纯的引用计数算法隐含着循环引用和性能的问题,JVM并不选择此算法。
2.标记清除法(Mark-Sweep)
3.复制算法(Copying)
- 复制算法的核心思想:将原有的内存分为2块。每次只使用其中的一块,在垃圾回收的时候,将使用一块中存活的对象全部复制到另外一块未使用的内存中去,然后清除使用内存中所有的对象。交换两个内存的角色,完成GC。具体如下图所示:
- 在java新生代3的串行垃圾回收器中,使用了复制算法,新生代分为:edenSpace、fromSpace、toSpace三个部分。其中fromSpace、toSpace4可以视为用于复制两块大小相同,地位相等并且可进行角色互换的空间块。
- 适用场景:复制算法比较适用于新生代,因为在新生代中,垃圾对象会比存活对象多,所以复制算法GC的效果比较可观。
- 优点:在存活对象少,垃圾对象多的情况下,复制算法的可以体现其高效性。
- 缺点:在存活对象多的情况下,比如老年代中,每次GC时,Copy的成本比较高,并不能体现其高效性。
4.标记压缩法(Mark-Compact)
- 标记压缩法是一种老年代的回收算法。此算法是在标记清除算法(Mark-Sweep)的基础上的优化。其开始都是从根对象开始,对所有可达对象1进行一次标记,与标记清除算法不一样的地方是在标记完成以后,并不是直接清理所以不可达对象,而是先对可达的对象进行了一次压缩整理至一片连续的内存中,然后清理其余所有的内存空间。
标记压缩法的最终效果也等价于标记清除法之后,对内存空间进行了一次内存碎片的整理。因此也可以称为标记清除压缩法(MarkSweepCompact)
如图:
- 优点:避免了标记清除法中的碎片内存空间的产生,也不需要复制算法中分为两个相同的内存空间。
5.分代算法(Generational Collecting)
- 分代算法是将内存根据对象的特点分为几块,根据每块内存区间的特点,使用不同的算法,以提高垃圾回收的效率。
- 在新生代中朝生夕灭的对象会被很快的回收,数量也比较多。回收的频率会高于老年代。所以新生代可以使用复制算法。
- 在老年代中,极端的情况下,对象的存活率可以达到100%,如果依然使用复制算法,则需要复制大量的对象,其性能太低,此法是不可取的。此时可以使用标记压缩法或者标记清除算法来进行老年代的垃圾回收,可以提高GC的效率。
6.分区算法(Region)
- 上述的分代算法是将Heap空间中对象的生命周期的长短分为两个部分,分区算法则是整个Heap空间分为连续不同的小区间。
- 好处:这样做的好处是在Heap空间越大的情况下,每次GC的耗时就越长。从而产生的系统停顿就越长。此时 采用分区算法,则可以根据系统停顿的时间的限制,每次可以合理的回收若干个小区间,而不是统一回收整个Heap的空间。这样就可以减少GC所产生的停顿。
参考书籍:《实战Java虚拟机—JVM故障诊断与性能优化》