Java基础之JVM基础调优与常见问题

常见命令

以下命令的介绍,全部在jdk8环境下运行的;

jps ☆☆☆☆☆

查看当前运行的进程号;

jmap ☆☆☆

jmap命令可以查看jvm的内存信息,class对应的实例个数以及占用的内存大小

jmap -histo

查看当前java进程
[rd@VM-8-12-centos ~]$ jps
8368 Jps
20073 CpuTopThreadHelper

# 查看历史生成的实例
jmp -histo:live 20073

# 查看当前进程存活的实例,其中执行过程中,可能会触发一次fullgc
jmap -histo:live 20073

列头说明:

  • num:序号
  • instantces:实例数
  • bytes:占用空间大小
  • class name:类名称
    • [C:char[]
    • [S:short[]
    • [I:int[]
    • [B:byte[]
    • [[I:int[][]

jmap -heap ☆☆☆☆

打印堆信息

[rd@VM-8-12-centos ~]$ jmap -heap 20073
Attaching to process ID 20073, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 968884224 (924.0MB)
   NewSize                  = 19922944 (19.0MB)
   MaxNewSize               = 322961408 (308.0MB)
   OldSize                  = 40894464 (39.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 15728640 (15.0MB)
   used     = 0 (0.0MB)
   free     = 15728640 (15.0MB)
   0.0% used
From Space:
   capacity = 2097152 (2.0MB)
   used     = 0 (0.0MB)
   free     = 2097152 (2.0MB)
   0.0% used
To Space:
   capacity = 2097152 (2.0MB)
   used     = 0 (0.0MB)
   free     = 2097152 (2.0MB)
   0.0% used
PS Old Generation
   capacity = 16252928 (15.5MB)
   used     = 260944 (0.2488555908203125MB)
   free     = 15991984 (15.251144409179688MB)
   1.6055199407762097% used

694 interned Strings occupying 46296 bytes.

jmap -dump

导出堆内存快照,用于分析执行dump操作时进程运行状态;

jmap -dump:format=b,file=/home/rd/20073.hprof 20073

进行启动时,可以设置在内存溢出时,自动dump当时内存快照(堆内存很大时,可能会dump失败),具体参数如下:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./xxxx.hprof

jstack ☆☆☆☆

查看当前进程下线程的运行情况,可以用来判断线程占用cpu情况,以及线程锁占用情况;

-- jstack pid
jstack 3305

部分字段说明:

  • prio:jvm线程优先级
  • os_prio:操作系统线程优化级
  • tid:jvm线程id
  • nid:操作系统线程id(十六进制)

jinfo ☆☆

查看正在运行的java应用程序的扩展参数;

查看jvm的参数:

bash 复制代码
[rd@VM-8-12-centos project]$ jinfo -flags 23852
Attaching to process ID 23852, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09
Non-default VM flags: 
-XX:CICompilerCount=2 
-XX:InitialHeapSize=60817408 
-XX:MaxHeapSize=968884224 
-XX:MaxNewSize=322961408 
-XX:MinHeapDeltaBytes=524288 
-XX:NewSize=19922944 
-XX:OldSize=40894464 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseParallelGC 
Command line:  
[rd@VM-8-12-centos project]$

查看java系统参数

bash 复制代码
[rd@VM-8-12-centos project]$ jinfo -flags 23852
Attaching to process ID 23852, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=60817408 -XX:MaxHeapSize=968884224 -XX:MaxNewSize=322961408 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=19922944 -XX:OldSize=40894464 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
Command line:  
[rd@VM-8-12-centos project]$ ^C
[rd@VM-8-12-centos project]$ jinfo -sysprops 23852
Attaching to process ID 23852, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.361-b09
sun.boot.library.path = /usr/local/java/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = US
user.dir = /home/rd/project
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_361-b09
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/local/java/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator = 

java.vm.specification.vendor = Oracle Corporation
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 3.10.0-1160.71.1.el7.x86_64
user.home = /home/rd
user.timezone = 
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
user.name = rd
java.class.path = .:/usr/local/java/lib/dt.jar:/usr/local/java/lib/tools.jar
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = com.ddu.jvm.DeadThreadHelper
java.home = /usr/local/java/jre
user.language = en
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_361
java.ext.dirs = /usr/local/java/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path = /usr/local/java/jre/lib/resources.jar:/usr/local/java/jre/lib/rt.jar:/usr/local/java/jre/lib/jsse.jar:/usr/local/java/jre/lib/jce.jar:/usr/local/java/jre/lib/charsets.jar:/usr/local/java/jre/lib/jfr.jar:/usr/local/java/jre/classes
java.vendor = Oracle Corporation
java.specification.maintenance.version = 4
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.cpu.isalist = 

jstat ☆☆☆☆☆

实际开发中,经常可以用到的命令,用来查看当前java进行内存使用情况;

bash 复制代码
jstat [-命令选项][vmid][间隔时间(毫秒)][查询次数]

垃圾回收统计

jstat -gc pid 最常用的命令,可以评估程序内存使用情况以及GC压力的整体情况

bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gc 23852 5000 10
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
2048.0 2048.0  0.0    0.0   15360.0   4302.9   39936.0      0.0     4480.0 779.8  384.0   76.0       0    0.000   0      0.000    0.000
2048.0 2048.0  0.0    0.0   15360.0   4302.9   39936.0      0.0     4480.0 779.8  384.0   76.0       0    0.000   0      0.000    0.000
2048.0 2048.0  0.0    0.0   15360.0   4302.9   39936.0      0.0     4480.0 779.8  384.0   76.0       0    0.000   0      0.000    0.000

单位统一为:KB

  • S0C:新生代第一个Survivor区的大小,单位KB
  • S1C:新生代第二个Survivor区的大小
  • S0U:新生代第一个幸存区的使用大小
  • S1U:新生代第二个幸存区的使用大小
  • EC:Eden区的大小
  • EU:Eden区的使用大小
  • OC:老年代的大小
  • OU:老年代的使用大小
  • MC:方法区(元空间)大小
  • MU:方法区(元空间)使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • YGC:年轻代垃圾回收次数(从当前进程启动开始至当前时间发生的YGC)
  • YGCT:年轻代垃圾回收消耗时间,单位s(从当前进程启动开始至当前时间发生的YGCT)
  • FGC:老年代垃圾回收次数(从当前进程启动开始至当前时间发生的FYGC)
  • FGCT:老年代垃圾回收消耗时间,单位s(从当前进程启动开始至当前时间发生的FYGCT)
  • GCT:垃圾回收总消耗时间,单位s(从当前进程启动开始至当前时间发生的GCT)

堆内存统计

bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gccapacity 23852
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC 
 19456.0 315392.0  19456.0 2048.0 2048.0  15360.0    39936.0   630784.0    39936.0    39936.0      0.0 1056768.0   4480.0      0.0 1048576.0    384.0      0     0
[rd@VM-8-12-centos project]$ 
  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0C:第一个Survivor大小
  • S1C:第二个Survivor的大小
  • EC:Eden区的大小
  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • OC:当前老年代大小
  • MCMN:最小元数据容量
  • MCMX:最大元数据容量
  • MC:当前元数据空间大小
  • CCSMN:最小压缩类空间大小
  • CCSMX:最大压缩类空间大小
  • CCSC:当前压缩类空间大小
  • YGC:年轻代gc次数
  • FGC:老年代GC次数

新生代垃圾回收统计

bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gcnew 23852
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT  
2048.0 2048.0    0.0    0.0 15  15    0.0  15360.0   4302.9      0    0.000
  • S0C:第一个Survivor的大小
  • S1C:第二个Survivor的大小
  • S0U:第一个Survivor的使用大小
  • S1U:第二个Survivor的使用大小
  • TT:对象在新生代存活的次数
  • MTT:对象在新生代存活的最大次数
  • DSS:期望的幸存区大小
  • EC:Eden区的大小
  • EU:Eden区的使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间

新生代内存统计

bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gcnewcapacity 23852
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC 
   19456.0   315392.0    19456.0 104960.0   2048.0 104960.0   2048.0   314368.0    15360.0     0     0
  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0CMX:第一个Survivor区最大大小
  • S0C:第一个Survivor区当前大小
  • S1CMX:第二个Survivor区最大大小
  • S1C:第二个Survivor区当前大小
  • ECMX:最大Eden区大小
  • EC:当前Eden区大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代回收次数

老年代垃圾回收统计

bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gcold 23852
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT     GCT   
  4480.0    779.8    384.0     76.0     39936.0         0.0      0     0    0.000    0.000
  • MC:方法区大小
  • MU:方法区使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

老年代内存统计

bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gcoldcapacity 23852
   OGCMN       OGCMX        OGC         OC       YGC   FGC    FGCT     GCT   
    39936.0    630784.0     39936.0     39936.0     0     0    0.000    0.000
  • OGCMN:老年代最小容量
  • OGCMX:老年代最大容量
  • OGC:当前老年代大小
  • OC:老年代大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

元数据空间统计

bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gcmetacapacity 23852
   MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC    FGCT     GCT   
       0.0  1056768.0     4480.0        0.0  1048576.0      384.0     0     0    0.000    0.000
  • MCMN:最小元数据容量
  • MCMX:最大元数据容量
  • MC:当前元数据空间大小
  • CCSMN:最小压缩类空间大小
  • CCSMX:最大压缩类空间大小
  • CCSC:当前压缩类空间大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间
bash 复制代码
[rd@VM-8-12-centos project]$ jstat -gcutil 23852
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00  28.01   0.00  17.41  19.80      0    0.000     0    0.000    0.000
  • S0:幸存1区当前使用比例
  • S1:幸存2区当前使用比例
  • E:伊甸园区使用比例
  • O:老年代使用比例
  • M:元数据区使用比例
  • CCS:压缩使用比例
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

常见问题

一 CPU使用率持续99.9%☆☆☆☆☆

实例代码:

java 复制代码
package com.ddu.jvm;

/**
 * CPU飙高
 *
 * @author wucj
 * @date 2024/04/16
 */
public class CpuTopThreadHelper {
    public static void main(String[] args) {
        run();
    }

    private static void run() {
        CpuTopThreadHelper helper = new CpuTopThreadHelper();
        while (true) {
            helper.sum();
        }
    }

    private static int sum() {

        int a = 100;
        int b = 200;
        int c = (a + b) * 325;
        return c;
    }
}

1.1 找到当前机器上运行的java进程号

1.2 查看当前进程下线程占用cpu情况

top命令查看当前进程下线程占用cpu情况

top -p 3305

1.3 查看当前进程下线程的cpu占用情况

在上述步骤(top -p pid)基础上按大写H,查看当前进程下线程的cpu占用情况

从上述打印结果看,当前进程 (pid=3305)下操作系统线程号 =3306的线程,占用cpu99.9%;

因为jstack中打印的操作系统线程号为十六进制的,因此需要将操作系统下的线程号转成十六进制;

  • 操作系统进程号=3306转成十六进制=cea

1.4 查看当前进程下指定操作系统线程号占用内存情况

通过jstack命令查看当前进程下指定操作系统线程号占用内存情况

jstack 3305 |grep cea -C 10

通过上述打印的日志,可以看到如下代码占用长时间占用cpu

 at com.ddu.jvm.CpuTopThreadHelper.run(CpuTopThreadHelper.java:17)

二 排查死锁 ☆☆☆☆☆

源码:

java 复制代码
package com.ddu.jvm;

/**
 * @date 2024/04/16
 */
public class DeadThreadHelper {
    private static Object obj1 = new Object();

    private static Object obj2 = new Object();
    private static Object obj3 = new Object();
    private static Object obj4 = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (obj1) {
                System.out.println(Thread.currentThread().getName() + " locked obj1");
                sleep(5);
                synchronized (obj2) {
                    System.out.println(Thread.currentThread().getName() + " release obj1");
                }
            }

        }).start();

        new Thread(() -> {
            synchronized (obj3) {
                System.out.println(Thread.currentThread().getName() + " locked obj3");
                sleep(5000000);
            }

        }).start();

        new Thread(() -> {
            synchronized (obj2) {
                System.out.println(Thread.currentThread().getName() + " locked obj2");
                sleep(5);
                synchronized (obj1) {
                    System.out.println(Thread.currentThread().getName() + " release obj2");
                }
            }

        }).start();

        new Thread(() -> {
            synchronized (obj4) {
                System.out.println(Thread.currentThread().getName() + " locked obj4");
                sleep(5000000);
            }

        }).start();
    }

    private static void sleep(int seconds) {
        try {
            Thread.sleep(seconds * 1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
bash 复制代码
jstack 23852 > xxxx.info

分析:从上述jstack打印的进程锁占用情况分析,其中Thread-2 Thread-1互相持有对方需要的锁,造成了死锁

|----------|------------------------|------------------------|
| 线程 | 持有锁-locked | 等待锁-waiting to lock |
| Thread-2 | 0x00000000ecc5bed8 | 0x00000000ecc5bec8 |
| Thread-0 | 0x00000000ecc5bec8 | 0x00000000ecc5bed8 |

三、内存泄露是如何发生的?

比如日常开发中,我们可能会使用hashmap,不断往里面放缓存数据,但是忘记考虑这个map的容量问题,结果这个缓存map越来越大,持续占用着老年代的很多空间,时间长了就会导致频繁的full gc,这就是一种内存泄漏的场景;

对于一些老旧数据没有及时清理导致一直占用着宝贵的内存资源,时间长了除了导致full gc,还有可能导致OOM。这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如Guava cache、Redis等;

四、case:系统频繁Full GC导致系统卡顿是怎么回事?

首先考虑是否有大对象查询在周期性执行,导致老年代内存周期性被占用,导致Full GC

相关推荐
昙鱼8 分钟前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
eternal__day8 分钟前
数据结构(哈希表(中)纯概念版)
java·数据结构·算法·哈希算法·推荐算法
天之涯上上12 分钟前
JAVA开发 在 Spring Boot 中集成 Swagger
java·开发语言·spring boot
2402_8575834913 分钟前
“协同过滤技术实战”:网上书城系统的设计与实现
java·开发语言·vue.js·科技·mfc
白宇横流学长14 分钟前
基于SpringBoot的停车场管理系统设计与实现【源码+文档+部署讲解】
java·spring boot·后端
APP 肖提莫17 分钟前
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
java·前端·算法
kirito学长-Java19 分钟前
springboot/ssm太原学院商铺管理系统Java代码编写web在线购物商城
java·spring boot·后端
爱学习的白杨树20 分钟前
MyBatis的一级、二级缓存
java·开发语言·spring
OTWOL25 分钟前
两道数组有关的OJ练习题
c语言·开发语言·数据结构·c++·算法
问道飞鱼29 分钟前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js