在堆里面存放着Java世界中几乎全部的对象实例, 垃圾收集器在对堆进行回收前, 第一件事就是判断哪些对象已死(可回收).html
在JDK1.2以前,使用的是引用计数器算法。 在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值就+1,当引用失效的时候,计数器的值就-1,当引用计数器被减为零的时候,标志着这个对象已经没有引用了,能够回收了!java
在主流商用语言(如Java、C#)的主流实现中, 都是经过可达性分析算法来断定对象是否存活的: 经过一系列的称为 GC Roots 的对象做为起点, 而后向下搜索; 搜索所走过的路径称为引用链/Reference Chain, 当一个对象到 GC Roots 没有任何引用链相连时, 即该对象不可达, 也就说明此对象是不可用的, 以下图:虽然E和F相互关联, 但它们到GC Roots是不可达的, 所以也会被断定为可回收的对象。 web
注: 即便在可达性分析算法中不可达的对象, VM也并非立刻对其回收, 由于要真正宣告一个对象死亡, 至少要经历两次标记过程: 第一次是在可达性分析后发现没有与GC Roots相链接的引用链, 第二次是GC对在F-Queue执行队列中的对象进行的小规模标记(对象须要覆盖finalize()方法且没被调用过).算法
垃圾收集策略有分代收集和分区收集。多线程
该算法分为“标记”和“清除”两个阶段: 首先标记出全部须要回收的对象(可达性分析), 在标记完成后统一清理掉全部被标记的对象. 并发
该算法会有两个问题:oracle
因此它通常用于"垃圾不太多的区域,好比老年代"。框架
该算法的核心是将可用内存按容量划分为大小相等的两块, 每次只用其中一块, 当这一块的内存用完, 就将还存活的对象(非垃圾)复制到另一块上面, 而后把已使用过的内存空间一次清理掉.jvm
优势:不用考虑碎片问题,方法简单高效。 缺点:内存浪费严重。线程
现代商用VM的新生代均采用复制算法, 但因为新生代中的98%的对象都是生存周期极短的, 所以并不需彻底按照1∶1的比例划分新生代空间, 而是将新生代划分为一块较大的Eden区和两块较小的Survivor区(HotSpot默认Eden和Survivor的大小比例为8∶1), 每次只用Eden和其中一块Survivor. 当发生MinorGC时, 将Eden和Survivor中还存活着的对象一次性地拷贝到另一块Survivor上, 最后清理掉Eden和刚才用过的Survivor的空间. 当Survivor空间不够用(不足以保存尚存活的对象)时, 须要依赖老年代进行空间分配担保机制, 这部份内存直接进入老年代。
复制算法的空间分配担保: 在执行Minor GC前, VM会首先检查老年代是否有足够的空间存放新生代尚存活对象, 因为新生代使用复制收集算法, 为了提高内存利用率, 只使用了其中一个Survivor做为轮换备份, 所以当出现大量对象在Minor GC后仍然存活的状况时, 就须要老年代进行分配担保, 让Survivor没法容纳的对象直接进入老年代, 但前提是老年代须要有足够的空间容纳这些存活对象. 但存活对象的大小在实际完成GC前是没法明确知道的, 所以Minor GC前, VM会先首先检查老年代连续空间是否大于新生代对象总大小或历次晋升的平均大小, 若是条件成立, 则进行Minor GC, 不然进行Full GC(让老年代腾出更多空间). 然而取历次晋升的对象的平均大小也是有必定风险的, 若是某次Minor GC存活后的对象突增,远远高于平均值的话,依然可能致使担保失败(Handle Promotion Failure, 老年代也没法存放这些对象了), 此时就只好在失败后从新发起一次Full GC(让老年代腾出更多空间).
标记清除算法会产生内存碎片问题, 而复制算法须要有额外的内存担保空间, 因而针对老年代的特色, 又有了标记整理算法. 标记整理算法的标记过程与标记清除算法相同, 但后续步骤再也不对可回收对象直接清理, 而是让全部存活的对象都向一端移动,而后清理掉端边界之外的内存.
在方法区进行垃圾回收通常”性价比”较低, 由于在方法区主要回收两部份内容: 废弃常量和无用的类.
回收废弃常量与回收其余年代中的对象相似, 但要判断一个类是否无用则条件至关苛刻:
分区算法则将整个堆空间划分为连续的不一样小区间, 每一个小区间独立使用, 独立回收. 这样作的好处是能够控制一次回收多少个小区间
在相同条件下, 堆空间越大, 一次GC耗时就越长, 从而产生的停顿也越长. 为了更好地控制GC产生的停顿时间, 将一块大的内存区域分割为多个小块, 根据目标停顿时间, 每次合理地回收若干个小区间(而不是整个堆), 从而减小一次GC所产生的停顿
Serial收集器是Hotspot运行在Client模式下的默认新生代收集器, 它在进行垃圾收集时,会暂停全部的工做进程,用一个线程去完成GC工做
特色:简单高效,适合jvm管理内存不大的状况(十兆到百兆)。
ParNew收集器实际上是Serial的多线程版本,回收策略彻底同样,可是他们又有着不一样。
咱们说了Parnew是多线程gc收集,因此它配合多核心的cpu效果更好,若是是一个cpu,他俩效果就差很少。(可用-XX:ParallelGCThreads参数控制GC线程数)
CMS(Concurrent Mark Sweep)收集器是一款具备划时代意义的收集器, 一款真正意义上的并发收集器, 虽然如今已经有了理论意义上表现更好的G1收集器, 但如今主流互联网企业线上选用的还是CMS(如Taobao),又称多并发低暂停的收集器。
由他的英文组成能够看出,它是基于标记-清除算法实现的。整个过程分4个步骤:
能够看到,初始标记、从新标记须要STW(stop the world 即:挂起用户线程)操做。由于最耗时的操做是并发标记和并发清除。因此整体上咱们认为CMS的GC与用户线程是并发运行的。
**优势:**并发收集、低停顿
缺点:
-XX:CMSInitiatingOccupancyFraction
来设置GC触发百分比(1.6后默认92%),固然咱们还得设置启用该策略-XX:+UseCMSInitiatingOccupancyOnly
-XX:+UseCMSCompactAtFullCollection
参数,它会在GC执行完后接着进行碎片整理,可是又会有个问题,碎片整理不能并发,因此必须单线程去处理,因此若是每次GC完都整理用户线程stop的时间累积会很长,因此XX:CMSFullGCsBeforeCompaction
参数设置隔几回GC进行一次碎片整理(默认为0)。同优秀的CMS垃圾回收器同样,G1也是关注最小时延的垃圾回收器,也一样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。G1最大的特色是引入分区的思路,弱化分代的概念,合理利用垃圾收集各个周期的资源,解决了其余收集器甚至CMS的众多缺陷。
由于每一个区都有E、S、O代,因此在G1中,不须要对整个Eden等代进行回收,而是寻找可回收对象比较多的区,而后进行回收(虽然也须要STW操做,可是花费的时间是不多的),保证高效率。
G1的新生代收集跟ParNew相似,若是存活时间超过某个阈值,就会被转移到S/O区。
年轻代内存由一组不连续的heap区组成, 这种方法使得能够动态调整各代区域的大小
分为如下几个阶段: