欢迎访问我的博客,你的支持,是我最大的动力!

Java应用性能监控与调优(一)JDK工具/监控/调试

Linux 小马奔腾 65℃ 评论
目录:
[显示]

生产环境发生内存溢出该如何处理?
生产环境应该给服务器分配多少内存合适?
如何对垃圾收集器的性能进行调优?
生产环境CPU负载飙高该如何处理?
生产环境应该给应用分配多少线程合适?
不加log如何确实请求是否执行了某一行代码?
不加log如何实时查看某个方法的入参与返回值?
JVM的节节码是什么东西?
循环体中做字符串+拼接为什么效率低?字符串+拼接一定就是StringBuilder.append吗?
String常量池是什么?i++与++i到底哪种写法效率更高?

基于java版本:1.8.0_101

基于JDK命令行工具的监控
JVM的参数类型

三类:标准参数、X参数、XX参数

标准参数:
-help -server -client -version -showversion -cp -classpath

X参数:
各个版本中可能会改变
-Xint (完全)解释执行
-Xcomp 第一次使用就编译成本地代码
-Xmixed 混合模式,JVM自己决定是否编译成本地代码

java -version 混合模式;java -Xint -version 解释执行;java -Xcomp -version 编译执行

XX参数:
非标准化参数,相对不稳定,主要用于JVM调优和Debug
Boolean类型
格式:-XX:[+-]<name>  启用或禁用name属性,+ 表示启用,- 表示禁用
示例:-XX:+UseConcMarkSweepGC  -XX:+UseG1GC
非Boolean类型
格式:-XX:<name>=<value>  表示name属性的值设置为value
示例:-XX:MaxGCPauseMillis=500  GC最大停顿时间为500毫秒  XX:GCTimeRatio=19

-Xms 初始堆内存大小 -> -XX:InitialHeapSize
-Xmx 最大堆内存大小 -> -XX:MaxHeapSize
-Xss  设置线程栈大小  -> -XX:ThreadStackSize  默认为1024,较大,通常可改小为512

查看运行时参数值,进程id为23789
jinfo -flag MaxHeapSize 23789
jinfo -flag ThreadStackSize 23789

运行时JVM参数查看

-XX:+PrintFlagsInitial  查看初始值
-XX:+PrintFlagsFinal    查看最终值
## 输出中 = 表示默认值, := 表示被用户或JVM修改后的值
-XX:+UnlockExperimentalVMOptions  解锁实验参数
-XX:+UnlockDiagnosticVMOptions      解锁诊断参数
-XX:+PrintCommandLineFlags             打印命令行参数

示例:
java -XX:+PrintFlagsFinal -version   #大约有700多个参数

jps 命令
类似于ps,用于查看java进程的进程id
使用:
jps   显示简略信息(类名)
jps -l   显示较详细的信息(类名)

官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/index.html
jps文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html#CHDCGECD

jinfo 命令
查看运行中java程序的参数值,需要知道参数的名称和进程id
示例:
jinfo -flag MaxHeapSize 23789  # 查看最大堆内存
jinfo -flag UseConcMarkSweepGC 23789  # 查看垃圾回收器
jinfo -flag UseG1GC 23789  # 查看垃圾回收器
jinfo -flag UseParallelGC 23789  # 查看垃圾回收器
jinfo -flags 23789  # 打印进程非默认的参数值(手动赋值的)及命令行

jstat查看虚拟机统计信息

jstat可查看类装载、垃圾收集、JIT编译
命令格式:jstat -help
options: -class 类装载信息, -compiler 编译信息, -gc 垃圾回收信息, -printcompilation

类装载
jstat -class 3176 1000 10  # 3176是进程号 1000毫秒,即每隔1秒钟输出一次  10表示一共输出10次

垃圾收集
-gc -gcutil -gccause -gcnew -gcold
jstat -gc 3176 1000 3    # 3176是进程号 1000毫秒,即每隔1秒钟输出一次  3表示一共输出3次
结果中,
S0C,S1C,S0U,S1U表示S0和S1的总量和使用量
EC,EU 表示Eden区总量和使用量
OC,OU 表示Old区总量和使用量
MC,MU 表示Metaspace区总量与使用量
CCSC,CCSU 表示压缩类空间总量和使用量(设置了短指针,32位)
YGC,YGCT 表示YoungGC的次数与时间
FGC,FGCT 表示FullGC的次数与时间
GCT 总的GC时间

