8.1 jvm资源监控工具

8.1.1jconsole监控工具

jmap:此工具在jdk安装目录的bin文件夹里面

jmap [option]<pid>

例如:jmap -heap 6033

MaxHeapSize =573870912(512.0MB)

jconsole【被取代了】:此工具在jdk安装目录的bin文件夹里面,运行后界面

可以查看内存、线程、类、jvm等相关信息

本地进程访问,选择对应的java程序进程即可

远程访问需要服务器开放对应的端口,启动时,添加一下对应的配置

-Dcom.sum.management.jmxremote

-Djava.rmi.server.hostname=192.168.1.15 这个ip写用于访问的ip,要确定你的jconsole客户端能够通过这个ip访问到服务器

-Dcom.sun.management.jmxremote.port=8999 给jconsole连接的端口

-Dcom.sun.management.jmxremote.rmi.port=9999

-Dcom.sun.management.jmxremote.ssl=false 取消ssl加密

-Dcom.sun.management.jmxremote.autheticate=false 取消用户名密码验证

服务器启动项目时,将这些参数加进去,启动命令为:

java -Xmx2G -Dcom.sun.management.jmxremote

-Dcom.sun.management.jmxremote.port=8999

-Dcom.sun.management.jmxremote.rmi.port=9999

-Djava.rmi.server.hostname=192.168.1.5

-Dcom.sun.management.jmxremote.ssl=false

-Dcom.sun.management.jmxremote.authenticate=false

-jar -Dspring.datasource.url="jdbc:mysql://192.168.1.7:3306/novel-plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true" -Dspring.datasource.username=root -Dspring.datasource.password=zhaoWEILI1314520@ novel-front-3.5.4.jar

在客户端jdk安装目录下的 bin目录,打开jconsole

注意:连不上,可以关闭防火墙,前面介绍别的工具安装,配置防火墙,让防火墙放行单个端口的设置去操作,需要将8998端口和9999端口一起给它配置了

firewall-cmd --add-port=8999/tcp --permannet

firewall-cmd --add-port=9999/tcp --permannet

Firewall-cmd --reload #重载防火墙

建议直接关闭防火墙 systemctl stop firewalld

对服务器进行压测,检测在测试过程数据的变成情况

8.1.2 jvisualvm监控工具(取代了jsconsole)

jvisualvm功能比jconsole强大

官网下载地址:https://visualvm.github.io/releases.html

解压后,在bin目录中 找到.exe文件,打开它,创建远程连接

这个工具和jconsole相关,服务器启动项目时,将jscnsole这些参数加进去;

添加jmx,增加端口号8999

接下来就可以查看远程主机的一些信息

被监控服务器上启动jstatd,jvm jstatd Daemon守护进程,一个RMI(Remote Method Invocation)服务器程序,用于监控本地所有jvm从创建开始,知道销毁整个过程中的资源使用情况,同时 提供接口给监控工具 (如这里的VisualVM)让工具能连接到本地所有的jvm

该命令在jdk的bin目录下:

创建安全策略文件:在jdk的bin目录下新建 jstatd.all.policy的文件

内容如下:

grant codebase "file:${java.home}/../lib/tools.jar" {

permission java.security.AllPermission; };

注意:服务器记得配置JAVA_HOME环境变量

启动:

jstatd -J-Djava.security.policy=/usr/local/jdk/bin/jstatd.all.policy -J-Djava.rmi.server.hostname=192.168.1.5

添加jstatd

注意:jvisualvm此功能要求服务器启动程序时和jconsole一样的请求参数

特别注明:jdk1.8,在bin目录,自带中文版的该工具,在jdk的bin目录下

在测试过程中可以通过抽样器对测试进行过程进行分析,找到最耗费cpu和内存的方法,从而协助开发有针对性的对代码进行优化,而不是优化所有代码。

8.1.3 jvm集群监控体系

基于prometheus+grafana

官网下载地址:https://github.com/prometheus/jmx_exporter

将jar包传到java应用服务器上,在jar同级目录下创建config.yaml

内容为(通用配置):

---

lowercaseOutputLabelNames: true

lowercaseOutputName: true

whitelistObjectNames: ["java.lang:type=OperatingSystem", "Tomcat:*"]

blacklistObjectNames: []

rules:

