原文链接
回收的是什么?
答:运行程序中没有任何指针援用的对象,这个对象就是须要被回收的垃圾
垃圾回收算法
标记阶段
- 在GC执行垃圾回收之前,首先须要辨别出内存中那些是存活的对象,那些是曾经死亡的对象。只有被标记为已死亡的对象,GC才会在执行垃圾回收时,开释掉其存活所占用的内存空间。此过程被称为垃圾标记阶段
- 当一个对象曾经不再被任何的存活对象持续援用时,就能够宣判为曾经死亡
- 判断对象存活的有两种形式:援用计数算法和可达性剖析算法
援用计数算法(Reference Counting)
- 形容:对每一个对象保留一个整型的援用计数器属性,用于记录对象被援用的的状况
长处:
- 实现简略,垃圾对象便于辨识
- 断定效率高,回收没有提早性
毛病:
- 须要独自的字段存储计数器,减少了存储空间
- 每次赋值都须要从新更新计数器,减少了工夫开销
- 无奈解决循环援用
可达性剖析算法
- 解决了援用计数算法中循环援用的问题,避免内存泄露
- 可达性剖析算法是以根对象汇合(GC Roots)为起始点,依照从上至下的形式搜寻被根对象汇合所连贯的指标对象是否可达
- 应用该算法后,内存中的存活对象都会被根汇合间接或间接的连贯着,搜寻走过的门路称为援用链
- 如果没有任何援用链,则不可达,阐明对象已死亡,能够标记为垃圾对象
GC Roots蕴含以下几类元素:
- 虚拟机栈中援用的对象
- 本地办法(native办法)中援用的对象
- 办法区中常量援用的对象
- 被同步锁
synchronized
持有的对象 Java虚拟机外部的援用
- 根本数据类型对应的Class对象常驻异样对象和零碎类加载器
- 反映Java虚拟机外部状况的JMXBean、 JVMTI中注册的回调、本地代码缓存等
finalization机制
- 提供对象被销毁之前的自定义解决逻辑
- 当垃圾回收器发现没有援用指向一个对象,总会先调用这个对象的finalize()办法
- finalize()办法容许在子类中被重写,用于在对象被回收时进行资源开释。例如:敞开文件,断开数据库连贯
革除阶段
当胜利辨别出内存中存活对象的和死亡对象后,接下来就会执行垃圾回收,开释掉无用对象所占用的内存空间,以便有足够的空间为新对象所调配
标记-革除算法(Mark-Sweep)
执行过程:当堆中的内存空间被耗尽的时候,就会进行整个程序(stop the world),而后进行标记和革除工作。
标记:Collector从援用根节点开始遍历,标记所有被援用的对象,并将这些对象记录在Header中。(找出在程序中被援用的对象,并革除其余的)
革除:Collector从堆内存中从头到尾的进行线性遍历,如果发现某个对象在Header中没有标记为可达对象,则将其回收。(将不可达对象寄存在闲暇列表中,当有新对象进入时,将其地位笼罩)
毛病:
- 效率不高
- 在进行GC时,须要进行整个应用程序,导致用户体验差
- 清理进去的闲暇内存是不间断的,产生内存碎片且须要保护一个闲暇列表
复制算法(Copying)
执行过程:当A空间同时存在可达和不可达对象时,将可达对象复制到B空间并保障空间的间断,后革除A空间的所以对象
长处:
- 没有标记革除过程,实现简略
- 将存在援用关系的对象复制过来后,保障空间的连续性,不会存在碎片
毛病:
- 须要两倍的内存空间
- 对于存活对象的数量远大于垃圾对象数量时,此革除算法效率差。
标记-压缩算法(Mark-Compact)
执行过程:
- 从根节点开始标记存在援用关系的对象
- 将这些对象压缩到内存的一端并按程序排放
- 革除边界外的所以对象
长处:
- 对于“标记-革除算法”,因为应用了压缩性能,当有新对象分配内存时,JVM只须要持有一个内存的起始地址即可
- 对于“复制算法”,缩小了内存空间的占用
毛病:
- 比照“复制算法”,效率低
- 在挪动存在援用关系的对象时,须要调整援用的地址
- 挪动过程中,须要全程暂停用户程序(stop the world)
分代收集算法
依据年老代和老年代的特点应用不同的内存回收算法
年老代:
针对年老代空间小,对象生命周期短,收集频繁和存活率低的特点,应用复制算法能够高效实现内存回收,复制算法使用率不高的问题,通过两个survivor的设计能够无效的缓解。
老年代:
老年代是空间大,对象生命周期长,存活率高和回收不频繁,应用标记-革除算法或标记-革除算法和标记-压缩算法联合实现内存空间的回收。