JIT编译
将代码编译成本地码的情况
-compiler,-printcompilation
示例:
jstat -compiler 3172
jstat -printcompilation 3172

jmap+MAT 实战内存溢出

区分为堆区的内存溢出和非堆区的内存溢出

http://start.spring.io  生成spring的工程,自动生成工程文件

-Xmx32M -Mxs32M   可以这样写,不加等号
-XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M  限制非堆内存大小

1、导出内存映像文件

内存溢出自动导出
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./

会导出一个 java_pidxxxx.hprof 文件

使用jmap命令手动导出

jmap 命令
jmap -help
option: -heap, -clstats, -dump:<dump-options>, -F
<dump-options>: format=b 二进制格式;file=<file> 导出文件路径及名字
示例:
jmap -dump:format=b,file=heap.hprof 16940    # 16940为进程号
jmap -heap 16940  # 打印内存信息

2、MAT分析内存溢出

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

使用:
1、导入内存映像文件,选择Leak Suspects Report
在default_report中,
Leak Suspects 怀疑有内存溢出
2、点击工具栏第二个图标(三条粗竖线),histogram工具
可以查看对象数量,支持正则匹配
3、查看对象是谁引用的
在对象上右键,选择 Merge Shortest Paths to GC Roots -> exclude all phantom/weak/soft etc. references 只看强引用
4、点击工具栏第三个图标,Dominator Tree工具
查看对象占的内存
在对象上右键,选择 Merge Shortest Paths to GC Roots -> exclude all phantom/weak/soft etc. references 只看强引用

jstack 实战死循环与死锁

打印JVM内部所有线程,当发现CPU飙高时,可考虑发生了死循环或死锁

jstack 命令
jastack PID >PID.txt   # 保存某进程所有线程信息
输出的内容:
线程名,#xxx 内部编号,daemon 后台线程,prio=5 内部优先级,os_prio=0 操作系统优先级,nid=0x55ce 线程号
java.lang.Thread.State 线程状态

线程状态:
NEW 新创建的,还没有启动
RUNNABLE 在JVM中执行
BLOCKED 阻塞的,等待锁
WAITING 等待另一个线程做一些特定操作
TIMED_WAITING  限时等待
TERMINATED 线程已经退出了

参考文章:https://mp.weixin.qq.com/s/GsxeFM7QWuR--Kbpb7At2w

实战死循环导致CPU飙高

top -p pid -H
jstack pid
printf "%x" 进制制和线程id

mvn clean package -Dmaven.test.skip    # 打包java程序,不做测试  会生成jar包

jstack 7930>7930.txt  # 保存线程情况
top -p 7930 -H # 显示所有线程
printf "%x" 8247 # 将十进制8247转成十六进制 0x2037

实战死锁排查

jstack 23674>23674.txt
在23674末尾,会有 “Found 1 deadlock”并且在它前面,会列出发生死锁的线程信息

基于JVisualVM的可视化监控

jdk的bin目录下,jvisualvm.exe可执行文件,是jdk自带的工具
打开后,默认监控本地的java线程
可添加插件,Visual GC

监控本地Tomcat

可导出 堆Dump,并展示其内容;也可以导入,文件 -> 装入 选择hprof文件,类似于MAT工具的功能,也能查看引用信息
也可执行 线程Dump
抽样器,对CPU抽样,可查看每个方法执行的CPU时间;对内存抽样,可查看各对象的内存占用情况

插件添加
工具 -> 插件 -> 设置
更新中心配置:编辑,添加插件中心地址https://visualvm.github.io/uc/8u131/updates.xml.gz  注意需要和jdk版本相对应
添加两个插件:Virsual GC 和 Btrace Workbench (BTrace的图形界面)
插件中心:https://visualvm.github.io/pluginscenters.html
文档:https://visualvm.github.io/documentation.html

监控远程Tomcat

在远程修改catalina.sh配置文件:

JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004
-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
-Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=10.110.3.62"

