哪些对象需要回收的对象?
在进行垃圾回收前,需要知道哪些对象已经没用可以回收了。在objc中,使用ARC自动引用计数,当一个对象ARC为降为0时,认为该对象不在使用。引用技术算法容易出现两个对象循环引用,导致无法正确回收的问题。
在Java中,使用可达性分析技术实现。算法的基本思路是:通过一系列成为”GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路称为引用链。如果一个对象到GC Roots不存在任何可达路径,则认为该对象不可用。
在Java中,GC Roots包括:
- 栈帧中局部变量表所引用对象
- 方法去静态字段引用对象
- 方法区常量引用对象
- 本地方法栈JNI Native方法引用对象
垃圾收集算法
标记-清除算法 (Mark-Sweep)
将垃圾回收分为两个阶段:标记阶段和清除阶段。首先标记出要回收对象;然后,在清除阶段,清除所有未被标记的对象。该算法的问题是存在大量空间碎片,因为回收后的空间是不连续的。在对大对象的内存分配中,容易出现无法找到连续内存而不得不提前进行垃圾收集动作。
复制算法 (Copying)
将可用内存空间分为两快,每次只使用其中一块,垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,然后清除已使用的内存块中所有对象,这样每次都对半个区内存回收。当每次存活对象较少时(对象短命),该算法高效,且没有内存碎片。
Java 新生代垃圾回收器中使用了该算法思想。将内存分成Eden 空间、from 空间、to 空间 3 个部分。其中 from 空间和 to 空间是用于复制的两块大小相同可进行角色互换的空间块。from 和 to 空间称为 Survivor 空间,即幸存者空间,用于存放未被回收的对象。HotSpot默认Eden:from:to = 8: 1: 1。
另外如果to空间没有足够空间保存下一次新生代收集下来的存活对象时,这些对象将通过内存分配担保机制直接进入老年代。
图片来源:http://www.cnblogs.com/zhguang/p/3257367.html
标记-整理算法 (Mark-Compact)
标记过程与“标记-清除”算法一致,让所有存活对象内存一端移动,然后直接清理端边界以外内存。
分代算法 (Generational Collecting)
以 Hot Spot 为例,所有的新建对象都放入新生代,新生代的特点是对象存活时间短,因此,在新生代就选择复制算法。
当一个对象经过几次回收后依然存活,该对象就会被放入称为老年代,认为这些对象是比较持久存在的。如果老年代依然使用复制算法回收老生代,将需要复制大量对象。可以对老年代的回收使用标记-清除或者标记-整理算法。
HotSpot垃圾回收器
Serial收集器
1)仅仅使用单线程进行垃圾回收;独占式的垃圾回收,进行垃圾回收时,Java 应用程序中的线程都需要暂停,等待垃圾回收的完成;
2)使用复制算法 (Copying)Serial Old
1)新生代Serial收集器老年版,是一个串行的、独占式的垃圾回收器;可以作为 CMS 回收器的备用回收器;
2)使用标记-压缩算法ParNew收集器
1)新生代Serial收集器的多线程版本;
2)使用标记-压缩算法;
3)可以使用参数-XX:+UseParNewGC:ParNew + SerialOld
4)是许多Server模式下JVM首选新生代收集器,因为可以与CMS共用Parallel Scavenge收集器
1)新生代吞吐量优先收集器,多线程进行垃圾收集;
2)使用复制算法;
3)使用参数-XX:MaxGCPauseMillis和-XX:GCTimeRatio精确控制吞吐量CMS收集器
1)CMS 收集器关注于系统停顿时间,是JAVA Web服务器端最优老年代垃圾收集器;
2)使用标记-清除算法;
3)使用参数-XX:MaxGCPauseMillis和-XX:GCTimeRatio精确控制吞吐量
4)CMS 工作时,主要步骤有:初始标记、并发标记、重新标记、并发清除和并发重置。其中初始标记和重新标记是独占系统资源的,而并发标记、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上来说,CMS 收集不是独占式的,它可以在应用程序运行过程中进行垃圾回收。
G1 收集器 (Garbage First)
G1收集器的目标是作为一款服务器的垃圾收集器,因此,它在吞吐量和停顿控制上,预期要优于 CMS 收集器。
垃圾收集器触发时机
- 新对象优先分配在Eden区,当Eden没有足够空间为新对象分配内存时,进行Minor GC(新生代GC);
- 长期存活的对象(Survivor区对象超过一定年龄)进入老年代,或者对于大对象直接进入老年代,此时如果老年代空间不够分配时,进行Full GC。
- 空间分配担保 。在进行Minor GC之前,老年代先检查老年代连续空间是否大于新生代所有对象总空间。如果条件成立,则本次Minor GC确保安全;如果否,JVM查看是否启动HandlePromotionFailure设置值是否允许担保失败。如果允许,JVM再检查老年代最大可用连续空间是否大于历次晋升到老年代对象大小(使用历史经验),如果大于,则先尝试进行Minor GC;如果小于,或者不允许HandlePromotionFailure,则直接改为进行一下Full GC;
- 显示调用System.gc(),则进行一次Full GC。