前言:
上期我们简单介绍了jvm优化的方向和部分gc算法,本期我们来介绍剩下的gc回收算法
什么是GC?
垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。
注意:垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身。换言之,垃圾回收只会负责释放那些对象占有的内存。对象是个抽象的词,包括引用和其占据的内存空间。当对象没有任何引用时其占据的内存空间随即被收回备用,此时对象也就被销毁。但不能说是回收对象,可以理解为一种文字游戏。
引用计数法
给每一个对象设置一个counter,只要有任何一个对象引用了该对象,那么就给其counter加1,当引用失效时,counter减1,当counter等于0时,对象就不存在任何引用,在GC时就被清除
优点:思想和实现都很简单,给每一个对象配备一个整型的计数器即可
缺点:
counter在每次引用生效和失效时都会进行加减法操作,会影响性能
无法处理循环引用问题,可能会造成死锁,如下图所示
A和B互相引用,此时他们俩的count都不会为0,就会一直存在,不会被回收,导致内存泄漏,所以jvm放弃使用这种算法
标记压缩法(标记清除压缩法)
标记压缩法是标记清除法的优化,和标记清除法一样,会先标记所有的存在引用的对象,和标记清楚算法不同的是,标记完成后并不直接清除未标记的垃圾对象,而是将所有的被标记的对象(即存活对象)压缩到内存空间的一端后在清理其他所有的空间。如下图所示
如图所示,将存活对象移动到一端后会将剩余部分清除,这样就不会产生内存碎片,大大提高内存利用率
优点:解决了标记清除法带来的空间碎片问题,又不像复制算法一样折损可使用空间
常用于年老代
因为新生代垃圾对象多于存活对象,所以使用复制算法效率高(要复制的对象少),老年代刚好相反,存活对象多于垃圾对象,复制算法会浪费大量空间,故使用标记压缩法
分代算法:
将内存空间根据对象的特点来进行划分,选择合适的垃圾回收算法,来提高垃圾回收的效率。如jvm将对象分为年轻代和年老代
因为不同的对象使用的算法不一致,所以GC的过程也不一样,比如新生代使用的是复制算法,老年代则使用标记压缩法
如年轻代会在form区和to区采用复制算法,没经历过一次minor gc 年龄+1,年龄超过15变为老年代,这个数值可以通过配置进行修改,
因为新生代gc频率较高,为了提高效率,jvm使用卡表这一数据结构,如下图所示
卡表为一个byte位集合,每一个元素表示某一个区域内的老年代对是否有新生代的引用,
如此一来,新生代gc时就可以不用去扫描所有年老代来判断是否新生代对象是否存在引用,只需要去扫描标记为1的区域内的老年代对象,为0区域中的老年代则是一定不含有新生代对象引用的
分区算法
将整个堆空间划分为连续的不同小区间,每一个小区间都独立使用,独立回收。
优点:可以控制一次回收多少个小区间而不是整个堆空间gc,
堆空间越大,一次GC所需的时间越长,产生的停顿时间就越长。为了更好的控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理的回收若干个小区间,而不是整个堆空间,从而减少一个GC的停顿时间。
标记清除算法和复制顺发上期介绍过了,本期就不介绍了,有兴趣的可以去上期查看
小编定期整理所学的知识,有兴趣的朋友可以关注下一起探讨,