- pattern: 'java.lang<type=OperatingSystem><>(committed_virtual_memory|free_physical_memory|free_swap_space|total_physical_memory|total_swap_space)_size:'

name: os_$1_bytes

type: GAUGE

attrNameSnakeCase: true

- pattern: 'java.lang<type=OperatingSystem><>((?!process_cpu_time)\w+):'

name: os_$1

type: GAUGE

attrNameSnakeCase: true

- pattern: 'Tomcat<type=Server><>: (.+)'

name: tomcat_serverinfo

value: 1

labels:

serverInfo: "SpringBoot Tomcat"

type: COUNTER

- pattern: 'Tomcat<type=GlobalRequestProcessor, name=\"(\w+-\w+)-(\d+)\"><>(\w+):'

name: tomcat_$3_total

labels:

port: "$2"

protocol: "$1"

help: Tomcat global $3

type: COUNTER

- pattern: 'Tomcat<j2eeType=Servlet, WebModule=//([-a-zA-Z0-9+&@#/%?=~_|!:.,;]*[-a-zA-Z0-9+&@#/%=~_|]), name=([-a-zA-Z0-9+/$%~_-|!.]*), J2EEApplication=none, J2EEServer=none><>(requestCount|processingTime|errorCount):'

name: tomcat_servlet_$3_total

labels:

module: "$1"

servlet: "$2"

help: Tomcat servlet $3 total

type: COUNTER

- pattern: 'Tomcat<type=ThreadPool, name="(\w+-\w+)-(\d+)"><>(currentThreadCount|currentThreadsBusy|keepAliveCount|connectionCount|acceptCount|acceptorThreadCount|pollerThreadCount|maxThreads|minSpareThreads):'

name: tomcat_threadpool_$3

labels:

port: "$2"

protocol: "$1"

help: Tomcat threadpool $3

type: GAUGE

- pattern: 'Tomcat<type=Manager, host=([-a-zA-Z0-9+&@#/%?=~_|!:.,;]*[-a-zA-Z0-9+&@#/%=~_|]), context=([-a-zA-Z0-9+/$%~_-|!.]*)><>(processingTime|sessionCounter|rejectedSessions|expiredSessions):'

name: tomcat_session_$3_total

labels:

context: "$2"

host: "$1"

help: Tomcat session $3 total

type: COUNTER

程序启动时添加对应的agent和配置:

java -javaagent:./jmx_prometheus_javaagent-0.18.0.jar=12345:config.yaml -jar

-Dspring.datasource.url="jdbc:mysql://192.168.1.7:3306/novel-plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true" -Dspring.datasource.username=root -Dspring.datasource.password=zhaoWEILI1314520@ novel-front-3.5.4.jar

java程序启动后,会开放一个12345的端口.通过http去访问这个端口,可以获取jvm的运行状态数据,配置防火墙策略:

firewall-cmd --add-port=12345/tcp --permanent

firewall-cmd reload

接下来:Prometheus配置数据采集,修改yml配置文件,添加监控,配置后重新启动

单独创建了一个任务--jvms,这个工具就会自动的调节前面启动监控数据接口,从而获取jvm运行时的数据

Grafanap配置监控大屏:

在garfana官网下载监控jvm,然后导入json文件,前面有介绍 ,就不写了,

注意点,jvm在导入时,需要主要job名称,要跟prometheus.yml文件中配置的一致,不然会没有数据

对服务器进行压测,使用jvm监控看板,查看jvm资源使用情况

实战演示:启动java程序时,设置堆内存大小为200M,对服务器进行压测

记录测试时间点:2023-03-21 13:56:23 -2023-03-21 13:58:09

其中吞吐量最大为:3.39k,平均3.7k,最小223.20

在这个时间段内,java应用服务器的cpu使用率最大为56.81%,内存20.45%

数据库服务器cpu的使用率最大为1.61%,内存40.04%

总结:cpu,内存,磁盘,网络资源使用率不高,程序出现瓶颈,最大吞吐量3.39k,应为程序的问题,查看jvm信息监控看板,jvm,cpu最高为45%,堆内存61.4%,jvm不存在明显的

某个资源使用率过高问题,相对并发数量设置过小,加大并发数,继续排查问题。

这里将堆内存设置为50M,对比看一下,设置前

将堆内存调增为50M后,使用同样的脚本对服务器进行压测,堆内存基本上满了

