在分析java内存问题的时候需要查看GC日志,这里概要的总结一下GC日志该如何看
通过设置-XX:+PrintGCDetails虚拟机参数就可以得到GC日志,下面是一段GC日志示例,笔者使用的是JDK8,HotSpot虚拟机
public
class
T {
public static void main(String[] args ) { T t = new T();
System.gc();
}
}
|
[GC (System.gc()) [PSYoungGen: 2662K->576K(38400K)] 2662K->584K(125952K), 0.0015403 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 576K->0K(38400K)] [ParOldGen: 8K->482K(87552K)] 584K->482K(125952K), [Metaspace: 2778K->2778K(1056768K)], 0.0056851 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] Heap PSYoungGen total 38400K, used 333K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000) eden space 33280K, 1% used [0x0000000795580000,0x00000007955d34a8,0x0000000797600000) from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000) to space 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000) ParOldGen total 87552K, used 482K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000) object space 87552K, 0% used [0x0000000740000000,0x00000007400788f8,0x0000000745580000) Metaspace used 2785K, capacity 4486K, committed 4864K, reserved 1056768K class space used 301K, capacity 386K, committed 512K, reserved 1048576K |
[GC (System.gc())
[Full GC (System.gc())表示这次垃圾收集的类型和由谁引发的。
[PSYoungGen: 576K->0K(38400K)]表示使用的是Parallel Scavenge收集器对新生代进行回收,GC前内存使用量为576K->GC后的内存使用量为0K(新生代内存总量为38400K)
[ParOldGen: 8K->482K(87552K)]表示使用
Parallel Scavenge收集器对老年代进行收集,
GC前内存使用量为8K->GC后的内存使用量为482K(老年代内存总量为
87552K
) 说明这次GC后将新生代中的一些对象移到了老年代,并没有直接干掉
[Metaspace: 2778K->2778K(1056768K)]
从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存大小有关。
[Times: user=0.01 sys=0.00, real=0.01 secs] user表示用户态消耗的CPU时间,sys表示内核态消耗的CPU时间,real表示操作从开始到结束所经过的墙钟时间,墙钟时间包括各种非运算所消耗的时间,包括磁盘IO等待时间,线程阻塞等待时间等。而CPU时间不包括这些。
Heap
PSYoungGen total 38400K, used 333K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000)
eden space 33280K, 1% used [0x0000000795580000,0x00000007955d34a8,0x0000000797600000)
from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000)
to space 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000)
ParOldGen total 87552K, used 482K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000)
object space 87552K, 0% used [0x0000000740000000,0x00000007400788f8,0x0000000745580000)
Metaspace used 2785K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 301K, capacity 386K, committed 512K, reserved 1048576K
PSYoungGen total 38400K, used 333K [0x0000000795580000, 0x0000000798000000, 0x00000007c0000000)
eden space 33280K, 1% used [0x0000000795580000,0x00000007955d34a8,0x0000000797600000)
from space 5120K, 0% used [0x0000000797600000,0x0000000797600000,0x0000000797b00000)
to space 5120K, 0% used [0x0000000797b00000,0x0000000797b00000,0x0000000798000000)
ParOldGen total 87552K, used 482K [0x0000000740000000, 0x0000000745580000, 0x0000000795580000)
object space 87552K, 0% used [0x0000000740000000,0x00000007400788f8,0x0000000745580000)
Metaspace used 2785K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 301K, capacity 386K, committed 512K, reserved 1048576K
这段是堆内存的具体信息,从新生代到老年代,再到方法区的Metaspace,都有详细的描述。其中eden表示新生代中的Eden区,from表示Survivor里的from surviver区,to 表示Survivor里的to Survivor区。新生代中采用复制清理的算法,大概的原理就是把这快内存分为两块,一块用于使用,另一块空着,当要回收的时候就把还活着的对象复制到空闲区,然后对使用区进行全部回收。通常90%对象都是朝生夕死的,所以基本按照9:1的比例划分使用区和空闲区。
其中Eden和from survivor就是在使用区,to survivor就是在空闲区。