💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

【Jvm调优篇3】JVM调优_jvm

  • 推荐:kuan 的首页,持续学习,不断总结,共同进步,活到老学到老
  • 导航
  • 檀越剑指大厂系列:全面总结 java 核心技术点,如集合,jvm,并发编程 redis,kafka,Spring,微服务,Netty 等
  • 常用开发工具系列:罗列常用的开发工具,如 IDEA,Mac,Alfred,electerm,Git,typora,apifox 等
  • 数据库系列:详细总结了常用数据库 mysql 技术点,以及工作中遇到的 mysql 问题等
  • 懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
  • 数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨


博客目录

  • 1. jps 作用与使用?
  • 2.jstat 作用与使用?
  • 3.jstack 作用?
  • 4.jinfo 作用与使用?
  • 5.jmap 作用与使用?
  • 6.jhat 作用与使用?
  • 7.jvm 调优实用工具?
  • 8.打印 gc 日志的命令?
  • 9.内存泄漏和内存溢出?
  • 10.什么是对象逃逸?
  • 11.什么是锁粗化?
  • 12.有过 jvm 调优经验吗?
  • 13.JVM 两种常见异常?
  • 14.堆栈溢出异常?
  • 15.方法区运行时常量池异常?
  • 16.本机直接内存异常?
  • 17.CPU 过高问题排查?
  • 18.生产环境访问慢?
  • 19.visualVM


1. jps 作用与使用?

可以列出正在运行的虚拟机进程,并显示虚拟机执行主类 Main Class, main()函数所在的类名称以及这些进程的本地虚拟机唯一 ID (LVMID, Local Virtual Machine Identifier)。虽然功能比较单一,但它绝对是使用频率最高的 JDK 命令行工具,因为其他的 JDK 工具大多需要输入它查询到的 LVMID 来确定要监控的是哪一个虚拟机进程。对于本地虚拟机进程来说, LVMID 与操作系统的进程 ID (PID, Process Identifier)是一致的,使用 Windows 的任务管理器或者 UNIX 的 ps 命令也可以查询到虚拟机进程的 LVMID,但如果同时启动了多个虚拟机进程,无法根据进程名称定位时,那就必须依赖 jps 命令显示主类的功能才能区分了。

  • -q:只输出进程 id
  • -m:输出虚拟机启动时传递给 main()方法的参数
  • -l:输出主类的全名,如果执行的是 jar 包,输出 jar 包的路径
  • -v:输出启动虚拟机的参数

2.jstat 作用与使用?

