码迷,mamicode.com
首页 > 其他好文 > 详细

jvm性能监控与GC调优

时间:2018-11-02 01:55:21      阅读:379      评论:0      收藏:0      [点我收藏+]

标签:rop   too   初始   文件名   site   tag   boolean   MIXED   sha   

目录

一 提出问题

问题

  1. 生产环境发生了内存溢出该如何处理?
  2. 生产环境应该给服务器分配多少内存?
  3. 如何对垃圾收集器的性能进行调优?
  4. 生产环境CPU负载飙升如何处理?
  5. 生产环境应该给应用分配多少线程?
  6. 如何不加log就确定是否执行了某一行代码?
  7. 如何不加log就能实时查看某个方法的参数值和返回值?
  8. JVM的字节码
  9. 循环体中用"+"做字符串拼接为什么效率低,“+”的底层一定是StringBuilder.append()吗?
  10. String常量池
  11. i++与++i哪种写法效率更高?
  12. jvm监控和调试工具的使用
  13. 理解JVM的GC机制,学会GC调优
  14. TomCat性能监控与调优

二 基于JDK命令行工具的监控

1. JVM的三种参数类型

1.1 标准参数

jvm的标准参数,在jvm各个版本中基本不变。
使用java获取所有标准参数

  • -help
  • -server -client ^①^
  • -version -showversion
  • -cp -classpath

使用java -version命令可以输出java的版本信息,从第4行可以看到:

JVM的名字(HotSpot)、类型(Server)、build ID(24.79-b02),JVM以混合模式(mixed mode)在运行,这是HotSpot默认的运行模式,意味着JVM在运行时可以动态的把字节码编译为本地代码。关于切换类型(Server Client)以及二者的区别详见参考文档1。

C:\Users\Administrator>java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

1.2 X 参数

非标准化参数,在jvm各个版本中会有一些小变化。
使用java -X获取所有X参数

  • -Xint:JVM以解释方式执行所有的字节码,会显著降低运行速度

  • -Xcomp:JVM在第一次使用时就把所有字节码编译为本地代码

  • -Xmixed:混合模式(默认),由jvm自己决定是否编译为本地代码,会将字节码中多次被调用的部分便以为本地代码以提高执行效率;被调用很少的方法会在解释模式下执行,减少编译和优化成本

    # 默认是mixed mode(混合模式),在java的版本信息中显示
    C:\Users\Administrator>java -version
    java version "1.8.0_51"
    Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
    Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)
    
    # 修改为interpreted mode(解释执行)
    C:\Users\Administrator>java -Xint -version
    java version "1.8.0_51"
    Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
    Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, interpreted mode)
    
    # 修改为compiled mode(编译执行)
    C:\Users\Administrator>java -Xcomp -version
    java version "1.8.0_51"
    Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
    Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, compiled mode

    测试三种JVM工作模式可以使用死循环代码测试运行时间,具体命令如下:

    javac HelloWorld.java
    java -Xint HelloWorld
    java -Xcomp HelloWorld
    java -Xmixed HelloWorld

1.3 XX 参数

? 非标准化参数,在jvm各个版本中变化较大,主要用于jvm调优和debug,也是最常用的参数
? 使用java -XX:+PrintFlagsFinal -version > flags1.txt命令获取所有XX参数^③^,大约有700+参数

  • Boolean 类型

    格式:-XX:[+-]

    举例:-XX:UseConcMarkSweepGC 启用CMS垃圾收集器
    ? -XX:+UseG1GC 启用G1垃圾收集器

  • K-V 类型

    格式:-XX:[+-]

    举例:-XX:InitialHeapSize=3116367872 初始化堆内存,常用-Xms512m表示,不要误认为是X参数

    -XX:MaxHeapSize=3116367872 最大堆内存,常用-Xmx1024m表示
    -XX:ThreadStackSize=1024 线程堆栈大小,常用-Xss128k表示
    -XX:MaxGCPauseMills=500 表示GC的最大停顿时间是500ms
    -XX:GCTimeRatio=19

1.4 常用命令

  • jps:查看java进程。

    -m显示传递给main方法的参数。

    -l显示应用程序main类的完整包名称或应用程序的JAR文件的完整路径名。

    -v显示传递给JVM的参数

  • jinfo:查看Java进程的修改过的jvm参数

    jinfo -flags 2345查看进程id为2345的jvm参数

    jinfo -flag MaxheapSize 2345查看进程id为2345的jvm参数最大堆内存MaxheapSize的值

2. jstat查看虚拟机统计信息

2.1 类加载信息

# 进程id21640 输出间隔1000ms 输出次数3
# Loaded加载类的数量  Bytes加载的kB数  Unloaded:卸载的类数  Bytes:卸载的Kbytes数Time:执行类加载和卸载操作所花费的时间
C:\Users\Administrator>jstat -class 21640 1000 3
Loaded  Bytes  Unloaded  Bytes     Time
  3010  5823.5       52    77.1       3.48
  3010  5823.5       52    77.1       3.48
  3010  5823.5       52    77.1       3.48

2.2 垃圾回收信息

# 输出结果各项指标详见参考文档2
C:\Users\Administrator>jstat -gc 21640
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
18432.0 19456.0  0.0    0.0   77824.0  48459.9   126976.0   10715.1   19456.0 18288.3 2304.0 1968.8     13    0.204   1      0.139    0.342

