JVM基础 -> 你们项目如何排查JVM问题

16 篇文章 3 订阅

你们项目如何排查JVM问题

对于还在正常运⾏的系统:

  • 通过各个命令的结果,或者jvisualvm等⼯具来进⾏分析

    • 可以使⽤jmap来查看JVM中各个区域的使⽤情况

    • 可以通过jstack来查看线程的运⾏情况,⽐如哪些线程阻塞、是否出现了死锁

    • 可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc⽐较频繁,那么就得进⾏调优了

  • ⾸先,初步猜测频繁fullgc(老年代整体大面积垃圾回收)的原因

  • 如果频繁发⽣fullgc但是⼜⼀直没有出现内存溢出,系统运行的好好的

  • 那么表示gc实际上是一下可以回收很多对象了,所以这些对象最好能在younggc(新生代垃圾回收)过程中就直接回收掉,避免这些对象进⼊到⽼年代,甚至搞到永久区里去

  • 对于这种情况,就要看看什么原因了

    • 考虑这些存活时间不⻓的对象是不是⽐较⼤,导致年轻代放不下,直接进⼊到了⽼年代,尝试加⼤年轻代的⼤小
    • 检查一下是哪个线程使用内存太多了
    • 检查一下是哪个线程占⽤CPU太多了,定位到具体的⽅法,优化这个⽅法的执⾏,看是否能避免某些对象的创建,从⽽节省内存

对于已经发⽣了OOM(内存溢出)的系统:

  1. ⼀般⽣产系统中都会设置当系统发⽣了OOM时,⽣成当时的dump⽂件

    • - XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base
  2. 我们可以利⽤jsisualvm(可视化)等⼯具来分析dump⽂件

  3. 根据dump⽂件找到异常的实例对象,和异常的线程(占⽤CPU⾼),定位到具体的代码

  4. 然后再进⾏详细的分析和调试

JVM图形化查看详细

  • 图形界面可以去jdk的bin下面找到jvisualvm.exe -> 这是一个自带的图形化界面
  • 有一些好用的插件可以试着安装Visual GC btrace

查看线程情况
请添加图片描述
查看各个线程使用的内存和cpu的情况
在这里插入图片描述
查看垃圾回收情况
在这里插入图片描述

查看dump跟踪信息
在这里插入图片描述

JVM命令查看详细

jps -> Java ps:查看正在运行的Java进程

  • jps: 可以列出正在运行的Java进程,并显示虚拟机执行主类名称以及进程id

    C:\>jps
    5932 测试Collection
    
  • 常见的选项: jps -l jps -v

    • jps -l -> 输出主类全类名,如果进程执行的是Jar包,输出jar包名字

      C:\>jps -l
      5932 rod.集合.测试Collection
      
    • jps -v -> 程序启动时指定的jvm参数

      5932 测试Collection 
          -agentlib:jdwp=transport=dt_socket,
      	address=127.0.0.1:54731,
      	suspend=y,
      	server=n 
          -javaagent:C:\Users\欧皇小德子\AppData\Local\JetBrains\IntelliJIdea2021.2\captureAgent\debugger-agent.jar 
          -Dfile.encoding=UTF-8
      

