JVM调优基础——运行参数、内存分析命令、内存分析工具

不多BB,开门见山

-D<name>=<value>

传入参数作为系统的参数属性,项目中可以根据这个命令传入文件路径去选择不同的配置文件,选择是开发环境配置还是测试环境配置、还是线上环境配置

java -version

我们看到上面的-server,java有两种运行模式:client和server,启动时client比server快,但是启动完成后server的比client性能更好,不同的模式默认的jvm参数和GC收集器也会不同,生产环境选择server,64位操作系统默认只有server
HotSpot也会在启动的时候会根据自身版本以及机器的硬件性能自动选择运行模式,比如J2SE会检测主机是否有至少2个CPU和至少2GB的内存,所以到底选择什么模式不需要我们关心

-D<name>=<value> 传入参数作为系统的参数属性

我们给tomcat设置一个UTF-8属性

后台输出日志变为了中文

public class Test {
    public static void main(String[] args) {
        String value = System.getProperty("file.encoding");
        System.out.println(value);
    }
}

非标准参数

-X 参数

执行下面的命令

java -X

-Xint、-Xcomp、-Xmixed

jvm的编译有三种模式:

  • 解释模式(interpreted mode):源程序输入到计算机后,解释程序将程序逐句翻译,翻译一句执行一句边翻译边执行
  • 编译模式(compiled mode):把源程序全部翻译成目标代码后才运行目标代码
  • 混合模式(mixed mode):将解释模式和变异模式进行混合使用,有JVM自己决定,这是JVM的默认模式,也是推荐模式
  • -Xint参数:在解释模式(interpreted mode)下,-Xint标记会强制JVM执行所有的字节码,jvm不会把代码全部编译完再运行而是一边编译一边运行,这当然会降低运行速度,通常低10倍或更多。

  • -Xcomp参数:在编译模式(compiled mode)下,-Xcomp参数与-Xint正好相反,JVM在第一次使用时会把所有的字节码编译成本地代码,把代码一次性编译完了才运行,从而带来最大程度的优化。

  • -Xmixed参数:-Xmixed是混合模式(mixed mode),将解释模式和变异模式进行混合使用,有JVM自己决定,这是JVM的默认模式,也是推荐模式

示例 使用 -showversion可以在编译时查看版本信息

以下使用不一的模式编译

-XX参数

-XX参数是主要非标准参数的高级选项,主要用于jvm的调优使用,是jvm参数学习的重点
-XX有以下几种类型:

  • 1、boolean类型
  • 2、非boolean类型
  • 3、-Xms、-Xmx、-Xmn、-Xss

boolean类型:Boolean类型-XX参数

公式:-XX:+V 、-XX:-V (+表示开启,-表示关闭)
例如:
是否使用串行垃圾收集器:
-XX:+UseSerialGC(开启)
-XX:-UserSerialGC(关闭)

开启打印jvm的所有内存参数:
-XX:+PrintFlagsFinal

非boolean类型:Boolean类型-XX参数

公式: -XX: key(属性)= value(属性值)
例如:设置元空间大小
 -XX:MetaspaceSize=21807104

-Xms、-Xmx:初始化堆内存参数和最大堆内存参数(一般-Xms、-Xmx两个参数会配置相同的值,优点是:能够在Java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源)

-Xmn、-Xss:年轻代大小和每个线程的堆栈大小

-Xms:Java虚拟机堆区内存初始内存分配的大小,等价-XX:InitialHeapSize=xxx 例如:-Xms512M 设置堆区的初始化内存为512M
-Xmx:Java虚拟机堆区内存可被分配的最大上限,等价-XX:MaxHeapSize=xxx 例如:-Xmx512M 设置堆区的最大内存为512M
-Xmn:年轻代大小例如:-Xmn2g 设置年轻代大小为2G
-Xss:每个线程的堆栈大小 例如:-Xss128k 设置每个线程的堆栈大小为128k

jvm参数查询及内存分析及分析工具的使用(重点内容)

一:-XX:+Print (了解即可)

-XX:+PrintFlagsFinal可以查看jvm的参数设置

示例:

cmd控制台输入:

java -XX:+PrintFlagsFinal -version

我们看到打印了所有的jvm运行的参数

其中 :=xxx 代表被修改过的值

java -XX:+PrintCommandLineFlags -version

这个参数比较有用,只打印一部分关键参数

二、jinfo -flags/-flag (了解即可)

jinfo可以查看运行于jvm的进程的jvm参数设置