远程 -> 输入主机名  这样将远程主机添加到界面
选择添加的主机,右键,添加JMX连接

远程不支持Visual GC插件

监控远程普通JAVA进程

需要添加启动参数,如:

nohup java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9005
-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
-Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=10.110.3.62 -jar monitor_tuning.jar &

VisualVM文档:https://visualvm.github.io/documentation.html  有中文版

基于Btrace的监控调试

在应用程序不重启、不修改的情况下,可动态修改字节码,从而达到监控调试的目的
可以动态地向目标应用程序的字节码注入追踪代码

使用的技术:JavaComplierApi、JVMTI、Agent、Instrumentation+ASM

Btrace安装

项目地址:https://github.com/btraceio/btrace
下载地址:
https://github.com/btraceio/btrace/releases/download/v1.3.11/btrace-bin-1.3.11.tgz.tar.gz
https://github.com/btraceio/btrace/releases/download/v1.3.11.3/btrace-bin-1.3.11.3.tgz

新建环境变量 BTRACE_HOME
添加Path: %BTRACE_HOME%\bin

两种运行脚本方式:
1、在JVisualVM中添加Btrace插件,添加classpath
2、使用命令行 btrace <pid> <trace_script>

在JVisualVM中使用
在进程上右键,选择 Trace application -> 编写代码 -> start

注意,Btrace脚本代码是java代码

依赖
btrace_home/build/btrace-agent.jar
btrace_home/build/btrace-boot.jar
btrace_home/build/btrace-client.jar

Btrace使用详解

拦截方法

普通方法: @OnMethod(clazz="",method="")
构造函数: @OnMethod(clazz="",method="<init>")
拦截同名函数,用参数区分

拦截时机

Kind.ENTRY 入口,默认
Kind.RETRURN 返回
Kind.THROW 异常    业务代码处理了异常,这时异常不会展示,可以通过这种方式拿到异常及异常堆栈
Kind.Line 行   可以看到代码是否执行到某一行

拦截this、参数、返回值

this: @Self
入参:可以用AnyType,也可以用真实类型,同名的用真实的
返回: @Return

获取对象的值

简单类型:直接获取
复杂类型:反射,类名+属性名

拦截支持正则表达式

其他

打印行号:Kind.LINE
打印堆栈:Threads.jstack()
打印环境变量

注意事项:
默认只能本地运行
生产环境下可以使用,但是被修改的字节码不会被还原

Tomcat性能监控与调优
tomcat远程debug

使用的技术是 jdwp
参考阅读:JDWP协议及实现 https://www.ibm.com/developerworks/cn/java/j-lo-jpda3/

配置远程tomcat开启jdwp

1、编辑 ./bin/startup.sh
在最后一行添加jpda参数:
exec "$PRGDIR"/"$EXECUTABLE" jpda start "$@"

2、编辑./bin/catalina.sh
里面有 JPDA_OPTS 项目

实际上,就是在启动参数上添加了:
-agentlib:jdwp=transport=dt_socket,address=54321,server=y,suspend=n
在普通java程序上添加上面一行启动参数,也可以远程debug

本端在IDE上配置远程debug(打断点)就可以了,使用端口54321

tomcat-manager监控

tomcat自带功能,默认不开启

文档: docs/manager-howto.html

步骤:
1、conf/tomcat-users.xml 添加用户
2、conf/Catalina/localhost/manager.xml 配置允许的远程连接
3、重启

1、tomcat-users.xml 中添加

2、manager.xml  文件默认不存在,需要创建

允许来自127.0.0.1的访问

可以管理站点、应用、查看内存使用情况、连接信息等

psi-probe监控

项目地址:https://github.com/psi-probe/psi-probe
war包:https://github.com/psi-probe/psi-probe/releases/download/3.2.0/probe.war

git clone https://github.com/psi-probe/psi-probe
mvn clean package -Dmaven.test.skip
最终会生成 web/target/probe.war 文件
复制probe.war到tomcat的webapps目录下即可

注意,需要在 tomcat-user.xml 中配置用户;在conf/Catalina/localhost/manager.xml中配置允许的IP
配置文件的内容和tomcat-manager监控中配置的完全一样