jstack -> Java stack:打印线程快照

  • 查看某个Java进程中所有线程的状态

  • 一般用来定位线程出现长时间停顿的原因,如发生死循环,死锁,请求外部资源长时间等待等!

  • 常见的选项: jstack 进程id jps -v

    • jstack 进程id -> 进程中所有线程的状态,只要程序还在走,就打印轨迹

      [C:\~]$ jstack 37476
      2022-02-22 19:17:04
      Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.231-b11 mixed mode):
      
      "DestroyJavaVM" #24 prio=5 os_prio=0 tid=0x0000000002f94000 nid=0x8a40 waiting on condition [0x0000000000000000]
         java.lang.Thread.State: RUNNABLE
      
      "myThreadB" #23 prio=5 os_prio=0 tid=0x0000000025314000 nid=0x5018 waiting for monitor entry [0x00000000266bf000]
         java.lang.Thread.State: BLOCKED (on object monitor)
      	at rod.TestMain.lambda$main$1(TestMain.java:35)
      	- waiting to lock <0x0000000743a6a820> (a java.lang.Object)
      	- locked <0x0000000743a6a830> (a java.lang.Object)
      	at rod.TestMain$$Lambda$2/1349414238.run(Unknown Source)
      	at java.lang.Thread.run(Thread.java:748)
      
      "myThreadA" #22 prio=5 os_prio=0 tid=0x0000000025313000 nid=0x2794 waiting for monitor entry [0x00000000265bf000]
         java.lang.Thread.State: BLOCKED (on object monitor)
      	at rod.TestMain.lambda$main$0(TestMain.java:21)
      	- waiting to lock <0x0000000743a6a830> (a java.lang.Object)
      	- locked <0x0000000743a6a820> (a java.lang.Object)
      	at rod.TestMain$$Lambda$1/1873653341.run(Unknown Source)
      	at java.lang.Thread.run(Thread.java:748)
      
      "Service Thread" #21 daemon prio=9 os_prio=0 tid=0x0000000024ff1000 nid=0x8cc4 runnable [0x0000000000000000]
         java.lang.Thread.State: RUNNABLE
         
      ...
      
      "VM Thread" os_prio=2 tid=0x00000000215f7000 nid=0x8710 runnable 
      
      "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002faa800 nid=0x9378 runnable 
      ...
      发现一个Java级死锁:
      =============================
      "myThreadB":
        等待锁定监视器 0x0000000021601c08 (object 0x0000000743a6a820, a java.lang.Object),
        这是由 "myThreadA"
      "myThreadA":
        等待锁定监视器 0x0000000021604338 (object 0x0000000743a6a830, a java.lang.Object),
        这是由 "myThreadB"
      
      列出的线程的Java堆栈信息:
      ===================================================
      "myThreadB":
      	at rod.TestMain.lambda$main$1(TestMain.java:35)
      	- waiting to lock <0x0000000743a6a820> (a java.lang.Object)
      	- locked <0x0000000743a6a830> (a java.lang.Object)
      	at rod.TestMain$$Lambda$2/1349414238.run(Unknown Source)
      	at java.lang.Thread.run(Thread.java:748)
      "myThreadA":
      	at rod.TestMain.lambda$main$0(TestMain.java:21)
      	- waiting to lock <0x0000000743a6a830> (a java.lang.Object)
      	- locked <0x0000000743a6a820> (a java.lang.Object)
      	at rod.TestMain$$Lambda$1/1873653341.run(Unknown Source)
      	at java.lang.Thread.run(Thread.java:748)
      
      
    • 可能看到一些线程的轨迹:

      • DestroyJavaVM -> 销毁线程**(RUNNABLE=运行状态)**
      • myThreadB丶myThreadA -> 我们直接定义的线程**(BLOCKED=阻塞状态)**
      • Service Thread -> 还有很多名称的daemon守护线程也是运行状态就不列举了
      • GC -> 垃圾回收线程**(RUNNABLE=运行状态)**
    • 并且在最后提示出了死锁发生的位置

jmap -> Java map:导出堆内存映像文件

  • jmap主要用来用来导出堆内存映像文件,看是否发生内存泄露等。

    • 内存溢出: 内存满了,炸了,你还要创建新对象,直接爆炸 -> 溢出比较好记,就是满了,反过来就是泄露
    • 内存泄露: 内存没满好好的,但是有很多垃圾需要可以回收却回收不了,站着茅坑不拉屎
  • 生产环境一般会配置如下参数,让虚拟机在OOM异常出现之后自动生成dump文件

    • Dump文件是进程内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。
    • 主要是用来在系统中出现异常或者崩溃的时候来生成dump文件
    • 然后用调试器进行调试,这样就可以把生产环境中的dump文件拷贝到自己的开发机上
    • 调试就可以找到程序出错的位置。
    //输出错误堆Dump信息 路径/Users/peng
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/peng
    
  • 执行如下命令即可手动获得dump文件

    jmap -dump:file=文件名.dump 进程id
    

jstat -> java stat: 查看jvm统计信息

  • jstat可以显示本地或者远程虚拟机进程中的类装载、 内存、 垃圾收集、 JIT(编译器)编译等运行数据

  • 用jstat查看一下类装载的信息。我个人很少使用这个命令,命令行看垃圾收集信息真不如看图形界面方便

    [C:\~]$ jstat -class  37476
    加载类的个数	加载类的字节数	卸载类的个数 卸载类的字节数	花费的时间
    Loaded  		Bytes 		Unloaded  	Bytes    	 Time   
       661  		1285.3        0     	0.0      	 0.17
    

点赞,靓仔!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值