JVM调优工具

查看本地java的安装目录

// 方式1
U-91KQD9WX-1828:~ xxxxx$ echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home

// 方式2
U-91KQD9WX-1828:~ xxxxx$ /usr/libexec/java_home
/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home

// 查看已安装的JDK
U-91KQD9WX-1828:~ xxxxxx$ /usr/libexec/java_home -V
Matching Java Virtual Machines (4):
    17.0.15 (arm64) "Oracle Corporation" - "Java SE 17.0.15" /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home
    1.8.291.10 (x86_64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
    1.8.0_291 (x86_64) "Oracle Corporation" - "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home
    1.8.0_151 (x86_64) "Oracle Corporation" - "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home
/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home

JVM调优命令

1. Jps

列出当前系统中所有的 Java 进程(包括 JVM 启动的应用程序和内置服务)。它类似于 Linux 的 ps 命令

2. Jmap

1. 查某个进程的内存信息,实例个数以及占用内存大小

  • num : 序号
  • instances : 实例数量
  • bytes : 占用空间大小
  • class name: 类名称

2. 堆信息

jmap -heap ${进程号} : 查看这个进程使用的堆的情况
但是我本地测试时报错 Can't attach to the process. Could be caused by an incorrect pid or lack of privileges.  看堆使用情况可以使用下面的命令代替

U-91KQD9WX-1828:~ xxxxx$ jcmd 62292 GC.heap_info
62292:
 PSYoungGen      total 229376K, used 29044K [0x00000006c0000000, 0x00000006d0000000, 0x00000007c0000000)
  eden space 196608K, 14% used [0x00000006c0000000,0x00000006c1c5d248,0x00000006cc000000)
  from space 32768K, 0% used [0x00000006ce000000,0x00000006ce000000,0x00000006d0000000)
  to   space 32768K, 0% used [0x00000006cc000000,0x00000006cc000000,0x00000006ce000000)
 ParOldGen       total 524288K, used 0K [0x00000004c0000000, 0x00000004e0000000, 0x00000006c0000000)
  object space 524288K, 0% used [0x00000004c0000000,0x00000004c0000000,0x00000004e0000000)
 Metaspace       used 3400K, capacity 4564K, committed 4864K, reserved 1056768K
  class space    used 360K, capacity 388K, committed 512K, reserved 1048576K

3. 堆dump

dump会导致一次Full GC,  STOP THE WORLD.

可以用 jvisualvm命令工具导入该文件做分析

3. Jstack

1. Jstack $进程id  : 查看死锁的状况

源代码:

public class DeadLockTest {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (lock1) {
                try {
                    System.out.println("thread1 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
                synchronized (lock2) {
                    System.out.println("thread1 end");
                }

            }
        }).start();


        new Thread(() -> {

            synchronized (lock2) {
                try {
                    System.out.println("thread2 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {

                }
                synchronized (lock1) {
                    System.out.println("thread2 end");
                }
            }
        }).start();

        System.out.println("main end");

    }
}

使用 jvisualvm自动检测死锁

2. Jstack找cpu占用最高的线程堆栈

思路:进程 -> 线程 -> 线程的具体代码

1. top查看进程使用CPU情况

// top命令查看进程使用CPU情况
192:~ xxxxx$ top

可以看到进程74667 CPU使用占到99.4%, 

2. top 查看具体进程内存情况

使用命令top -pid <pid> ,显示你的java进程的内存情况,pid是java进程号.

3. 按H,获取每个线程的内存情况

但是,在我的mac上这样操作并没有展示线程

4. 找到内存和cpu占用最高的线程tid, 比如是19664

5. tid转换成十六机制得到0x4cd0

6. 执行 jstack <pid>|grep -A 10 4cd0,得到线程堆栈信息中 4cd0 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调用方法。

7. 查看对应的堆栈信息找出可能存在问题的代码

3. 远程连接jvisualvm

启动普通的jar程序JMX端口配置:

java ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60

‐Dcom.sun.management.jmxremote.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false

‐jar microservice‐eureka‐server.jar

PS:

-Dcom.sun.management.jmxremote.port 为远程机器的JMX端口

-Djava.rmi.server.hostname 为远程机器IP

会自动找出本地启动的JVM进程。

4. Jinfo/jcmd

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

jinfo -flags <进程号>

但是使用Jinfo时有报错,暂时替代方案:jcmd <进程号> VM.flags

查看java系统参数

jinfo sysprops <进程号>

有报错,暂时替代方案:

jcmd <PID> VM.system_properties # 查看所有系统属性(等效于 jinfo -sysprops)

5. Jstat(最常用)

jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:

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

(注意:使用的jdk版本是jdk8)

1. 垃圾回收器

jstat -gc <进程号>

S0C:第一个幸存区的大小,单位KB

S1C:第二个幸存区的大小

S0U:第一个幸存区的使用大小

S1U:第二个幸存区的使用大小

EC:伊甸园区的大小

EU:伊甸园区的使用大小

OC:老年代大小

OU:老年代使用大小

MC:方法区大小(元空间)

MU:方法区使用大小

CCSC:压缩类空间大小

CCSU:压缩类空间使用大小

YGC:年轻代垃圾回收次数

YGCT:年轻代垃圾回收消耗时间,单位s

FGC:老年代垃圾回收次数

FGCT:老年代垃圾回收消耗时间,单位s

GCT:垃圾回收消耗总时间,单位s

补充:

 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。

2. 堆内存统计

jstat -gccapacity <进程号>

NGCMN:新生代最小容量

NGCMX:新生代最大容量

NGC:当前新生代容量

S0C:第一个幸存区大小

S1C:第二个幸存区的大小

EC:伊甸园区的大小

OGCMN:老年代最小容量

OGCMX:老年代最大容量

OGC:当前老年代大小

OC:当前老年代大小

MCMN:最小元数据容量

MCMX:最大元数据容量

MC:当前元数据空间大小

CCSMN:最小压缩类空间大小

CCSMX:最大压缩类空间大小

CCSC:当前压缩类空间大小

YGC:年轻代gc次数

FGC:老年代GC次数

3. 新生代垃圾回收统计

jstat -gcnew <进程号>

S0C:第一个幸存区的大小

S1C:第二个幸存区的大小

S0U:第一个幸存区的使用大小

S1U:第二个幸存区的使用大小

TT:对象在新生代存活的次数

MTT:对象在新生代存活的最大次数

DSS:期望的幸存区大小

EC:伊甸园区的大小

EU:伊甸园区的使用大小

YGC:年轻代垃圾回收次数

YGCT:年轻代垃圾回收消耗时间

4. 新生代内存统计

jstat -gcnewcapacity <进程号>

NGCMN:新生代最小容量

NGCMX:新生代最大容量

NGC:当前新生代容量

S0CMX:最大幸存1区大小

S0C:当前幸存1区大小

S1CMX:最大幸存2区大小

S1C:当前幸存2区大小

ECMX:最大伊甸园区大小

EC:当前伊甸园区大小

YGC:年轻代垃圾回收次数

FGC:老年代回收次数

5. 老年代垃圾回收统计

gstat -gcold <进程号>

MC:方法区大小

MU:方法区使用大小

CCSC:压缩类空间大小

CCSU:压缩类空间使用大小

OC:老年代大小

OU:老年代使用大小

YGC:年轻代垃圾回收次数

FGC:老年代垃圾回收次数

FGCT:老年代垃圾回收消耗时间

GCT:垃圾回收消耗总时间

6. 老年代内存统计

jstat -gcoldcapacity <进程号>

OGCMN:老年代最小容量

OGCMX:老年代最大容量

OGC:当前老年代大小

OC:老年代大小

YGC:年轻代垃圾回收次数

FGC:老年代垃圾回收次数

FGCT:老年代垃圾回收消耗时间

GCT:垃圾回收消耗总时间

7. 元数据空间统计

jstat -gcmetacapacity <进程号>

MCMN:最小元数据容量

MCMX:最大元数据容量

MC:当前元数据空间大小

CCSMN:最小压缩类空间大小

CCSMX:最大压缩类空间大小

CCSC:当前压缩类空间大小

YGC:年轻代垃圾回收次数

FGC:老年代垃圾回收次数

FGCT:老年代垃圾回收消耗时间

GCT:垃圾回收消耗总时间

8. 统计 JVM 垃圾回收(GC)内存区域的使用率百分比

jstat -gcutil <进程号>

S0:幸存1区当前使用比例

S1:幸存2区当前使用比例

E:伊甸园区使用比例

O:老年代使用比例

M:元数据区使用比例

CCS:压缩使用比例

YGC:年轻代垃圾回收次数

FGC:老年代垃圾回收次数

FGCT:老年代垃圾回收消耗

JVM运行情况预估

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

分析思路:

1. 确定当前young gc和full gc的频率(jstat -gc,可以看到内存使用情况)

2. 查看当前JVM参数, 结合实际业务情况,确定参数的配置是否合理。比如,当前业务是否产生大对象、是否有长期存活的对象、是否Survivor区太小触发动态年龄判断、老年代空间担保机制?在上述分析的基础上,调整JVM参数做测试

  • 对象动态年龄判断机制导致的full gc较为频繁可以先试着优化下JVM参数,把年轻代适当调大点
  • 触发老年代担保机制,可能导致full gc次数比young gc次数多

3. 借助jmap命令大概看下是什么对象在频繁gc

jmap -histo <进程号>

4. 确定这个对象在代码哪里产生的

  1. 代码里全文搜索生成User对象的地方(适合只有少数几处地方的情况)
  2. 如果生成User对象的地方太多,无法定位具体代码,我们可以同时分析下占用cpu较高的线程,代码肯定会被频繁调用,占用的cpu必然较高。 可以用 jstack或jvisualvm来定位cpu使用较高的代码

阿里巴巴Arthas

Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6+

详见:快速入门 | arthas

arthas启动

选择 进程序号1,进入进程信息操作。

dashboard查看整个进程的运行情况,线程、内存、GC、运行环境

thread可以查看线程详细情况

thread  <线程id> : 查看线程堆栈

thread -b 可以查看线程死锁

 jad加类的全名 可以反编译,这样可以方便我们查看线上代码是否是正确的版本

ongl 直接修改线上变量

GC日志详解

通过分析gc日志,得到关键性指标,分析GC原因,从而调优JVM参数。

GC日志打印配置

打印GC日志方法,在JVM参数里增加如下参数,%t 代表时间,   Tomcat则直接加在JAVA_OPTS变量里。

-Xloggc:./gc-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps 

-XX:+PrintGCTimeStamps -XX:+PrintGCCause  

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10  -XX:GCLogFileSize=100M

本地执行的时候配置VM options, 如果没有这个选项,可以手动增加。

        

        对于gc日志文件,一般是结束工具来分析,这里推荐一个gceasy(https://gceasy.io),可以上传gc文件,然后他会利用可视化的界面来展现GC情况

JVM参数汇总查看

java -XX:+PrintFlagsInitial 表示打印出所有参数选项的默认值

java -XX:+PrintFlagsFinal 表示打印出所有参数选项在运行程序时生效的值

内存泄露

        一般电商架构可能会使用多级缓存架构,就是redis加上JVM级缓存。为了图方便对于JVM级缓存就简单使用一个hashmap,于是不断往里面放缓存数据,但是很少考虑这个map的容量问题,结果这个缓存map越来越大,一直占用着老年代的很多空间,时间长了就会导致full gc非常频繁,这就是一种内存泄漏。

        对于一些老旧数据没有及时清理导致一直占用着宝贵的内存资源,时间长了除了导致full gc,还有可能导致OOM。

        这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值