jstat (JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据,在没有 GUI 图形界面、只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的常用工具。

#参数interval和count代表查询间隔和次数,如果省略这2个参数,说明只查询一次。假设需要每1000毫秒查询一次进程42339垃圾收集状况,一共查询10次,那命令应当是:
jstat -gc 42339 1000 10

【Jvm调优篇3】JVM调优_老年代_02

上图显示了各个区以及垃圾回收的情况,具体代表含义如下(C 代表 Capacity,U 代表 Used 已使用大小)

  • S0C 和 S1C 代表 Survivor 区的 S0 和 S1 的大小
  • S0U 和 S1U 表示已使用空间
  • EC 表示 Eden 区大小,EU 表示 Eden 区已使用容量
  • OC 表示老年代大小,OU 代表老年代已使用容量
  • MC 表示方法区大小,MU 表示方法区已使用容量
  • CCSC 表示压缩类空间大小,CCSU 表示压缩类空间已使用大小
  • YGC 表示新生代 GC 次数,YGT 表示新生代 GC 总耗时
  • FGC 表示 Full GC 次数,FGCT 表示 FULL GC 总耗时
  • GCT 表示 GC 总耗时时间

选项 option 代表用户希望查询的虚拟机信息,主要分为三类:类加载、垃圾收集、运行期编译状况。

【Jvm调优篇3】JVM调优_老年代_03

【Jvm调优篇3】JVM调优_jvm_04

3.jstack 作用?

jstack (Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为 threaddump 或者 javacore 文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因。线程出现停顿时通过 jstack 来查看各个线程的调用堆栈,就可以获知没有响应的线程到底在后台做些什么事情,或者等待着什么资源。

jstack 进程号 > stack1.log

【Jvm调优篇3】JVM调优_Java_05

option 选项的合法值与具体含义下图所示。

【Jvm调优篇3】JVM调优_JVM_06

从 JDK 5 起, java.lang.Thread 类新增了一个 getAllStackTraces()方法用于获取虚拟机中所有线程的 StackTraceElement 对象。使用这个方法可以通过简单的几行代码完成 jstack 的大部分功能,在实际项目中不妨调用这个方法做个管理员页面,可以随时使用浏览器来查看线程堆栈

4.jinfo 作用与使用?

jinfo (Configuration Info for Java)的作用是实时查看和调整虚拟机各项参数。注意,如果是修改,参数类型是 manageable 类型才能修改。

#查看某个java进程的name属性的值
jinfo -flag name PID

5.jmap 作用与使用?

jmap (Memory Map for Java)命令用于生成堆转储快照(一般称为 heapdump 或 dump 文件)。

jmap 的作用并不仅仅是为了获取堆转储快照,它还可以查询 finalize 执行队列、Java 堆和方法区的详细信息,如空间使用率、当前用的是哪种收集器等。

#打印出堆内存相关信息
jmap -heap PID

#生成dump文件
jmap -dump:format=b,file=/usr/heap.hprof  pid

#OOM时自动生成dump文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof

jmap 可以添加的参数如下:

【Jvm调优篇3】JVM调优_Java_07

6.jhat 作用与使用?

JDK 提供 jhat (JVM Heap Analysis Tool)命令与 jmap 搭配使用,来分析 jmap 生成的堆转储快照。

jhat heap.hprof

然后访问地址 http://localhoust:7000/ 可以看到这款工具展示的信息比较简单。

生产上应该结合 dump 文件分析工具 MAT 或者 VisualVM 工具进行分析使用

7.jvm 调优实用工具?

  • jconsole 工具
  • VisualVM 工具
  • 监控应用程序的 CPU、GC、堆。方法区和线程信息(jstack 和 jstat 的功能)
  • dump 文件以及分析(jmap 和 jhat 的功能)
  • 方法级的程序性能分析,可以找出被调用最多,运行时间最长的方法
  • 离线程序快照:收集程序运行时配置、线程 dump。内存 dump 等信息建立一个快照,并可以将快照发送给开发者进行 bug 反馈。
  • 插件化处理,有无限扩展可能

8.打印 gc 日志的命令?

#打印gc日志的命令
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:/Users/lizhengqiang/Documents/gc.log

找到 gc.log 文件,刚开始没有发生 GC,所以文件是空的,等到发生 GC 后打开

【Jvm调优篇3】JVM调优_jvm_08

9.内存泄漏和内存溢出?

内存泄漏:是指创建的对象已经没有用处,正常情况下应该会被垃圾收集器回收,但是由于该对象仍然被其他对象进行了无效引用,导致不能够被垃圾收集器及时清理,这种现象称之为内存泄漏。内存泄漏会导致内存堆积,最终发生内存溢出,导致 OOM。

内存溢出:java 堆用于存储对象实例,只要不断的创建实例,并保证 GC roots 到对象是可达的,避免被回收,对象数量达到堆的最大容量时就会出现内存溢出异常.

出现 Java 堆内存溢出时,异常堆栈信息“java.lang.OutOfMemoryError”会跟着进一步提示“Java heap space”。

//设置jvm参数 VM Args:-Xms20m-Xmx20m -XX:+HeapDumpOnOutOfMemoryError
public class Jvm_01_HeapOOM {
  static class OOMObject {
  }

  public static void main(String[] args) {
    List<OOMObject> list = new ArrayList<OOMObject>();
    while (true) {
      list.add(new OOMObject());
    }
  }
}

内存溢出分三种情况

OutOfMemoryError: PermGen space

Permanent Generation space 这个区域主要用来保存加来的 Class 的一些信息,在程序运行期间属于永久占用的,Java 的 GC 不会对他进行释放,所以如果启动的程序加载的信息比较大,超出了这个空间的大小,就会发生溢出错误;

解决的办法:增加空间分配——增加 java 虚拟机中的 XX:PermSize 和 XX:MaxPermSize 参数的大小,其中 XX:PermSize 是初始永久保存区域大小,XX:MaxPermSize 是最大永久保存区域大小。

OutOfMemoryError:Java heap space

heap 是 Java 内存中的堆区,主要用来存放对象,当对象太多超出了空间大小,GC 又来不及释放的时候,就会发生溢出错误。Java 中对象的创建是可控的,但是对象的回收是由 GC 自动的,一般来说,当已存在对象没有引用(即不可达)的时候,GC 就会定时的来回收对象,释放空间。但是因为程序的设计问题,导致对象可达但是又没有用(即前文提到的内存泄露),当这种情况越来越多的时候,问题就来了。
针对这个问题,我们需要做一下两点:

1、检查程序,减少大量重复创建对象的死循环,减少内存泄露。
2、增加 Java 虚拟机中 Xms(初始堆大小)和 Xmx(最大堆大小)参数的大小。

StackOverFlowError

stack 是 Java 内存中的栈空间,主要用来存放方法中的变量,参数等临时性的数据的,发生溢出一般是因为分配空间太小,或是执行的方法递归层数太多创建了占用了太多栈帧导致溢出。针对这个问题,除了修改配置参数-Xss 参数增加线程栈大小之外,优化程序是尤其重要。

10.什么是对象逃逸?

什么是对象逃逸?对象逃逸优化有哪几种?

逃逸分析的基本原理是:分析对象动态作用域,当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种称为方法逃逸;甚至还有可能被外部线程访问到,譬如赋值给可以在其他线程中访问的实例变量,这种称为线程逃逸;从不逃逸、方法逃逸到线程逃逸,称为对象由低到高的不同逃逸程度。

#在Java代码运行时,通过JVM参数可指定是否开启逃逸分析,
-XX:+DoEscapeAnalysis : 表示开启逃逸分析
-XX:-DoEscapeAnalysis : 表示关闭逃逸分析。

优化有三种:栈上分配;标量替换;锁消除(或称同步消除)。

栈上分配(Stack Allocations):在 Java 虚拟机中, Java 堆上分配创建对象的内存空间几乎是 Java 程序员都知道的常识, Java 堆中的对象对于各个线程都是共享和可见的,只要持有这个对象的引用,就可以访问到堆中存储的对象数据。虚拟机的垃圾收集子系统会回收堆中不再使用的对象,但回收动作无论是标记筛选出可回收对象,还是回收和整理内存,都需要耗费大量资源。如果确定一个对象不会逃逸出线程之外,那让这个对象在栈上分配内存将会是一个很不错的主意,对象所占用的内存空间就可以随栈帧出栈而销毁。在一般应用中,完全不会逃逸的局部对象和不会逃逸出线程的对象所占的比例是很大的,如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了,垃收集子系统的压力将会下降很多。栈上分配可以支持方法逃逸,但不能支持线程逃逸。

标量替换(Scalar Replacement):若一个数据已经无法再分解成更小的数据来表示了, Java 虚拟机中的原始数据类型(int 、 long 等数值类型及 reference 类型等)都不能再进一步分解了,那么这些数据就可以被称为标量。相对的,如果一个数据可以继续分解,那它就被称为聚合量(Aggregate), Java 中的对象就是典型的聚合量。如果把一个 Java 对象拆散,根据程序访问的情况,将其用到的成员变量恢复为原始类型来访问,这个过程就称为标量替换。假如逃逸分析能够证明一个对象不会被方法外部访问,并且这个对象可以被拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。将对象拆分后,除了可以让对象的成员变量在栈上(栈上存储的数据,很大机会被虚拟机分配至物理机器的高速寄存器中存储)分配和读写之外,还可以为后续进一步的优化手段创建条件。标量替换可以视作栈上分配的一种特例,实现更简单(不用考虑整个对象完整结构的分配),但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内。

同步消除(Synchronization Elimination):线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个变量不会逃逸出线程,无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,对这个变量实施的同步措施也就可以安全地消除掉。

public String concatString(String s1, String s2, String s3) {
  StringBuffer sb = new StringBuffer();
  sb.append(s1);
  sb.append(s2);
  sb.append(s3);
  return sb.toString();
}

每个 StringBuffer.append()方法中都有一个同步块,锁就是 sb 对象在 concatString()方法内部。也就是 sb 的所有引用都永远不会逃逸到 concatString()方法之外,其他线程无法访问到它,所以这里虽然有锁,但是可以被安全地消除掉 。在解释执行时这里仍然会加锁,但在经过服务端编译器的即时编译之后,这段代码就会忽略。虚拟机观察变量 sb,经过逃逸分析后会发现它的动态作用域被限制所有的同步措施而直接执行。

11.什么是锁粗化?

【Jvm调优篇3】JVM调优_老年代_09

12.有过 jvm 调优经验吗?

JVM 调优情况十分复杂,各种情况都可能导致垃圾回收不能够达到预想的效果。对于场景问题,可以从如下几个大方向进行设计:

  1. MinorGC 频繁
  • MinorGC 是针对新生代进行回收的,每次在 MGC 存活下来的对象,会移动到 Survivor1 区。
  • 大访问压力下, MGC 频繁一些是正常的,只要 MGC 延迟不导致停顿时间过长或者引发 FGC
  • 可以适当的增大 Eden 空间大小,降低频繁程度,同时要保证,空间增大对垃圾回收时间产生的停顿时间增长也是可以接受的。
  1. Full GC
  • 如果 MinorGC 频繁,且容易引发 Full GC 。
  • 每次 MGC 存活的对象的大小,是否能够全部移动到 S1 区,如果 S1 区大小< MGC 存活的对象大小,这批对象会直接进入老年代。
  • 这批对象的年龄才 1 岁,很有可能再多等 1 次 MGC 就能被回收了,可是却进入了老年代,只能等到 Full GC 进行回收,很可怕。这种情况下,应该在系统压测的情况下,实时监控 MGC 存活的对象大小,并合理调整 eden 和 s 区的大小以及比例。
  • 还有一种情况会导致对象在未达到 15 岁之前,直接进入老年代,就是 S1 区的对象,相同年龄的对象所占总空间大小>s1 区空间大小的一半,所以为了应对这种情况,对于 S 区的大小的调整就要考虑:尽量保证峰值状态下, S1 区的对象所占空间能够在 MGC 的过程中,相同对象年龄所占空间不大于 S1 区空间的一半,因此对于 S1 空间大小的调整,也是十分重要的。
  1. 大对象创建频繁,导致 Full GC 频繁。
  • 对于大对象, JVM 专门有参数进行控制,-XX: PretenureSizeThreshold 。超过这个参数值的对象,会直接进入老年代,只能等到 full GC 进行回收,所以在系统压测过程中,要重点监测大对象的产生。
  • 如果能够优化对象大小,则进行代码层面的优化,优化如:根据业务需求看是否可以将该大对象设置为单例模式下的对象,或者该大对象是否可以进行拆分使用,或者如果大对象确定使用完成后,将该对象赋值为 null,方便垃圾回收。
  • 如果代码层面无法优化,则需要考虑
  • 调高-XX: PretenureSizeThreshold 参数的大小,使对象有机会在 eden 区创建,有机会经历 MGC 以被回收。但是这个参数的调整要结合 MGC 过程中 Eden 区的大小是否能够承载,包括 S1 区的大小承载问题。
  • 这是最不希望发生的情况,如果必须要进入老年代,也要尽量保证,该对象确实是长时间使用的对象,放入老年代的总对象创建量不会造成老年代的内存空间迅速长满发生 Full GC,在这种情况下,可以通过定时脚本,在业务系统不繁忙情况下,主动触发 full gc。
  1. 内存泄漏导致的 MGC 和 FGC 频繁,最终引发 oom 。
  2. 纯代码级别导致的 MGC 和 FGC 频繁。
  • 如果是这种情况,那就只能对代码进行大范围的调整,这种情况就非常多了,而且会很糟糕。
  • 如大循环体中的 new 对象,未使用合理容器进行对象托管导致对象创建频繁,不合理的数据结构使用等等。

MGC 与 FGC 停顿时间长导致影响用户体验。其实对于停顿时间长的问题无非就两种情况:

  • a:gc 真实回收过程时间长,即 realtime 时间长。这种时间长大部分是因为内存过大导致,导致从标记到清理的过程中需要对很大的空间进行操作,导致停顿时间长。
  • b:gc 真实回收时间 real time 并不长,但是 user time(用户态执行时间)和 systime (核心态执行时间)时间长,导致从客户角度来看,停顿时间过长。

对于 a 情况,要考虑减少堆内存大小,包括新生代和老年代,比如之前使用 16G 的堆内存,可以考虑将 16G 内存拆分为 4 个 4G 的内存区域,可以单台机器部署 JVM 逻辑集群,也可以为了降低 GC 回收时间进行 4 节点的分布式部署,这里的分布式部署是为了降低 GC 垃圾回收时间。

对于 b 情况,要考虑线程是否及时达到了安全点,通过-XX:+PrintSafepointStatistics 和-XX: PrintSafepointStatisticsCount=1 去查看安全点日志,如果有长时间未达到安全点的线程,再通过参数-XX:+SafepointTimeout 和-XX: SafepointTimeoutDelay=2000 两个参数来找到大于 2000ms 到达安全点的线程,这里的 2000ms 可以根据情况自己设置,然后对代码进行针对的调整。除了安全点问题,也有可能是操作系统本身负载比较高,导致处理速度过慢,线程达到安全点时间长,因此需要同时检测操作系统自身的运行情况。

13.JVM 两种常见异常?

StackOverFlowError: 如果 Java 虚拟机栈容量不能动态扩展,而此时线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。

OutOfMemoryError: 如果 Java 虚拟机栈容量可以动态扩展,当栈扩展的时候,无法申请到足够的内存(Java 虚拟机堆中没有空闲内存,垃圾回收器也没办法提供更多内存)

14.堆栈溢出异常?

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;

如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展,只不过 Java 虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出 OutOfMemorvError 异常。

  • 单线程 用-Xss 减少栈内存容量,来模拟 StackOverflowError 异常.
  • 多线程,-Xss 设置大一些,模拟 OutOfMemoryError 异常.

15.方法区运行时常量池异常?

String.intern()是一个 Native 方法,它的作用是:如果字符串常量池中已经包含一个等于此 String 对象的字符串,则返回代表池中这个字符串的 String 对象;否则,将此 String 对象包含的字符串添加到常量池中,并且返回此 String 对象的引用。在 JDK1.6 及之前的版本中,由于常量池分配在永久代内,我们可以通过-Xx:PermSize 和-
XX:MaxPermSize 限制方法区大小,从而间接限制其中常量池的容量.

public class Jvm_04_RuntimeConstantPooloOM {
  public static void main(String[] args) {
    //使用List保持着常量池引用,避免FullGC回收常量池行为
    List<String> list = new ArrayList<String>();
    // 10MB的PermSize在integer范围内足够产生00M了
    int i = 0;
    while (true) {
      list.add(String.valueOf(i++).intern());
    }
  }
}

OutOfMemoryError 后面跟随的提示信息是“Perm Gen space”

16.本机直接内存异常?

DirectMemory 容量可通过-XX:MaxDirectMemorySize 指定,如果不指定,则默认与 Java 堆最大值(-Xmx 指定)一样,直接通过反射获取 Unsafe 实例进行内存分配(Unsafe 类的 getUnsafe))方法限制了只有引导类加载器才会返回实例,也就是设计者希望只有 rtjar 中的类才能使用 Unsafe 的功能)。因为,虽然使用 DirectByteBuffer 分配内存也会抛出内存溢出异常,但它抛出异常时并没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请分配内存的方法是 unsafeallocateMemorv)。

