垃圾回收常用算法
1、引用计数
引用计数算法很简单,对于一个对象A,只要有一个对象引用了A则的引用计数加1,当引用失效则引用计数减1,只要对象A的引用计数为0,则对象A即可被列为回收的对象。引用计数的实现也很简单,只要为对象配置一个整形的计数器即可,但引用计数有一个严重的问题就是无法处理循环引用的情况,因此在java的垃圾回收器中没有用这种算法。
2、标记清除
标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。在标记阶段首先通过根节点,标记所有从根节点开始的较大对象,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。该算法最大的问题是存在大量的空间碎片,因为回收后的空间是不连续的。在对象的堆空间分配过程中,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续的空间
3、标记压缩
标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。也首先需要从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间,这种方法避免了碎片的产生。
4、复制算法
将现有的内存空间分为两快,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大。因此在真正需要垃圾回收的时刻,复制算法的效率是很高的。又由于对象在垃圾回收过程中统一被复制到新的内存空间中,因此,可确保回收后的内存空间是没有碎片的。该算法的缺点是将系统内存折半。
Java 的新生代串行垃圾回收器中使用了复制算法的思想。新生代分为 eden 空间、from 空间、to 空间 3 个部分。其中 from 空间和 to 空间可以视为用于复制的两块大小相同、地位相等,且可进行角色互换的空间块。from 和 to 空间也称为 survivor 空间,即幸存者空间,用于存放未被回收的对象。
在垃圾回收时,eden 空间中的存活对象会被复制到未使用的 survivor 空间中 (假设是 to),正在使用的 survivor 空间 (假设是 from) 中的年轻对象也会被复制到 to 空间中 (大对象,或者老年对象会直接进入老年带,如果 to 空间已满,则对象也会直接进入老年代)。此时,eden 空间和 from 空间中的剩余对象就是垃圾对象,可以直接清空,to 空间则存放此次回收后的存活对象。这种改进的复制算法既保证了空间的连续性,又避免了大量的内存空间浪费。
垃圾回收器种类的分类点和性能考量点
1、种类分类点
线程数,单线程是串行垃圾回收,多线程是并行垃圾回收
是否独占cpu,独占cpu时应用线程全部暂停执行(即非并发方式),非独占cpu时应用线程和垃圾线程交替执行(即并发方式)
碎片的处理方式,压缩式垃圾回收和非压缩式垃圾回收
工作内存空间,有新生代垃圾回收和老年代垃圾回收
2、性能考量点
吞吐量,指在应用程序的生命周期内,应用程序所花费的时间和系统总运行时间的比值。系统总运行时间=应用程序耗时+GC 耗时
stop-the-world时长,使用并发的回收器时,由于垃圾回收器和应用程序交替运行,程序的停顿时间会变短,但是,由于其效率很可能不如独占垃圾回收器,故系统的吞吐量可能会较低。
常见垃圾回收器说明(HotSpot)
1、-XX:+UseSerialGC,client模式下默认的垃圾回收器
新生代:串行复制算法,独占cpu方式
老年代:串行标记压缩算法,独占cpu方式
2、-XX:+UseParNewGC
新生代:并行复制算法,独占cpu方式
老年代:串行标记压缩算法,独占cpu方式
3、-XX:+UseParallelOldGC
新生代:并行复制算法,独占cpu方式
老年代:并行标记压缩算法,独占cpu方式
4、-XX:+UseConcMarkSweepGC
新生代:并行复制算法,独占cpu方式
老年代:并行标记清除算法,非独占cpu方式
常见垃圾器相关配置(HotSpot)
1、串行垃圾收集器
-XX:+SuivivorRatio:设置 eden 区大小和 survivor 区大小的比例。
-XX:+PretenureSizeThreshold:设置大对象直接进入老年代的阈值。当对象的大小超过这个值时,将直接在老年代分配。
-XX:MaxTenuringThreshold:设置对象进入老年代的年龄的最大值。每一次 Minor GC 后,对象年龄就加 1。2、并行垃圾收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数。通常情况下可以和 CPU 数量相等。但在 CPU 数量比较多的情况下,设置相对较小的数值也是合理的。
-XX:MaxGCPauseMills:设置最大垃圾收集停顿时间。它的值是一个大于 0 的整数。收集器在工作时,会调整 Java 堆大小或者其他一些参数,尽可能地把停顿时间控制在 MaxGCPauseMills 以内。
-XX:GCTimeRatio:设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。
-XX:+UseAdaptiveSizePolicy:打开自适应 GC 策略。在这种模式下,新生代的大小,eden 和 survivor 的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。
3、CMS垃圾收集器
-XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。
-XX:+ParallelCMSThreads: 设定 CMS 的线程数量。
-XX:+CMSInitiatingOccupancyFraction:设置 CMS 收集器在老年代空间被使用多少后触发,默认为 68%。
-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩(可能影响性能,但能减少碎片)
-XX:+UseFullGCsBeforeCompaction:设定进行多少次 CMS 垃圾回收后,进行一次内存压缩。
-XX:+CMSParallelRemarkEndable:启用并行重标记。
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收,即对方法区使用CMS进行垃圾回
-XX:CMSInitatingPermOccupancyFraction:当永久区占用率达到这一百分比后,启动 CMS 回收 (前提是
-XX:+CMSClassUnloadingEnabled 激活了)。
-XX:UseCMSInitatingOccupancyOnly:表示只在到达阈值的时候,才进行 CMS 回收。
-XX:+CMSIncrementalMode:使用增量模式,比较适合单 CPU。
辅助配置参数
1、GC日志相关
-XX:+PrintGCDetails,输出GC日志详情
-XX:+PrintGCTimeStamps,输出时间戳信息
-XX:+PrintGCDateStamps ,输出标准格式时间
-XX:+PrintTenuringDistribution,每次miniGC后存活周期的阀值
-XX:+PrintGCApplicationStoppedTime,打印垃圾回收期间程序暂停的时间
-XX:-OmitStackTraceInFastThrow,打印jdk内部异常的信息,默认server模式下开启
-Xloggc:filename -输出gc信息到日志文件
2、内存相关
-Xms 初始堆大小,默认物理内存的1/64(<1GB)
-Xmx 最大堆大小, 默认物理内存的1/4(<1GB)
-Xmn 年轻代大小(1.4or lator),此处的大小是(eden+ 2 survivor space)增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:PermSize 设置持久代(perm gen)初始值,默认物理内存的1/64
-XX:MaxPermSize 设置持久代最大值,默认物理内存的1/4
-Xss 每个线程栈的大小,JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K
-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值,-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值,设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:+UseFastAccessorMethods 原始类型的快速优化
-XX:+DisableExplicitGC 禁用system.gc()方法