对Java GC的简单理解

灵魂拷问

  1. 为什么需要?
  2. 对什么东西?
  3. 在什么时候?
  4. 做什么事情?

一、为什么需要

应用程序对资源操作,通常简单分为以下几个步骤:

  1. 为对应的资源分配内存
  2. 初始化内存
  3. 使用资源
  4. 清理资源
  5. 释放内存

手动管理的话,容易造成以下典型问题:

  1. 程序员忘记释放内存
  2. 应用程序访问已经释放的内存

从而导致了内存泄漏、数据内容乱码等

总结:无法自动化的内存管理方式极易产生bug,影响系统稳定性,源源不断的类似bug影响开发人员编程积极性

二、对什么东西

垃圾收集主要是针对方法区不使用的对象进行的。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。

JVM是一个内存中的虚拟机,目前有三大Java虚拟机:HotSpot,oracle JRockit,IBM J9

  • Class Loader:根据特定格式,加载Class文件到内存
  • Runtime Data Area:JVM内存空间结构模型(也叫运行时数据区域)
  • Execution Engine:对命令进行解析
  • Native Interface:融合不同开发语言的原生库为Java所用,在执行时加载Native Libraries

2.1 运行时数据区域(Runtime Data Area)

JDK 1.6 运行时数据区域

JVM内存模型-JDK1.8

  • 线程私有
  1. 程序计数器(字节码指令 no OOM)
  2. 虚拟机栈(Java方法 SOF&OOM)
  3. 本地方法栈(native方法 SOF&OOM)
  • 线程共享
  1. MetaSpace(类加载信息 OOM)
  2. Java堆(数组和类对象 OOM,常量池)

JDK1.7中把运行时常量池放到了堆中。
JDK1.8及之后,由于每次Full GC之后永久代大小都会改变,经常OOM,所以把方法区(一种JVM规范,永久代和元空间都是它的实现方式,之前HotSpot虚拟机把这个作为永久代来进行垃圾回收)去掉了,移至元空间,而其原本的数据也分成两部分,元空间存储类的元信息,静态变量和常量池等都放到了堆中。

Java堆

  • 所有对象都在这里分配内存,是垃圾收集的主要区域(GC 堆)

  • 现代的垃圾收集器基本都是采用分代收集算法,其主要的思想是针对不同类型的对象采取不同的垃圾回收算法。可以将堆分成两块:

    • 新生代(Young Generation):一个Eden,两个Survivor区,HopSpot默认大小8:1:1
    • 老年代(Old Generation)


      HotSpot Heap Structure

    永久代(HotSpot的方法区)在JDK1.8之后移除,Java堆里只有新生代和老年代了。

  • 堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。

2.2 怎么判断一个对象是否可被回收

2.2.1 引用计数算法

为对象添加一个引用计数器
当对象增加一个引用时计数器加 1,引用失效时计数器减 1
引用计数为 0 的对象可被回收

优点:执行效率高,程序执行受影响较小
缺点:无法检测出循环引用的情况,导致内存泄漏,因此Java虚拟机不实用引用技术算法

2.2.2 可达性分析算法

GC Roots为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收

可以作为GC Roots的对象

  • 虚拟机栈中引用的对象(栈帧中的局部变量表)
  • 方法区中的常量引用的对象
  • 方法区中的类静态属性引用的对象
  • 本地方法栈中JNI(native方法)的引用对象

可被回收

未到达的对象并非是“非死不可”的,若要宣判一个对象死亡,至少需要经历两次标记阶段

st=>start: 第一次标记&第一次筛选
e=>end: 被回收
callFinallize=>condition: 是否有必要执行该对象的finallize方法
putFQueue=>operation: 放入F-Queue队列,虚拟机自动建立低优先级的Finalizer线程
selfSave=>condition: 是否在finalize中拯救了自己(只能拯救一次)
removeFromQueue=>operation: 从即将回收的集合中移除,等待下次回收