public class Jvm_05_DirectMemory0OM {

  private static final int _1MB = 1024 * 1024;

  public static void main(String[] args) throws Exception {
    Field unsafeField = Unsafe.class.getDeclaredFields()[0];
    unsafeField.setAccessible(true);
    Unsafe unsafe = (Unsafe) unsafeField.get(null);
    while (true) {
      unsafe.allocateMemory(_1MB);
    }
  }
}

17.CPU 过高问题排查?

1.top 命令

使用 top 查询到 cup 过高的进程 PID

top

2.查应用

ps -ef | grep java

查看所有的 java 进程,在结果中找到进程号为 12836 的进程,即可查看是哪个应用占用的该进程

3.查线程

#查看进程的cpu和内存信息
top -H -p  进程PID

#查看十六进制的异常线程PID
printf "%x\n"  线程PId

#这是十进制的数据,将 3034 转成十六进制为 0Xbda
#十六进制转十进制,直接放在浏览器的控制台即可

#生成stack文件
jstack -l 3033 > ./3033.stack

#查看线程信息
cat 3033.stack |grep 'xxxx'

#查看使用最耗费cpu的线程堆栈信息
cat stack | grep -i 34670 -C10 --color

4.异常信息

jstack 进程号|grep 16进制异常线程号 -A100