8.2GC垃圾回收

8.2.1自动垃圾收集机制

内存不是无穷无尽的,每一个请求处理过程中,都会创建一些对象,占用一些内存。请求

处理完毕,占用内存应该被释放。JVM内置垃圾回收机制,JVM程序里面有一个模块的垃圾回收器。

java垃圾回收过程:标记垃圾,清除垃圾,整理内存

8.2.2分代回收机制

新生代-eden --Xmn

新生代-survivor 1

新生带-survivor 2

老年代-XX:pretenureSizeTHreshold=1024 升级到老年代的对象定义

两大区域--

小范围GC,大范围Full GC 两种

新生代-新对象新数据

老年代-超级大的对象,经历多次新生代GC,依然存在的对象

特殊--永久代【元数据空间】

8.2.3 STW概念(stop the world)

和性能有关:一个请求--处理实际

业务代码执行时间+ GC垃圾回收时间

JVM在GC的时候,业务代码线程,处于停止等待的一个状态,就好比打扫垃圾的时候,总不能边扔边打扫,如果是这样的情况,那么业务代码在执行的过程,就会出现,代码还没执行完毕,数据被当作垃圾清理掉了,找到数据,程序运行就会出错。

8.2.3 minor gc小范围GC

新生代--时间一般比较短

8.2.4 full gc大范围GC

所有的堆空间、元数据空间

导致程序中断的时间比较长

8.2.5垃圾回收器

串行GC:基本不用

并行GC:PS

并发GC:cms G1

实战1:StackOverflowError -栈内存溢出

线程栈内存默认大小1M:每个线程都会分配一个小内存区块,保存线程执行的方法、局部变量的信息。

建议代码优化,:出现场景--递归调用--递归--方法嵌套调用自己

栈--数据结构--后进先出--每一次执行方法之前,把方法信息,压入栈,从上往下执行

递归:方法调用没完毕,又调用自身……,如果长时间不结束,导致栈内存不够

确实有大量递归场景,尝试修改配置,增加JVM每个线程栈内存的大小

-Xss参数:java每个线程的stack大小

jdk5.0以后,每个线程堆栈大小为1M,以前为256K

在相同的物理内存下,减少这个值能生成更多的线程

物内存是固定,增大栈内存后,可运行的线程数变少

实战2:OutOfMemoryError-内存溢出

持久性压力测试,程序内存占用逐步增高,最终挂掉,突然大量并发,内存不够i用

java.lang.OutOfMemoryError:unable to create new native thread:java程序已经达到了它可以启动的线程数的限制

java.lang.OutOfMemoryError:Metaspace

1.8+java.lang.OutOfMemoryError:permGen space

-XX:MaxPermSize=512m

加大元数据空间的内存,或检查元数据空间占是否合理,class类加载的太多,动态创建了很多class类

java.lang.OutOfMemoryError:java heap space

Java.lang.OutOfMemoryError:GC overhead limit exceeded

加大堆内存,或者检查内存占用是否合理

程序调优-获取内存快照-占到占用比较大的对象,让开发修改

JVM在遇见OOM(OutOfMemoryError)时生成内存快照文件:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp

增加这些JVM启动参数

在启动项目时,调整项目的栈内存为2M,堆内存为200M,对服务器进行压测,内存一旦溢出就会保存文件到reading/test目录下

java -Xss2m -Xmx200m

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/reading/test

-javaagent:./jmx_prometheus_javaagent-0.18.0.jar=12345:config.yaml -jar

-Dspring.datasource.url="jdbc:mysql://192.168.1.7:3306/novel-plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true" -Dspring.datasource.username=root -Dspring.datasource.password=zhaoWEILI1314520@ novel-front-3.5.4.jar

此时执行压测脚本,在对应目录下,创建对应的java文件

内存快照分析:

工具MAT -eclipse Memory Analyzer Tools

下载地址:https://www.eclipse.org/mat/downloads.php

下载一个高版本的jdk,我这里下载了1个19的

修改配置文件:MemoryAnalyzer.ini,增加一行

-vm

E:\jdk-19\bin\javaw.exe通过mat打开内存快照 #这个为更改版本jdk的javaw,exe的安装位置

使用该工具,打开从服务器上下载的java_pid2818.hprof文件

