1. 判断对象是否已死的方法
- 可达性分析
可作为GC Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
2. 垃圾收集算法:
-
标记-清除(Mark Sweep)算法:
分标记和清除两个阶段,缺点:一是两个阶段效率低,二是产生内存碎片。
-
复制(Copying)算法:
把内存平均分为两块,每次只使用其中一块,当这一块的内存使用完了就将还存活的对象复制到另外一块上面,然后在把使用过的内存空间一次清理掉。
特点:实现简单、运行高效。缺点是:每次只使用一半内存,浪费内存,而且如果存活对象比较多则需要复制很多对象,效率会变低。 -
标记-整理(Mark-Compact)算法:
标记过程与标记-清除(Mark Sweep)算法一样,然后让存活的对象向一端移动,然后直接清理掉边界以外的内存。
- 分代收集算法:
大多数商业虚拟机都采用这种算法,一般把Java堆分为新生代和老年代,根据各个年代的特点采取适当的收集算法,一般新生代采用复制算法,老年代采用标记-清理或标记-整理。
3. 垃圾收集器:
-
Serial收集器
新生代收集器,采用复制算法。
特点:简单高效,Client模式下默认的垃圾收集器。
缺点:单线程进行垃圾收集,收集期间发生STW(Stop The World)暂停所有用户线程。 -
ParNew收集器
Serial收集器的多线程版本。使用复制算法,新生代收集器,除了Serial之外唯一一个可以搭配CMS的收集器。
随着可以使用的CPU数量的增加,它对于GC时系统资源的有效利用还是很有好处的。它默认开启的收集线程数与CPU的数量相同,在CPU非常多的环境下,可以使使用-XX:ParallelGCThreads参数来限制来限制垃圾收集的线程数。
-
Parallel Scavenge收集器
是一个新生代收集器,使用复制算法,并行的多线程收集器。关注吞吐量,目的为达到一个可控的吞吐量,可通过参数-XX:MaxGCPauseMillis控制最大停顿时间,单位毫秒,参数-XX:GCTimeRatio控制垃圾收集时间占总时间占比,值为大于0小于100的整数。参数-XX:UseAdaptiveSizePolicy开启后可以自动调节新生代大小、Eden与Survivor区的比例以及晋升老年代对象大小。
-
Serial Old收集器
Serial收集器的老年代版本,单线程收集器,标记-整理算法进行收集,主要用于Client模式下。
-
Parallel Old收集器
Parallel Scavenge收集器的老年代版本。多线程,使用标记-整理算法。
- CMS收集器
年老代收集器,一种以获取最短回收停顿时间为目标的收集器。基于“标记-清除”算法。收集过程包含如下四个步骤:
1) 初始标记
2) 并发标记
3) 重新标记
4) 并发清除
其中1)、3)需要STW,但耗时很少,另外两个耗时的过程收集线程可以和用户线程一起工作。所以整体来说CMS收集器的内存回收过程是与用户线程一起并发执行的。特点:并发收集(整体来说收集线程和用户线程一起工作)、低停顿。
缺点:- 和用户线程一起并发工作时会占用一些CPU资源.
- 无法处理浮动垃圾,浮动垃圾是指在和用户线程一起工作时用户线程产生的垃圾无法回收,考虑到会和用户线程一起工作所以会预留一部分(可配百分比)内存给用户线程,在收集期间如果这部分预留的内存不满足用户线程就会导致“Concurrent Mode Failure”,从而使JVM启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集。
- 因为基于“标记-清除”算法所以会产生内存碎片。
4 GC 日志
- -XX:+UseSerialGC
GC收集器:
新生代:Serial
老年代:SerialOld
新生代回收日志:
[GC (Allocation Failure): [DefNew: 1664K->0K(1856K), 0.0001900 secs] 2105K->441K(5952K), 0.0002271 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
老年代回收日志:
[Full GC (Allocation Failure) [Tenured: 4095K->4095K(4096K), 0.0096600 secs] 5951K->5544K(5952K), [Metaspace: 3373K->3373K(1056768K)], 0.0096927 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Heap
def new generation total 1856K, used 899K [0x00000007bfa00000, 0x00000007bfc00000, 0x00000007bfc00000)
eden space 1664K, 54% used [0x00000007bfa00000, 0x00000007bfae0bf8, 0x00000007bfba0000)
from space 192K, 0% used [0x00000007bfba0000, 0x00000007bfba0048, 0x00000007bfbd0000)
to space 192K, 0% used [0x00000007bfbd0000, 0x00000007bfbd0000, 0x00000007bfc00000)
tenured generation total 4096K, used 441K [0x00000007bfc00000, 0x00000007c0000000, 0x00000007c0000000)
the space 4096K, 10% used [0x00000007bfc00000, 0x00000007bfc6e740, 0x00000007bfc6e800, 0x00000007c0000000)
Metaspace used 3378K, capacity 4564K, committed 4864K, reserved 1056768K
class space used 370K, capacity 388K, committed 512K, reserved 1048576K
- -XX:+UseParallelGC
GC收集器:
新生代:Parallel Scavenge
老年代:SerialOld
新生代回收日志:
[GC (Allocation Failure) [PSYoungGen: 1056K->64K(1536K)] 1553K->561K(5632K), 0.0005464 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
老年代回收日志:
[Full GC (Ergonomics) [PSYoungGen: 1024K->981K(1536K)] [ParOldGen: 3870K->3870K(4096K)] 4894K->4852K(5632K), [Metaspace: 3371K->3371K(1056768K)], 0.0131815 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 1536K, used 309K [0x00000007bfe00000, 0x00000007c0000000, 0x00000007c0000000)
eden space 1024K, 24% used [0x00000007bfe00000,0x00000007bfe3d7d0,0x00000007bff00000)
from space 512K, 12% used [0x00000007bff00000,0x00000007bff10000,0x00000007bff80000)
to space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
ParOldGen total 4096K, used 497K [0x00000007bfa00000, 0x00000007bfe00000, 0x00000007bfe00000)
object space 4096K, 12% used [0x00000007bfa00000,0x00000007bfa7c530,0x00000007bfe00000)
Metaspace used 3378K, capacity 4564K, committed 4864K, reserved 1056768K
class space used 370K, capacity 388K, committed 512K, reserved 1048576K
- -XX:+UseConcMarkSweepGC
GC收集器:
新生代:ParNew
老年代:CMS
新生代回收日志:
[GC (Allocation Failure) 11.273: [ParNew: 1090K->2K(1216K), 0.0014268 secs] 1546K->458K(6016K), 0.0015251 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
老年代回收日志:
[Full GC (Allocation Failure) [CMS: 4800K->4800K(4800K), 0.0179098 secs] 6015K->6015K(6016K), [Metaspace: 3372K->3372K(1056768K)], 0.0179558 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
Heap
par new generation total 1216K, used 35K [0x00000007bfa00000, 0x00000007bfb50000, 0x00000007bfb50000)
eden space 1088K, 3% used [0x00000007bfa00000, 0x00000007bfa08588, 0x00000007bfb10000)
from space 128K, 1% used [0x00000007bfb30000, 0x00000007bfb30810, 0x00000007bfb50000)
to space 128K, 0% used [0x00000007bfb10000, 0x00000007bfb10000, 0x00000007bfb30000)
concurrent mark-sweep generation total 4800K, used 456K [0x00000007bfb50000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3376K, capacity 4564K, committed 4864K, reserved 1056768K
class space used 370K, capacity 388K, committed 512K, reserved 1048576K
- -XX:+UseParNewGC(java8下提示过时)
GC收集器:
新生代:ParNew
老年代:SerialOld
8.506: [GC (Allocation Failure) 8.506: [ParNew: 1666K->2K(1856K), 0.0002354 secs] 2214K->550K(5952K), 0.0002726 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 1856K, used 1400K [0x00000007bfa00000, 0x00000007bfc00000, 0x00000007bfc00000)
eden space 1664K, 84% used [0x00000007bfa00000, 0x00000007bfb5db38, 0x00000007bfba0000)
from space 192K, 1% used [0x00000007bfba0000, 0x00000007bfba0810, 0x00000007bfbd0000)
to space 192K, 0% used [0x00000007bfbd0000, 0x00000007bfbd0000, 0x00000007bfc00000)
tenured generation total 4096K, used 548K [0x00000007bfc00000, 0x00000007c0000000, 0x00000007c0000000)
the space 4096K, 13% used [0x00000007bfc00000, 0x00000007bfc89268, 0x00000007bfc89400, 0x00000007c0000000)
Metaspace used 3378K, capacity 4564K, committed 4864K, reserved 1056768K
class space used 370K, capacity 388K, committed 512K, reserved 1048576K
- 解释
GC (Allocation Failure):表示新生代发生的垃圾回收日志。
Full GC (Allocation Failure):表示老年代和元空间发生的垃圾回收日志。[gc收集器名称:gc前该内存空间使用量-> gc后该内存空间使用量(该内存空间总大小)]
5. 内存分配与回收策略
-
对象优先在Eden分配
大多数情况下,对象在新生代Eden中分配。当Eden没有足够空间进行分配时,虚拟机将发起一次Minor GC。默认 Eden: From Survivor: To Survivor = 8:1:1
-
大对象直接进入老年代
大对象是指需要大量连续空间的Java对象,最典型的就是很长的字符串以及数组。参数-XX:PretenureSizeThreshold可以指定这个阈值。
-
长期存活的对象将进入老年代
虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经历过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设置为1。对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1,当它的年龄达到一定程度(默认15),将会被晋升到老年代。对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。
-
动态对象年龄判定
为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。
- 空间分配担保
在发生Minor GC之前,虚拟机会先检查老年代最大可用都连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那么这时也要改为进行一次Full GC。
下面解释一下“冒险”是冒了一次什么风险,前面提到过,新生代使用复制收集算法,但为了内存利用率,只使用其中一个Survivor空间来作为轮换备份,因此当出现大量对象在Minor GC后仍然存活的情况,就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。