5.查看gc信息

jstat -gc pid 1000 100 > gctimes.log

18.生产环境访问慢?

1.查看错误日志

#最后1000行
tail  -1000   xxx.log

如果看到 OOM

2.进容器排查

#获取容器id,并查看容器运行状态
docker stats

#进入容器
docker exec -it 47863d1021e9   /bin/bash

#十进制转十六进制
printf "%x\n"  24306

#找到进程pid
ps -ef |grep java

#查看gc情况,1000ms执行一次,一共执行10次
jstat -gc 进程pid 1000 10

#线程快照,用于排查线程间死锁、死循环、请求外部资源导致的长时间挂起等
jstack 进程pid

#打印出堆内存相关信息
jmap -heap 进程pid

#生成dump文件
jmap -dump:format=b,file=/home/heap.hprof 进程pid

#图形化展示
jhat heap.hprof

3.OOM 问题定位

#可以查看新生代,老年代堆内存的分配大小以及使用情况,看是否本身分配过小
jmap -heap pid

#结果以表格的形式显示存活对象的信息,并按照所占内存大小排序,找到最耗内存的对象
jmap -histo:live 进程pid | more

19.visualVM

在服务器上使用命令导出文件

#检测容器占用的内存和cpu,以及IO情况
docker stats

#进入容器
docker exec -it 容器id bash

#进入home目录
cd  /home

#查找进程id
ps -ef|grep java

#生成dump文件
jmap -dump:format=b,file=/home/heap-$(date +%F%n).hprof  进程id

#拷贝文件到宿主机
docker cp   ce4830fa74f7:/home/heap-2023-03-23.hprof  /home/app/deepexi-dsc-belle-insight-command/

打开 visualVM 软件,导入生成的 dump 文件,主要观测概览,对象,线程三个主要信息

summary:概览

【Jvm调优篇3】JVM调优_Java_10

objects:对象信息

【Jvm调优篇3】JVM调优_Java_11

线程:观测可能出现的死锁信息

【Jvm调优篇3】JVM调优_jvm_12

在 idea 中使用,需要使用 visualVM 启动应用

【Jvm调优篇3】JVM调优_Java_13

【Jvm调优篇3】JVM调优_JVM_14

【Jvm调优篇3】JVM调优_老年代_15

觉得有用的话点个赞 👍🏻 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

【Jvm调优篇3】JVM调优_JVM_16