st->callFinallize
callFinallize(yes)->putFQueue->selfSave
selfSave(no)->e
selfSave(yes)->removeFromQueue
callFinallize(no)->e
  1. 如果对象可达性分析之后GC Roots搜索不到,会被第一次标记并进行一次筛选(是否有必要执行该对象的finallize方法
  • 未覆盖或者已执行的 --> 回收
  • 已覆盖未执行的 --> 放入一个叫F-Queue的队列中,之后会有虚拟机自动建立的、低优先级的Finalizer线程去执行,而虚拟机不必要等待该线程执行结束,即虚拟机只负责建立线程,其他的让此线程区处理
  1. 对F-Queue中的对象进行第二次标记
  • 如果对象在finalize方法中拯救了自己(只能拯救一次),即关联上了GC Roots引用链,则第二次标记的时候会将该对象从“即将回收”的集合中移除
  • 如果对象还是没有拯救自己,则会被回收
package com.dyl.myspringboot;

/**
 * @author dyl
 * @date 2019/08/06
 */
public class FinalizeTest {

    private static FinalizeTest finalize;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("method finalize is running");
        finalize = this;
    }
    
    public static void main(String[] args) throws Exception {
        finalize = new FinalizeTest();

        // 第一次执行,finalize方法会自救
        finalize = null;
        System.gc();

        Thread.sleep(500);
        if (finalize != null) {
            System.out.println("I'm alive");
        } else {
            System.out.println("I'm dead");
        }

        // 第二次执行,finalize方法已经执行过
        finalize = null;
        System.gc();

        Thread.sleep(500);
        if (finalize != null) {
            System.out.println("I'm alive");
        } else {
            System.out.println("I'm dead");
        }
    }
    
}

2.3 引用类型

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关
Java 提供了四种强度不同的引用类型

2.3.1 强引用

被强引用关联的对象不会被回收
使用 new一个新对象的方式来创建强引用

Object obj = new Object();

2.3.2 软引用

被软引用关联的对象只有在内存不够的情况下才会被回收
使用 SoftReference 类来创建软引用

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj); 
obj = null; // 使对象只被软引用关联

2.3.3. 弱引用

被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前
使用 WeakReference 类来创建弱引用

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;

2.3.4 虚引用

又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象
为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知
使用PhantomReference 来创建虚引用

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, null);
obj = null;

强引用 > 软引用 > 弱引用 > 虚引用

引用类型 被垃圾回收时间 用途 生存时间
强引用 从来不会 对象的一般状态 JVM停止运行时终止
软引用 在内存不足时 对象缓存 内存不足时终止
弱引用 在垃圾回收时 对象缓存 GC运行后终止
虚引用 Unknown 标记、哨兵 Unknown

总结:引用计数为0的对象;从GC Roots开始搜索不到的对象,而且经过一次标记、清理,仍然没有复活的对象

三、在什么时候

3.1 内存分配策略

  1. 对象优先在 Eden 分配

大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC

  1. 大对象直接进入老年代

大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。 经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
-XX:PretenureSizeThreshold大于此值的对象直接在老年代分配,避免在 Eden 和 Survivor 之间的大量内存复制

  1. 长期存活的对象进入老年代

为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁, 增加到一定年龄则移动到老年代中。
-XX:MaxTenuringThreshold用来定义年龄的阈值

  1. 动态对象年龄判定

虚拟机并不是永远要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在 Survivor 中相同年龄 所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

3.2 内存回收策略

  • Minor GC

回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快

  • Full GC

回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC慢很多

3.3 触发条件

3.3.1 Minor GC

  • Eden空间满时,就将触发一次 Minor GC

3.3.2 Full GC

  1. 调用System.gc()

只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存

  1. 老年代空间不足

老年代空间不足的常见场景为 大对象直接进入老年代、长期存活的对象进入老年代等
为了避免,应当尽量不要创建过大的对象以及数组。除此之外,可以通过-Xmn虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过-XX:MaxTenuringThreshold调大对象进入老年代的年龄,让对象在新生代多存活一段时间

  1. JDK 1.7 及以前的永久代空间不足

