8. 内存回收(GC算法)
GC 算法
这里讨论的是oracle的Hotspot VM常见的垃圾回收算法。使用的搜索算法都是基于根搜索算法实现的
标记-清除算法(Mark-Sweep)
该算法分两步执行:
- 标记Mark:从GC ROOTS开始,遍历堆内存区域的所有根对象,对在引用链上的对象都进行标记。这样下来,如果是存活的对象就会被做了标记,反之如果是垃圾对象,则没做有标记。GC很容易根据有没有被做标记就完成了垃圾对象回收。
- 清除Sweep:遍历堆中的所有的对象(标记阶段遍历的是所有根节点),找到未被标记的��象,直接回收所占的内存,释放空间。
优点
- 没有产生额外的内存空间消耗,内存利用率高。
缺点
- 效率低,清除阶段要遍历所有的对象;回收的垃圾对象是在各个角落的,直接回收垃圾对象,导致存在不连续的内存空间,产生内存碎片。
标记-清除算法操作的对象是【垃圾对象】,对于活着的对象(被标记的对象),它则直接不理睬。
复制算法(Copying)
复制算法把内存区间一分为二,有对象存在的一半区间称为“活动区间”,没有对象存在处于空闲状态的空间则为“空闲区间”。
当内存空间不足时触发GC,先采用根搜索算法标记对象,然后把活着的对象全部复制到另一半空闲区间上,复制算法的“复制”就来自这一操作。复制到另一半区间的时候,严格按照内存地址依次排列要存放的对象,然后一次性回收垃圾对象。
这样原来的空闲区间在GC后就变成活动区间,而且内存顺序齐整美观。原来的活动区间在GC后就变成了完全空的空闲区间,等待下一次GC把活的对象被copy进来。
优点
- GC后的内存齐整,不产生内存碎片
缺点
- GC要使用两倍的内存,或者说导致堆只能使用被分配到的内存的一半,这个算法对空间要求太高!如果存活的对象较多,则意味着要复制很多对象并且要维护大量对象的内存地址,所以存活的对象数量不能太多,否则效率也会很低。
复制算法复制移动的对象是【活着的对象】,对于垃圾对象(不被标记的对象)则直接回收。
标记-整理算法(Mark-Compact)
这个算法则是对上面两个算法的综合结果。也分为两个阶段:
- 标记:这个阶段和标记-清除Mark-Sweep算法一样,遍历GC ROOTS并标记存活的对象。
- 整理:移动所有活着的对象到内存区域的一侧(具体在哪一侧则由GC实现),严格按照内存地址次序依次排列活着的对象,然后将最后一个活着的对象地址以后的空间全部回收。
-
优点
内存空间利用率高,消除了复制算法内存减半的情况;GC后不会产生内存碎片。
缺点
- 需要遍历标记活着的对象,效率较低;复制移动对象后,还要维护这些活着对象的引用地址列表。
分代回收算法(Generational Collecting)
分代回收算法就是现在JVM使用的GC回收算法。
简要说明
- 先来看看简单化后的堆的内存结构
1 | Java堆 = 年老代 + 年轻代 |
- 按照对象存活时间长短,我们可以把对象简单分为三类:
- 短命对象:存活时间较短的对象,如中间变量对象、临时对象、循环体创建的对象等。这也是产生最多数量的对象,GC回收的关注重点。
- 长命对象:存活时间较长的对象,如单例模式产生的单例对象、数据库连接对象、缓存对象等。
- 长生对象:一旦创建则一直存活,几乎不死的对象。
- 对象分配区域
- 短命对象存在于年轻代
- 长命对象存在于年老代
- 长生对象则存在于方法区中。
由于GC的主要内存区域是堆所以GC的对象主要就是短命对象和长命对象这类寿命“有限”的对象。
分代回收的GC类型
针对HotSpot VM的的GC其实准确分类只有两大种
- Partial GC:部分回收模式
- Young GC:只收集young gen的GC。和Minor GC一样。
- Old GC:只收集old gen的GC。只有CMS的concurrent - collection是这个模式
- Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式
- Full GC:收集整个堆,包括young gen、old gen,还有永久代perm gen(如果存在的话)等所有部分的模式。同Major GC
触发时机
HotSpot VM的串行GC的触发条件是:
young GC:当young gen中的eden区分配满的时候触发。
full GC:
- 当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC;
- 或者,如果有perm gen的话,要在perm gen分配空间但已经没有足够空间时,也要触发一次full GC;
- 或者System.gc()、heap dump带GC,默认也是触发full GC。
并发GC的触发条件就不太一样。
- 以CMS GC为例,它主要是定时去检查old gen的使用量,当使用量超过了触发比例就会启动一次CMS GC,对old gen做并发收集。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 我的生活小站!