1、jinfo -flag xxxx(jvm程序的进程号) 可以查看某个进程的jvm参数

例如:
查看服务器某个jar包的jvm参数,jar包对应的进程号是xxxx

jinfo -flags xxxx

先使用netstat 查询jar端口号对应的进程号xxxx,然后使用进程号jinfo -flags xxx

netstat -anp|grep xxxx 

对应的进程号是61924

jinfo -flags 61924
2、jinfo -flag xxxx xxxx(jvm程序的进程号) 可以查看某个进程的jvm参数某个参数
jinfo -flag InitialHeapSize 61924

三:jstat(了解即可)

Jstat是JDK自带的一个轻量级小工具,可以对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。
jstat -options xxxx(jvm程序的进程号)
可以列出当前JVM版本支持的选项,常见的有

  • -class (类加载器)
  • -compiler (JIT)
  • -gc (GC堆状态)
  • -gccapacity (各区大小)
  • -gccause (最近一次GC统计和原因)
  • -gcnew (新区统计)
  • -gcnewcapacity (新区大小)
  • -gcold (老区统计)
  • -gcoldcapacity (老区大小)
  • -gcpermcapacity (永久区大小)
  • -gcutil (GC统计汇总)
  • -printcompilation (HotSpot编译统计)

示例:
查看Tomcat启动的编译情况

jstat -class 20428

查看对应jar包的垃圾回收情况

jstat -gcutil 61924
监控jar包的gc回收情况,这里每1000毫秒打印一次gc回收消息 jstat -gcutil 61924 1000

参数太多了,下面找到了CSDN的博客,里面有这个命令的各个参数介绍,自己细品吧

https://blog.csdn.net/zhaozheng7758/article/details/8623549?biz_id=102&utm_term=jstat%E5%91%BD%E4%BB%A4%E8%AF%A6%E8%A7%A3&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-8623549&spm=1018.2118.3001.4187

四:jmap(分析内存泄漏的基石,建议掌握)

jmap也jdk自带的工具之一,它可以输出所有内存中对象,主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节,并且可以将JVM 中的heap以二进制输出成文本

jmap -heap xxx(进程号) 打印heap的概要信息,GC使用的算法,heap的配置及heap内存空间使用情况
jmap -histo[:live] xxx(进程号) 打印每个class的实例数目,内存占用,类全名信息.如果live子参数加上后,只统计活的对象数量.

我们加上live参数,只查询存活对象的情况

-dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件, live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.
-jhat -port xxxx(端口) xxx.xxx(hprof文件) 使用这个命令然后使用浏览器访问相应端口就可以查看这个二进制文件
手动导出

如:jmap -dump:format=b,file=head.log 4579

oom时自动导出:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/log/sdc

jhat -port 8999 head.log 查看文件

然后我这边是访问这个地址就可以看到相应的jmap信息http://192.168.200.130:8999/

下面有几个有意思的功能,

例如:使用OQL查询字符串长度大于100的

查出来的结果

点进去一个,这个是我前天晚上学习的dubbo的URL

jmap除了可以手动导出外,也可以设置自动导出

设置参数,当内存溢出后自动导出映像文件

  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=./ -----------------------(导出到程序的当前路径)

写一段快速内存溢出的代码

然后设置最大堆内存和最小堆内存设置得小一些,接着设置生产的hprof文件的生成位置

执行程序看到内存溢出,然后打印出来了

生产的文件

五:MAT(分析内存泄漏的神器,建议掌握)

MAT是一款非常强大的内存分析工具,在Eclipse中有相应的插件,同时也有单独的安装包。在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。
这个软件需要下载,各位自行百度下载
下载后是长这样的:


我桌面有一份hprof文件,记得用jmap生成的文件一定要hprof结尾

利用MAT打开它:

选择Leak Suspercts Report 这个的意思内存泄漏报告

确定后进入这个界面default_report,这个界面帮我们列出来了几个可能有问题的类,点击下面Details »就可以查看详情

下面的点击Overview,这个界面的有以下几个重要的选项:

  • Histogram:Histogram帮我们列出了堆内存对象信息、对象个数、对象大小
  • Dorminator Tree:Dorminator Tree这个选项帮我们列出了大对象的信息

MAT分析实战

我们利用这段代码生产的hprof进行实战

default_report的分析

打开这个hprof文件进入default_report界面,它已经帮我们列出了可疑的问题,事实证明它的怀疑是正确的

第一段表示本地变量占用了很大的堆内存空间,第二段看出来占据这个空间的是一个LinkedList的集合
我们点击下面的Details »进入详细分析