在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。
当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。
可采用增大永久代空间或转为使用 CMS GC的方法来避免

  1. CMS GC时出现promotion failedconcurrent mode failure

预留的内存不够存放浮动垃圾

总结:新生代的Eden区空间满时触发Minor GC;系统在不可测的时间调用System.gc()方法、老年代空间不足、jdk1.7及以前的永久代空间不足等情况会触发Full GC

四、做什么事情

4.1 垃圾收集算法

4.1.1 标记-清除算法(Mark and Sweep)

标记:从根集合GC Roots进行扫描,对存活的对象进行标记
清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存

缺点

  • 标记和清除过程效率都不高
  • 会产生大量不连续的内存碎片,导致无法给大对象分配内存

4.1.2 标记-整理算法(Compacting)

标记:从根集合进行扫描,对存活的对象进行标记
清除:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收

优点

  • 避免内存的不连续行
  • 不用设置两块内存互换
  • 适用于存活率高的场景

缺点

  • 需要移动大量对象,处理效率低

4.1.3 复制算法(Copying)

分为对象面和空闲面
对象在对象面上创建
存活的对象被从对象面复制到空闲面
将对象面所有对象内存清除

优点:

  • 解决碎片化问题
  • 顺序分配内存,简单高效
  • 适用于对象存活率低的场景

4.1.4 分代收集算法(Generational Collector)

垃圾回收算法的组合拳
按照对象生命周期的不同划分区域以采取不同的垃圾回收算法
目的:提高JVM的回收效率

  • 新生代:复制算法:HotSpot默认值 Eden : Survivor = 8 : 1
  • 老年代:标记-清除 or 标记-整理

4.2 垃圾回收器

HotSpot的7个垃圾收集器

以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用

  • 单线程与多线程

单线程指的是垃圾收集器只使用一个线程,而多线程使用多个线程

  • 串行与并行

串行指的是垃圾收集器与用户程序交替执行,这意味着在执行垃圾收集的时候需要停顿用户程序;并行指的是垃圾收集器和用户程序同时执行。除了 CMS 和 G1 之外,其它垃圾收集器都是以串行的方式执行

4.2.1. Serial 收集器

  • 它是单线程的收集器,只会使用一个线程进行垃圾收集工作。
  • 它的优点是简单高效,在单个 CPU 环境下,由于没有线程交互的开销,因此拥有最高的单线程收集效率。
  • 它是 Client 场景下的默认新生代收集器,因为在该场景下内存一般来说不会很大。它收集一两百兆垃圾的停顿时间可以控制在一百多毫秒以内,只要不是太频繁,这点停顿时间是可以接受的

4.2.2. ParNew 收集器

  • 它是 Serial 收集器的多线程版本。
  • 它是 Server 场景下默认的新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合使用

4.2.3 Parallel Scavenge 收集器

  • 与 ParNew 一样是多线程收集器。
  • 其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是达到一个可控制的吞吐量,因此它被称为吞吐量优先收集器。这里的吞吐量指 CPU 用于运行用户程序的时间占总时间的比值。
  • 缩短停顿时间是以牺牲吞吐量和新生代空间来换取的:新生代空间变小,垃圾回收变得频繁,导致吞吐量下降。

4.2.4 Serial Old 收集器

  • 是 Serial 收集器的老年代版本,也是给 Client 场景下的虚拟机使用。如果用在 Server 场景下,它有两大用途
    • 在 JDK 1.5 以及之前版本(Parallel Old 诞生以前)中与 Parallel Scavenge 收集器搭配使用
    • 作为 CMS 收集器的后备预案,在并发收集发生 Concurrent Mode Failure 时使用

4.2.5 Parallel Old 收集器

  • 是 Parallel Scavenge 收集器的老年代版本。
  • 在注重吞吐量以及 CPU 资源敏感的场合,都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器

4.2.6 CMS 收集器

CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法
分为以下四个流程:

  • 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿
  • 并发标记:进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿
  • 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿
  • 并发清除:不需要停顿