首先打开Histogram(直方图),查看每个对象占用了多少内存,但具体是那段代码占用不知道,直方图中,右键,list object菜单中,查看引用对象

with incoming references 表示的是当前查看的对象,被外部应用

with outGoing references 表示的是当前对象,引用了外部对象

接下来看dominator tree(支配树)

当在支配树视图中选中某一对象时,我们还可以通过Path To GC Roots 功能,反向列出该对象到 GC Roots 的引用路径

到了这个程度,开发还找不到,在Reports分析中,Leak Suspects,去找可疑点,点击details

8.3性能稳定性调优

实战:性能测试过程中,吞吐量呈现出波浪状,性能稳定性调优

疑因分析:JVM垃圾回收,导致STW产生时间停顿

波浪小:可能是小规模GC比较多,或者每次GC时间补偿,能接收就行

波浪起伏大,GC时间长,可能是产生了多次Full gc,调整垃圾回收器参数,减少停顿时间

最优先的是让开发从编码的角度去优化代码,减少内存占用、复用内存,90%的情况下,不需要做大量所谓垃圾回收的调整

我们对服务器进行压测

8.3.1垃圾回收器优化思路

并发量不变,垃圾不会减少

垃圾不减少,那就提高垃圾回收的次数,每次少清理一些垃圾,时间则会减少

8.3.2GC是否需要优化

响应时间角度:要求:所有用户请求必须在1000ms内完成,对于响应时间的要求,根据经验来说,我们要求GC占用的时间不超过10%,为了方便理解,假定一次请求最多经过一次GC。也就是一个请求要求1000ms完成,则GC导致STW的时间不能超过100ms,只要在这个范围内,就是符合要求的(也就是无需优化)

耗时=业务代码执行时间+GC-STW时间

吞吐量角度:要求:一定时间内处理完多少此请求,例如:1分钟处理完毕6000此请求,则平均每秒处理100次,追求吞吐量的情况下,我们就不去细说一次GC的时间,我们要统计的就是这段时间内GC消耗的总时间,同样,根据经验值,GC占用的总时间不应超过10%

8.3.4GC相关参数

自适应参数:

单次最大暂停时间(STW时间): -XX:MaxGCPauseMillis=200 JVM尽量保证把每次GC的时间控制在这个时间内

应用程序吞吐量目标:-XX:GCTimeRatio=19 如果参数设置为19,则垃圾回收总时间小于应用程序总运行时间的5%,计算公式:GC总时长限值==1/(1+GCtimeRation)*程序总时长

这两个参数配置后,JVM会根据运行情况,自动动态调整堆中分代各区域的大小。在这种动态调整的情况下,JVM自行选择。

同时配置,优选保证的顺序:最大暂停时间目标>吞吐量目标

分代空间大小配置:-Xmn 新生代代大小

-XX:newSize 新生代初始化内存的大(注意:该值需要小于-Xms的值)

-XX:MaxnewSize 新生代可被分配的内存的最大上限(注意:该值需要小于-Xmx的值)

一个参数顶两个细分参数

-XX:NewRatio=2 默认新生代和老年代比例1:2

-XX:SurvivorRatio=8 默认情况下Eden:from:to=8:1:1

大对象的判定标准,通过-XX:+pretenureSizeThreshold控制

切换不同的垃圾回收器,这个是用的最多的GC方面优化手段

-XX:+UseSerialGC :服务器基本不用,单线程去做垃圾回收

--XX:+UseParallelGC -XX:+UseParallelOldGC:大部分默认这种,并行多线程回收

--XX:+UseConcMarkSweepGC:响应时间要求高的选择这一种,并发多线程回收

GC的多个阶段中,有一些阶段业务线程代码依旧可以运行,不像并行多线程,GC 全都是暂停业务线程

G1 GC,全程Garbage-Firest Garbage Collector,通过-XX:+UseG1GC:大内存服务器选择这一种,并发多线程回收

预申请内存:-XX:+AlwaysPreTouch

每个java应用启动参数必加

每个java应用启动之后梦想操作系统把内存全部申请到位

JVM配置了2G内存,不代表马上就有2G内存归属JVM,默认用了多少,操作系统给它多少,当值JVM要用的时候,操作系统没用足够内存,启动之后,提前申请

GC调优--所有jvm核心开发团队,不断努力提升的方向,参数很多,调了某一个参数不一定管用。多次配置修改,反复尝试,大规模落地--几家大厂核心业务系统

