一.如何判断对象为垃圾对象
- 引用计数法
在对象中添加一个引用计算器,当有地方引用这个对象的时候,引用计数器就+1,任何时刻计数器为0的对象就是不可能再被使用的对象。但会出现相互循环引用的问题。
- 可达性分析
从GC Roots为根节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用。
可作为GC Roots的对象:
a.虚拟机栈(栈帧中的局部变量表)中的引用的对象
b.方法区中的类静态属性引用的对象
c.方法区中的常量所引用的对象
d.本地方法栈中J引用的对象
二.如何回收
1.回收策略
标记-清除算法
存在问题:效率不高,会导致内存不连续,大对象无法分配
复制算法
存在问题:内存区域浪费,只有一半进行分配。(故新生代8:1)
标记-整理算法
针对老年代,回收垃圾较少的算法,
分代收集算法
根据内存的分代选择不同的回收算法。
新生代:复制算法
老年代:标志-整理算法。
2.垃圾回收器
Serial/Serial Old收集器
最基本,发展最悠久的收集器。其是一个单线程的垃圾收集器。
Serial针对新生代进行回收,Serial Old针对老年代进行回收。
采用的回收算法,和回收过程如下:ParNew收集器
它其实就是Serial收集器的多线程版本,针对新生代进行回收。
Parallel Scavenge/Parallel Old收集器
Parallel Scavenge与ParNew收集器同样是多线程收集器,针对新生代,采用复制算法。不同点是Parallel关注点是吞吐量,即CPU用于运行用户代码的时间与CPU消耗的总时间的比值,其目标是达到一个可控制的吞吐量。故应用于不需要太多交互的场景。
两个参数控制吞吐量:- -XX:MaxGCPauseMillis:垃圾收集器的最大停顿时间
这个参数不是越小越好,因为GC停顿时间是已牺牲新生代空间换取的,设置的越小,新生代空间就越小,相应的GC就更频繁了,吞吐量就下来了。 - -xx:GCTimeRatio:吞吐量大小(0~100)
- -XX:MaxGCPauseMillis:垃圾收集器的最大停顿时间
CMS收集器(Concurrent Mark Sweep)
其是一种以最小回收时间停顿为目标的并发回收器,应用于要求服务器响应较快,用户体验较好的场景。采用标志-清除算法。
但其要占用大量的CPU资源,无法处理浮动垃圾,容易出现Concurrent Mode Failure。这是因为在并发清理期间,用户线程还在运行,所以会有浮动垃圾,同时也要预留部分内存给用户使用,这部分内存如果过小,就会出现错误内存不足。另外还会产生空间碎片。
a.初始标记:只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。
b.并发标记:接着往下找关联对象,进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。
c.重新标记:为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。
d.并发清除:清除GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看CMS收集器的内存回收和用户线程是一起并发地执行。
G1收集器(相对最好)
第一时间处理垃圾最多的区块。
多应第一时间处理垃圾最多的区块。用于服务端多核CPU、JVM内存占用较大的应用。G1不划分新生代和老年代(但保留概念)。它把堆内存划分为大小固定的多个独立区域Region,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域。
区域划分和优先级区域回收机制,确保G1收集器可以在有限时间获得最高的垃圾收集效率。
基于标记-整理算法,不产生内存碎片。
可预测停顿:可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间。
三.何时回收
首先要知道GC分为minor GC 和 Full GC(major GC)。
Minor GC:是从年轻代回收内存
当jvm无法为一个新的对象分配空间时会触发Minor GC,比如当Eden区满了。
执行Minor GC操作时不会影响到永久代,从永久带到年轻代的引用被当成GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉(永久代用来存放java的类信息)。Full GC:是清理整个堆空间,即年轻代和老年代。
对于Minor GC的触发条件:大多数情况下,直接在eden区中进行分配。如果eden区域没有足够的空间,
那么就会发起一次Minor GC;对于FullGC的触发条件:如果老年代没有足够的空间,那么就会进行一次FullGC.在发生MinorGC之前,虚拟机会先检查老年代最大可利用的连续空间是否大于新生代所有对象的总空间。
如果大于则进行Minor GC,如果小于则要看是否允许内存分配担保:不允许担保,直接进行Full GC,
如果允许,就会继续检查老年代最大可利用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试minor gc (如果尝试失败也会触发Full GC),如果小于则进行Full GC。