可以查看:
应用的统计信息,请求、session、jsp预编译,线程信息,内存信息等等,较manager强大

tomcat调优

内存优化(略,后面有JVM的内存调优)

线程优化

参考文档:docs/config/http.html

maxConnections   最大连接数,默认10000 因为使用的是nio(多路复用),所以最大连接数可以设置得很大
acceptCount          队列大小,默认100,当所有线程都忙时,新连接可放到临时队列中,此为队列大小,队列满了后,新的请求会被拒绝
maxThreads          最大工作线程,默认200,同一时刻,最多可以有多少个线程,并发能力
minSpareThreads 最小空闲工作线程

配置优化

参考文档:docs/config/host.html

autoDeploy: false 是否自动部署,默认为true,会有一定性能损耗   在server.xml中配置

参考文档:docs/config/http.html

enableLookups: false   不进行DNS查询解析,默认禁用状态

参考文档:docs/config/context.html

reloadable: false  不监控/WEB-INF/classes/ 和 /WEB-INF/lib 下的文件变化,若变化则重新载入发生变化的类,默认禁用状态

参考文档:conf/server.xml   protocol="HTTP/1.1" ->bio

protocol="org.apache.coyote.http11.Http11AprProtocol"  适用于大并发的场景
apr连接器:http://apr.apache.org/

Session优化

如果是JSP,可以禁用Session

Nginx性能监控与调优

nginx 源

最新版本:http://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.16.0-1.el7.ngx.x86_64.rpm

ngx_http_stub_status监控连接信息

添加配置:

ngxtop监控请求信息

项目地址:https://github.com/lebinh/ngxtop

实时解析access.log日志,获取统计信息
默认从ngxtop开始运行后开始计算,之前的旧log不会被分析到

安装python-pip
yum install epel-release
yum install python-pip
安装ngxtop
pip install ngxtop

使用:
指定配置文件:ngxtop -c /etc/nginx/nginx.conf
查询状态是200的:
ngxtop -c /etc/nginx/nginx.conf -i 'status==200'
查询访问最多的ip:
ngxtop -c /etc/nginx/nginx.conf -g remote_addr

nginx-rrd图形化监控

基于 ngx_http_stub_status 输出的数据,依赖php环境

# 安装php
yum install php php-gd php-soap php-mbstring php-xmlrpc php-dom php-fpm
# 安装依赖
yum install perl rrdtool perl-libwww-perl libwww-perl perl-rrdtool
# 安装nginx-rrd
wget http://soft.vpser.net/status/nginx-rrd/nginx-rrd-0.1.4.tgz
tar zxvf nginx-rrd-0.1.4.tgz
cd nginx-rrd-o.1.4
cp usr/sbin/* /usr/sbin
cp etc/nginx-rrd.conf /etc
cp html/index.php /usr/share/nginx/html/
# 修改/etc/nginx-rrd.conf配置文件
RRD_DIR="/usr/share/nginx/html/nginx-rrd";
WWW_DIR="/usr/share/nginx/html/";
# 定时任务
*    * * * * /bin/sh /usr/sbin/nginx-collect
*/1 * * * * /bin/sh /usr/sbin/nginx-graph
# 测试
ab -n 10000 -c 10 http://127.0.0.1/index.html
# 访问
http://127.0.0.1/index.php

nginx调优

增加工作线程数和并发连接数

worker_processes 4;  # cpu核心数
events {
worker_connections 10240; # 每个进程打开的最大连接数,包含nginx与客户端及nginx与upstream之间的连接
multi_accept on;  # 可以一次建立多个连接
use epoll;
}

启用后端Server的长连接

nginx与浏览器之间默认启用了长连接,但与后端服务器默认未做长连接

http -> keepalive_timeout 65;      #nginx与客户端之间的长连接

启用缓存、压缩

gzip on;

其他

sendfile on;         减少文件在应用和内核之间拷贝
tcp_nopush on;   当数据包达到一定大小再发送
tcp_nodelay off;  有数据随时发送

操作系统优化

配置文件 /etc/sysctl.conf

配置文件 /etc/security/limits.conf

 

转载请注明:轻风博客 » Java应用性能监控与调优(一)JDK工具/监控/调试

喜欢 (0)or分享 (0)