点击Details »进入详细分析,如图:
我们可以看到这里显示程序产生的大量的 LinkedList对象

往下看我们看到这里帮我们列出了对象产生的数量,对象的类型,对象占用内存的大小

Dorminator Tree的分析

我们利用Dorminator Tree分析大对象,点击进入Dorminator Tree得到的列表如下:
可以看到排在第一的线程占据了大量的空间

点击进入查看:
我们看到这个线程占据的哪个对象比较大,都已经从大到小帮我们排好了,我看到这个LinkedList集合占据了大量的空间

点到这个集合中我可以看到集合中是装载的是什么对象,对象成员变量的类型,对象的占用的内存大小一目了然

Histogram的分析

我们利用Histogram可以查看所有对象的数量及占用的内存

如图一目了然:

六:jstack(查看线程堆栈信息专用,需要比较牛批的技能才看得懂)

注意:千万不要jstack和jstat弄混了
jstack用于打印出给定的java进程ID或core file或远程调试服务的Java进程内的线程堆栈信息
命令格式:

jstack [option] pid
jstack [option] executable core
jstack [option] [server-id@]remote-hostname-or-ip

实战

我先写一段死锁代码

/**
 * 死锁
 * tzb
 */
public class SiSuo {
    private final static Object lock1=new Object();
    private final static Object lock2=new Object();

    public void getLock1(){
        synchronized (lock1){
            System.out.println(Thread.currentThread().getName()+"获取到了【lock1】业务执行中..........");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"等待【lock2】.................");
            getLock2();
        }
    }

    private void getLock2() {
        synchronized (lock2){
            System.out.println(Thread.currentThread().getName()+"获取到了【lock2】业务执行中..........");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"等待【lock1】.................");
            getLock1();
        }
    }

    public static void main(String[] args) {
        final SiSuo siSuo=new SiSuo();
        new Thread(()->{
           while (true){
              siSuo.getLock1();
           }
        }).start();
        new Thread(()->{
            while (true){
                siSuo.getLock2();
            }
        }).start();
    }
}

把代码考到服务器上,用最牛批的javac编译

出现了SiSuo的进程

注意的地方,我已经全部全出来了,我们看到线程0和线程1都BLOCKED了,然后他们都在waiting着对方的锁,你在看着图细细品

上图往下拉,deadlock,waiting。各种复杂的单词,信息线程的信息比较全,你细品

七:jvisualvm(究极武器,前面的工具能做的它都能做)

JVisualVM是JDK自带的一款全能型性能监控和故障分析工具,包括对CPU使用、JVM堆内存消耗、线程、类加载的实时监控,内存dump文件分析,垃圾回收运行情况的可视化分析等,对故障排查和性能调优很有帮助。在windows中安装JDK后,VisualVM位于%JAVA_HOME%/bin/下,直接执行jvisualvm.exe即可

在jdk的bin目录下

双击打开
里面有本机JVM参数信息,还要idea的,Tomcat,有监视,线程,概述,抽样器的选项

监视选项

监视选项中信息,有很多如下图

image.png
还有其他选项,百度自己研究吧,直接略过了

远程连接Tomcat

连接远程的Tomcat要在catalina.sh 中加上这个配置

CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=192.168.200.130 -Dcom.sun.management.jmxremote.port=8111 -Dcom.sun.management.jmxremote.rmi.port=8111 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

企业级监控工具:prometheus(普罗米修斯)+Grafana

现在云上应用大部分使用的是prometheus+Grafana
参考:https://blog.csdn.net/duysh/article/details/104247956

prometheus

引入依赖:

 <dependency>
          <groupId>io.micrometer</groupId>
         <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
          <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

下载prometheus
https://prometheus.io/download/
下载安装好后配置项目的yml

management:
  endpoints:
    web:
      exposure:
        include: 'info,health,prometheus'
  endpoint:
    prometheus:
      enabled: true
    metrics:
      enabled: true
  metrics:
    tags:
      application: ${spring.application.name}
    export:
      prometheus:
        enabled: true

配置prometheus的yml

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'test-pro'
    scrape_interval: 5s
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: [ 'localhost:19090' ]

访问 localhost:9090


下载安装Grafana
https://grafana.com/grafana/download
如何配置整合prometheus请参考:https://blog.csdn.net/duysh/article/details/104247956
Grafana的端口是3000,默认用户名和密码:admin/admin
Grafana的监控,非常的全面:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342