在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。
具有以下缺点:

  • 吞吐量低:低停顿时间是以牺牲吞吐量为代价的,导致 CPU 利用率不够高
  • 无法处理浮动垃圾,可能出现 Concurrent Mode Failure。浮动垃圾是指并发清除阶段由于用户线程继续运行而产生的垃圾,这部分垃圾只能到下一次 GC 时才能进行回收。由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS
  • 标记 - 清除算法导致的空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC

4.2.7 G1 收集器

G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。
HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器

堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收

总结:触发GC时,JVM根据分代收集算法,使用相应的垃圾收集器来回收垃圾


个人观点

Java GC,打个不太形象的比喻,就好比拿扫把扫地:

哪些是垃圾(可达性分析,把有用的标记)
哪里的垃圾(Runtime Data Area是房间,Java堆是地板)
什么时候扫?(GC触发条件)
用什么扫?(垃圾回收器)
怎么扫?(垃圾收集算法)


JVM性能调优常用参数

参数名称 含义 默认值 说明
-Xms 初始堆大小 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制
-Xmx 最大堆大小 物理内存的1/4(<1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制
-Xmn 新生代大小(1.4or lator) 注意:此处的大小是(eden+ 2 survivor space)。与jmap -heap中显示的New gen是不同的。整个堆大小=新生代大小+老年代大小+永久代大小。增大新生代后,将会减小老年代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize 设置新生代大小(for 1.3/1.4)
-XX:MaxNewSize 新生代最大值(for 1.3/1.4)
-XX:PermSize 设置永久代(perm gen)初始值 物理内存的1/64 jdk1.8之后不生效
-XX:MaxPermSize 设置永久代最大值 物理内存的1/4 jdk1.8之后不生效
-Xss 每个线程的堆栈大小 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用,如果栈不是很深,应该是128k够用的,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试
-XX:ThreadStackSize Thread Stack Size (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]
-XX:NewRatio 新生代(包括Eden和两个Survivor区)与老年代的比值(除去永久代) -XX:NewRatio=4表示新生代与老年代所占比值为1:4,新生代占整个堆栈的1/5Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值 HotSpot默认设置为8,即两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个新生代的1/10
-XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响Perm的大小 =128m
-XX:+UseFastAccessorMethods 原始类型的快速优化
-XX:+DisableExplicitGC 关闭System.gc() 这个参数需要严格的测试
-XX:MaxTenuringThreshold 垃圾最大年龄 如果设置为0的话,则新生代对象不经过Survivor区,直接进入老年代。对于老年代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则新生代对象会在Survivor区进行多次复制,这样可以增加对象再新生代的存活时间,增加在新生代即被回收的概率。该参数只有在串行GC时才有效
-XX:+AggressiveOpts 加快编译
-XX:+UseBiasedLocking 锁机制的性能改善
-Xnoclassgc 禁用垃圾回收
-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间 1s softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap
-XX:PretenureSizeThreshold 对象超过多大是直接在老年代分配 0 单位字节 新生代采用Parallel Scavenge GC时无效,另一种直接在老年代分配的情况是大的数组对象,且数组中无外部引用对象
-XX:TLABWasteTargetPercent TLAB占eden区的百分比 1%
-XX:+CollectGen0First FullGC时是否先YGC false

JVM性能调优工具

先进入到jdk的目录

[dengyulong@09:20:14]~$ /usr/libexec/java_home -V
Matching Java Virtual Machines (1):
    1.8.0_201, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home

[dengyulong@09:23:50]~$ cd /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin

[dengyulong@09:27:16]/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin$ ls
appletviewer   java           javap          jdeps          jmc            jstat          orbd           rmiregistry    unpack200
extcheck       javac          javapackager   jhat           jps            jstatd         pack200        schemagen      wsgen
idlj           javadoc        jcmd           jinfo          jrunscript     jvisualvm      policytool     serialver      wsimport
jar            javafxpackager jconsole       jjs            jsadebugd      keytool        rmic           servertool     xjc
jarsigner      javah          jdb            jmap           jstack         native2ascii   rmid           tnameserv

1. jps

查看JVM中运行的进程状态信息

[dengyulong@09:33:03]~$ jps
16224 Bootstrap
30529
27157 Bootstrap
53030 Main
24376 Bootstrap
52637 Launcher
52845 Launcher
52846 Bootstrap
53150 Jps

2. jstack

查看某个Java进程内的线程堆栈信息

$ jstack [pid]

3. jstat

查看各个区内存和GC的情况

-class
显示加载class的数量,及所占空间等信息

[dengyulong@10:29:28]~$ jstat -class 30529
Loaded  Bytes  Unloaded  Bytes     Time
145689 253316.0    61218 80613.1     102.79
  • Loaded : 已经装载的类的数量
  • Bytes : 装载类所占用的字节数
  • Unloaded:已经卸载类的数量
  • Bytes:卸载类的字节数
  • Time:装载和卸载类所花费的时间

-compiler
显示VM实时编译(JIT)的数量等信息

[dengyulong@15:04:33]~$ jstat -compiler 30529
Compiled Failed Invalid   Time   FailedType FailedMethod
  402707     25       0  5391.41          1 com/intellij/psi/impl/compiled/StubBuildingVisitor visitMethod
  • Compiled:编译任务执行数量
  • Failed:编译任务执行失败数量
  • Invalid :编译任务执行失效数量
  • Time :编译任务消耗时间
  • FailedType:最后一个编译失败任务的类型
  • FailedMethod:最后一个编译失败任务所在的类及方法

-gc
显示gc相关的堆信息,查看gc的次数,及时间

[dengyulong@15:04:40]~$ jstat -gc 30529
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
38336.0 38336.0 10431.9  0.0   306944.0 93751.1   769408.0   475510.8  567576.0 517967.3 72696.0 63593.5   9158  131.185  683   362.332  493.517
  • S0C:新生代中第一个 survivor 的容量(Byte)
  • S1C:新生代中第二个 survivor 的容量(Byte)
  • S0U:新生代中第一个 survivor 目前已使用空间(Byte)
  • S1U:新生代中第二个 survivor 目前已使用空间(Byte)
  • EC:新生代中 Eden 的容量(Byte)
  • EU:新生代中 Eden 目前已使用空间(Byte)
  • OC:老年代的容量(Byte)
  • OU:老年代目前已使用空间(Byte)
  • MC:metaspace(元空间)的容量(Byte)
  • MU:metaspace(元空间)目前已使用空间(Byte)
  • YGC:从应用程序启动到采样时新生代中gc次数
  • YGCT:从应用程序启动到采样时新生代gc(Minor GC)所用时间(s)
  • FGC:从应用程序启动到采样时老年代gc(Full GC)次数
  • FGCT:从应用程序启动到采样时老年代gc(Full GC)所用时间(s)
  • GCT:从应用程序启动到采样时gc用的总时间(s)

-gcutil
统计gc信息

[dengyulong@15:17:22]~$ jstat -gcutil 30529
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  53.89  63.02  91.26  87.48   9159  131.207   684  363.946  495.153
  • S0:新生代中第一个survivor已使用的占当前容量百分比
  • S1:新生代中第二个survivor已使用的占当前容量百分比
  • E:新生代中Eden已使用的占当前容量百分比
  • O:老年代已使用的占当前容量百分比
  • M:Metaspace已使用的占当前容量百分比
  • YGC:从应用程序启动到采样时新生代中gc次数
  • YGCT:从应用程序启动到采样时新生代中gc所用时间(s)
  • FGC:从应用程序启动到采样时老年代GC(Full GC)次数
  • FGCT:从应用程序启动到采样时老年代GC(Full GC)所用时间(s)
  • GCT:从应用程序启动到采样时gc用的总时间(s)

其他的如:-gccapacity-gcmetacapacity-gcnew-gcnewcapacity-gcold-gcoldcapacity-gccause-printcompilation按需使用,这里不多做介绍

4. jvisualvm

JDK自带监控程序

jvisualvm的监控界面

参考文章
面试题:“你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?”
jvm 性能调优工具之 jstat

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343