2.3 JIT编译信息

# 查看JIT编译信息
C:\Users\Administrator>jstat -compiler 21640
Compiled Failed Invalid   Time   FailedType FailedMethod
    3054      1       0    12.35          1 org/apache/tomcat/util/IntrospectionUtils setProperty

3. jmap + MAT分析内存溢出 [实战]

技术分享图片

3.1 模拟内存溢出

/**
 * Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit 
 * exceeded
 */
private static void heapSize2() {
    Map<Object,Object> map = new HashMap();
    Random r = new Random();
    Integer i = 0;
    while (true) {
        map.put(i++, "aaa");
    }
}

/**
 * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 */
private static void heapSize() {
    List<String> names = new ArrayList();
    for (;;) {
        names.add("bbb");
    }
}

3.2 导出内存影像文件

  1. 使用命令行参数,内存溢出时自动导出(最常用)

    -XX+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./

    另外也可以使用-Xrunhprof:head=site 参数生成java.hprof.txt 文件,不过这样会影响 JVM的运行效率,不建议在生产环境中使用

  2. 使用jmap命令手动导出 jmap -dump:format=b,file=heap.hprof 2345

format表示以二进制形式导出,file表示文件名称,2345表示要操作的java进程id

更多jmap命令选项详见参考文档2

  1. 使用JConsole生成Dump文件技术分享图片

3.3 使用MAT分析dump文件

首先下载MAT (Memory Analyzer),参考MAT教程^④⑤^,分析dump文件

技术分享图片

Shallow Heap表示对象本身所占的内存^④⑤^,Retained Heap表示对象和对象中的引用总共占的内存大小。比如User对象包含name属性,会引用String对象。

排除虚引用,根据GC Roots查看对象引用技术分享图片

技术分享图片

4. jstack分析死循环与死锁 [实战]

CPU负载(load average)^⑦^和使用率过高,很可能的原因就是死循环。

  1. 首先可以使用top命令查看占用CPU最高的进程信息,获取进程pid

  2. 使用jstack [pid] > myStack.txt命令打印指定java进程的所有线程堆栈信息

  3. 使用Linux命令top -p [pid] -H监控指定进程中所有线程信息,然后找到CPU占用率高的线程nid(windows中可以process exlporer工具查看指定进程中所有线程信息

    技术分享图片

    如图可以看到8247(0x2037)线程的CPU使用率达到了96.7%,此时我们就可以确认是这个线程的问题了

  4. 在myStack.txt文件中查看线程id为nid的堆栈信息(myStack.txt线程id是16进制,top -p命令线程id是10进制),就可以找到导致CPU飙升的线程的堆栈信息了,然后分析问题,解决问题。技术分享图片

    如图可以看到8247(0x2037)线程的堆栈信息

  5. 另外jstack命令能自动检测到死锁,在myStack.txt末尾会有 “found 1 deadlock”,也会打印死锁线程的相关信息,根据这些信息就可以分析问题,解决问题了。另外,也可以使用JConsole来检测死锁

技术分享图片

三 基于JVisualVM的可视化监控

四 基于Btrace的监控调试

4.1 简介

BTrace可以动态地向目标应用程序的字节码注入追踪代码,用到的技术JavaCompilerAPI,JVMTI,Agent,Instrumentation+ASM

1、接口性能变慢,分析每个方法的耗时情况;

2、当在Map中插入大量数据,分析其扩容情况;

3、分析哪个方法调用了System.gc(),调用栈如何;

4、执行某个方法抛出异常时,分析运行时参数;

4.2 环境准备

下载JVisualVM BTrace插件https://visualvm.github.io/pluginscenters.html

下载BTracehttps://github.com/btraceio/btrace/releases/tag/v1.3.11

jar包

4.2 使用BTrace

4.3.1 简单使用

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

@BTrace
public class PrintArgSimple {
    // 在Ch4Controller类的fun1方法的入口处(ENTRY)进行追踪
    @OnMethod(clazz = "com.imooc.monitor_tuning.chapter4.Ch4Controller", 
            method = "fun1", location = @Location(Kind.ENTRY))
    public static void anyRead(@ProbeClassName String className, @ProbeMethodName String methodName, AnyType[] args) {
        BTraceUtils.printArray(args);  //fun1方法的所有参数
        BTraceUtils.println(className + ":" + methodName);
    }
}

btrace [pid] MyBtrace.java

4.3.2 拦截构造函数,重载方法

4.3.3 拦截返回值,异常,行号

4.3.4 拦截复杂参数,环境变量

4.3.5 注意事项

五 Tomcat性能监控与调优

七 JVM层GC调优

八 JVM字节码与Java代码层调优

九 总结

参考文档:

  1. 关于JVM的类型和模式
  2. Java命令行工具帮助文档 - Oracle
  3. 打印所有XX参数
  4. 生成dump文件与MAT的使用
  5. MAT使用教程
  6. MAT中文文档
  7. 理解Linux系统负荷 - 阮一峰
  8. JVisual VM使用教程
  9. 如何在生产环境使用Btrace进行调试 - 占小狼

jvm性能监控与GC调优

标签:rop   too   初始   文件名   site   tag   boolean   MIXED   sha   

原文地址:https://www.cnblogs.com/mkwfqd/p/9893546.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!