基于GC日志逐步理解Java虚拟机GC过程

来源:互联网 发布:网站程序员工资 编辑:程序博客网 时间:2024/06/05 20:41

基于GC日志理解JVM GC过程

87384.477: [GC [PSYoungGen: 259488K->448K(259584K)] 398515K->139475K(448000K), 0.0061107 sec]

以上,为一条普通的GC日志。将从左到右的顺序解释各参数的含义。

  • 87384.477: 代表了GC发生的时间,含义是从JVM启动以来经过的秒数。

  • GC: GC/FULL GC,FULL代表新生代和老年代都进行了垃圾收集。

  • PSYoungGen: 发生GC的区域,这里显示的区域名跟使用的是密切相关的,如PSYoungGen为Parallel Scavenge收集器配套的新生代。

  • 259488K->448K(259584K): gc前该内存区used容量->gc后该内存区used容量(该内存区总容量)

  • 398515K->139475K(448000K):gc前java堆used容量 -> gc后java堆used容量(java堆总容量)

Java堆是垃圾收集管理的主要区域,现在收集器基本都采用分代收集算法,所以Java堆中可以细分为新生代和老年代,即

    java堆used容量 = 新生代used容量 + 老年代used容量

例如,在一条普通的新生代gc日志中,如果新生代gc前后used相差大,但java堆gc前后used基本没什么变化,就意味着对象被移入了老年代。一般情况下,对象优先在新生代分配,根据某些策略(如对象所需内存大或对象长期存活),进入老年代。新生代和老年代的区分,主要是收集器收集算法不同,简而言之,新生代中对象存活率低,回收速度快,老年代中对象存活率高,回收速度慢。回收速度与选择的收集算法相关,收集算法衡量的是速度与内存的取舍。收集算法详情。

另外,HotSpot选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区,可以像管理Java堆一样管理这部分内存。所以也会出现永久代的gc日志,区域名为Perm

Java堆

Java堆是JVM所管理的内存中最大的一块,Java堆是被所有线程共享的一块内存区域。所有的对象实例以及数组都在堆上分配。如果在堆中没有内存完成实例的分配,并且堆也无法再扩展(扩展通过-Xmx,-Xms控制)时,就会抛出OOM异常

方法区

方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

Java堆存对象实例相关,方法区存类相关。

Stop-The-World

GC进行时, 必须停顿所有Java执行线程。

分代收集算法

基础的垃圾收集算法有三种,标记-清除,复制,标记-整理。后两种是对第一种的不足进行改进而得到的。

  • 标记-清除。标记出所有需要回收的对象,完成后统一回收。不足之一在于效率,标记和清除的效率都不高,之二在于内存碎片,标记清除后会产生大量的不连续的内存碎片,以后需要分配较大对象时,就可能无法找到足够的连续内存而不得不触发一次gc。
  • 复制。将可用内存划分为两块,使用其中的一块,这一块用完后,将存活的对象复制到另一块上面,再把已使用的内存一次清理。适用于对象存活率低的情况,即复制少,清理快。复制算法适用于新生代。
  • 标记-整理。先标记出需要回收的对象,再让所有存活的对象向一端移动,然后直接清理掉端以外的内存。适用于老年代。

当前商业虚拟机的垃圾收集都采用“分代收集”算法,根据对象存活周期的不同将内存划分为几块,一般是划分为新生代和老年代。新生代采用复制算法,将内存都是分为一块较大的Eden空间和较小的Survivor,当survivor不够用时,这些对象将直接通过分配担保机制进入老年代。老年代使用标记-整理算法。

1.8新特性:永久代Perm-> 元空间MetaSpace

上面提到的永久代,JDK8 HotSpot JVM 将移除永久区,使用本地内存来存储类元数据信息并称之为:元空间。默认情况下,类元数据只受可用的本地内存限制(容量取决于是32位或是64位操作系统的可用虚拟内存大小)。新参数(MaxMetaspaceSize)用于限制本地内存分配给类元数据的大小。如果没有指定这个参数,元空间会在运行时根据需要动态调整。

以上知识点大多来源于深入理解Java虚拟机(周志明)这本书第2,3章。