性能测试瓶颈分析与系统调优(9)java程序GC机制及性能稳定性调优分析相关推荐

  1. 人脉社群系统带流量主小程序源码【源码好优多】

    1.一个集发布.展示社群信息.人脉推广的裂变工具/平台. 2.通过人脉广场,将商家信息通过名片进行展示,让同城服务.资源对接.人脉推广更加便捷高效.低门槛创建商家名片,通过分享转发,在实现推广宣传自己 ...

  2. 如何分析java程序_如何利用 JConsole观察分析Java程序的运行,进行排错调优

    一.JConsole是什么 从Java 5开始 引入了 JConsole.JConsole 是一个内置 Java 性能分析器,可以从命令行或在 GUI shell 中运行.您可以轻松地使用 JCons ...

  3. 如何利用 JConsole观察分析Java程序的运行,进行排错调优

    原文链接:http://jiajun.iteye.com/blog/810150 一.JConsole是什么 从Java 5开始 引入了 JConsole.JConsole 是一个内置 Java 性能 ...

  4. 如何利用 JConsole观察分析Java程序的运行,进行排错调优(转)

    一.JConsole是什么 从Java 5开始 引入了 JConsole.JConsole 是一个内置 Java 性能分析器,可以从命令行或在 GUI shell 中运行.您可以轻松地使用 JCons ...

  5. 三菱伺服定长追剪,系统为Q172DSCPU 包含一个程序例子,有详细的机械参数分析,伺服参数设置

    三菱伺服定长追剪,系统为Q172DSCPU,高级同步模式. 包含一个程序例子,有详细的机械参数分析,伺服参数设置,以及追剪凸轮表设置的由来. ID:69100609575345770个人资料窝

  6. Android应用程序消息处理机制(Looper、Handler)分析(1)

    Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通 ...

  7. Android 应用程序消息处理机制(Looper、Handler)分析

    Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通 ...

  8. Android应用程序消息处理机制(Looper、Handler)分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6817933 Android应用程序是通过消息来 ...

  9. Android应用程序消息处理机制(Looper、Handler)分析(5)

    ActivityThread类的这个mH成员变量是什么时候创建的呢?我们前面在分析应用程序的消息循环时,说到当应用程序进程启动之后,就会加载ActivityThread类的main函数里面,在这个ma ...

最新文章

  1. NLP分词数据准备及模型训练实例
  2. 非 985/211 学校的毕业生,进大厂的机率有多大?
  3. 【翻译】CodeMix使用教程(三):Emmet
  4. 电脑计算机无法找到脚本文件夹,win10系统开机出现无法找到脚本文件的设置办法...
  5. 如何用Fiddler对手机(安卓/iOS)数据进行抓包
  6. ChubaoFS:一个面向大规模容器平台的分布式文件系统
  7. [原创]java获取word里面的文本
  8. Linux进程O(1)调度算法,面试必考哦
  9. 什么是 devops_DevOps对您意味着什么?
  10. java.io.IOException: Server returned HTTP response code: 411 for URL
  11. HDU 3082 HDOJ 3082 Simplify The Circuit ACM 3082 IN HDU
  12. 使用装机软件后,删除开机启动项的方法
  13. 副业做淘宝可以么?淘宝可以当做副业来做吗?
  14. 禁用红蜘蛛自启的简单办法
  15. JTAG、SBW、BSL 三种接口的区别
  16. “好好说话,别伤人。”
  17. 罗技无线网卡linux,Linux Kernel 5.2将改进对Logitech无线设备的支持
  18. 抽奖机(用随机数)2.0-python
  19. 【2-SAT初学+模板题讲解】POJ3683 Priest John's Busiest Day
  20. 老男孩python全栈第9期

热门文章

  1. 如何用纯 CSS 实现酷炫的霓虹灯效果?
  2. Delphi实现悬浮的卡拉OK字幕
  3. TDSQL-C for PostgreSQL 主从架构详解
  4. 2022山东省安全员C证考试题库及答案
  5. markdown编辑简介
  6. day0506列表的进阶
  7. Android和iOS静态代码扫描工具
  8. Win10编译FFmpeg-64位动态库
  9. c语言单链表倒置(附原理讲解)
  10. Nginx入门使用教程