JVM调优

一、jvm调优基础

1.jvm堆内存配置

-XX:MetaspaceSize=128m (元空间默认大小) 
-XX:MaxMetaspaceSize=128m (元空间最大大小) 
-Xms1024m (堆默认大小) 
-Xmx1024m (堆最大大小) 
-Xmn256m (新生代大小) 
-Xss256k (棧最大深度大小) 
-XX:SurvivorRatio=8 (新生代分区比例 8:2) 
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器) 
-XX:+PrintGCDetails (打印详细的GC日志)

堆内存调整参数如图所示:

在这里插入图片描述
我们可以发现每一个区域都有一个可变的伸缩区,当我们的内存空间不足的时候,会在可变的范围内扩大内存空间,当我们的内存空间变得不紧张的时候我们再释放可变空间。

在堆内存的调优之中我们要特别注意两个参数-Xms初始化内存分配大小,默认为物理内存的1/64,-Xmx 最大的分配内存默认为物理内存的1/4。

即执行Java -jar 命令默认的内存分配策略

查看jvm配置可使用下面的命令

ps -ef | grep java
在这里插入图片描述

2.JVM运行情况预估

用 jstat gc -pid 命令可以计算出如下一些关键数据,有了这些数据就可以采用之前介绍过的优化思路,先给自己的系统设置一些初始性的JVM参数,比如堆内存大小,年轻代大小,Eden和Survivor的比例,老年代的大小,大对象的阈值,大龄对象进入老年代的阈值等。

年轻代对象增长的速率

可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不同的时间分别估算不同情况下对象增长速率。

Young GC的触发频率和每次耗时
知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC 公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。

每次Young GC后有多少对象存活和进入老年代
这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。

Full GC的触发频率和每次耗时
知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。

优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。

JVM垃圾回收参数实例解析:

root 8508 1 0 11:51 ? 00:02:08 /bin/java -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -server -Xmx2g -Xms2g -Xmn512m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70

-server:

启用-server时新生代默认采用并行收集,其他情况下,默认不启用。

-XX:+UseAdaptiveSizePolicy:

上文中,因启用-server模式,所以新生代使用并行收集器。

设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,建议使用并行收集器时一直打开。

-XX:+DisableExplicitGC
关闭System.gc()

-XX:+UseConcMarkSweepGC:

设置老年代为并发收集。

-XX:+CMSParallelRemarkEnabled
降低标记停顿

-XX:+UseCMSCompactAtFullCollection
在FULL GC的时候, 对老年代的压缩。
CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。
可能会影响性能,但是可以消除碎片

-XX:LargePageSizeInBytes

内存页的大小不可设置过大, 会影响Perm的大小

-XX:+UseFastAccessorMethods

原始类型的快速优化

XX:+UseCMSInitiatingOccupancyOnly

使用手动定义初始化定义开始CMS收集,目的:禁止hostspot自行触发CMS GC

-XX:CMSInitiatingOccupancyFraction=70
使用cms作为垃圾回收,使用70%后开始CMS收集

其他相关参数:

-XX:PermSize
设置持久代(perm gen)初始值 默认为:物理内存的1/64

-XX:MaxPermSize
设置持久代最大值 默认为:物理内存的1/4

-XX:MaxTenuringThreshold

垃圾最大年龄.如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率
该参数只有在串行GC时才有效.

@see 深入了解JVW https://www.cnblogs.com/yang-hao/p/5936059.html

idea 设置jvm运行参数

-Xms100m -Xmx100m

在这里插入图片描述

二、jvm调优常用命令

这里主要介绍如下几个工具:

1、jps:查看本机java进程信息

2、jstack:打印线程的栈信息,制作 线程dump文件

3、jmap:打印内存映射信息,制作 堆dump文件

4、jstat:性能监控工具

5、jhat:内存分析工具,用于解析堆dump文件并以适合人阅读的方式展示出来

6、jconsole:简易的JVM可视化工具

7、jvisualvm:功能更强大的JVM可视化工具

JAVA Dump:
JAVA Dump就是虚拟机运行时的快照,将虚拟机运行时的状态和信息保存到文件中,包括:

线程dump:包含所有线程的运行状态,纯文本格式

堆dump:包含所有堆对象的状态,二进制格式

1.jps

显示当前所有java进程pid的命令,我们可以通过这个命令来查看到底启动了几个java进程(因为每一个java程序都会独占一个java虚拟机实例),不过jps有个缺点是只能显示当前用户的进程id,要显示其他用户的还只能用linux的ps命令。

在这里插入图片描述

执行jps命令,会列出所有正在运行的java进程,其中jps命令也是一个java程序。前面的数字就是进程的id,这个id的作用非常大,后面会有相关介绍。

jps -v 输出传递给JVM的参数
在这里插入图片描述

2.jstack

主要用于生成指定进程当前时刻的线程快照,线程快照是当前java虚拟机每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致长时间等待。

在这里插入图片描述

3.jmap

主要用于打印指定java进程的共享对象内存映射或堆内存细节。

堆Dump是反映堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。一般在内存不足,GC异常等情况下,我们会去怀疑内存泄漏,这个时候就会去打印堆Dump。

1. jmap -heap pid:查看堆使用情况

在这里插入图片描述

jmap 踩坑记录

Windows下jmap命令报错问题 https://www.cnblogs.com/ocean234/p/11525721.html

2. jmap -histo pid:查看堆中对象数量和大小

在这里插入图片描述

4.jstat

主要是对java应用程序的资源和性能进行实时的命令行监控,包括了对heap size和垃圾回收状况的监控。

jstat - [-t] [-h] [ []]

option:我们经常使用的选项有gc、gcutil

vmid:java进程id

interval:间隔时间,单位为毫秒

count:打印次数

jstat -gc PID 5000 20

在这里插入图片描述

S0C:年轻代第一个survivor的容量(KB)

S1C:年轻代第二个survivor的容量(KB)

S0U:年轻代第一个survivor已使用的容量(KB)

S1U:年轻代第二个survivor已使用的容量(KB)

EC:年轻代中Eden的空间(KB)

EU:年代代中Eden已使用的空间(KB)

OC:老年代的容量(KB)

OU:老年代中已使用的空间(KB)

MC:当前metaspace的容量(KB)
MU:当前metaspace已使用的空间(KB)

CCSC:当前compressed class space的容量(KB)
CCSU:当前compressed class space已使用的空间(KB)

YGC:从应用程序启动到采样时年轻代中GC的次数
YGCT:从应用程序启动到采样时年轻代中GC所使用的时间(单位:S)

FGC:从应用程序启动到采样时老年代中GC(FULL GC)的次数

FGCT:从应用程序启动到采样时老年代中GC所使用的时间(单位:S)
CGC:从应用程序启动到采样时发生concurrent GC的次数
CGCT:从应用程序启动到采样时concurrent GC所用的时间(秒)
GCT:从应用程序启动到采样时垃圾回收所用的总时间(秒)

5.jinfo

jinfo可以用来查看正在运行的java运用程序的扩展参数,甚至支持在运行时动态地更改部分参数。

基本使用语法如下: jinfo -< option > < pid > ,其中option可以为以下信息:

查看当前的应用java参数配置

jinfo -flags pid

在这里插入图片描述

6.jcmd

在JDK 1.7之后,新增了一个命令行工具jcmd。它是一个多功能工具,可以用来导出堆,查看java进程,导出线程信息,执行GC等。jcmd拥有jmap的大部分功能,Oracle官方建议使用jcmd代替jmap。

子命令含义:


VM.native_memory
VM.commercial_features
GC.rotate_log
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
Thread.print,                         打印线程栈信息
GC.class_histogram,              查看系统中类统计信息
GC.heap_dump,                    导出堆信息,与jmap -dump功能一样
GC.run_finalization,               触发finalize()
GC.run,                                触发gc()
VM.uptime,                           VM启动时间
VM.flags,                              获取JVM启动参数
VM.system_properties,          获取系统Properties
VM.command_line,                 启动时命令行指定的参数
VM.version
help

如查看JVM启动参数

在这里插入图片描述

思考

1.如何用jstack找出占用cpu最高的线程堆栈信息?

我们使用jdk自带的jstack来分析。
思路:

先找到高负载的进程ID,然后查看进程中高负载的线程ID。最后使用jstack 线程ID找到详细的堆栈位置

当linux出现cpu被java程序消耗过高时,以下过程说不定可以帮上你的忙:

1、执行:top

查看高负载的进程

2、top -H -p 28973

查看高负载进程下的高负载线程

把线程号 28973 进行换算成16进制编号:print"%x\n" 28973 ->72d6

3、jstack 28973 >> log.txt

将线程堆栈信息保存为txt文档

4、打开log.txt,find一下72d6

导出进程的堆栈日志,找到72d6 这个线程号

在这里插入图片描述

@see jstack命令查看占用CPU高的线程堆栈信息https://www.cnblogs.com/kaymi/p/12667708.html

2.如何查看内存占用最高的对象

通过jconsole分析dump文件。

3.如何查看进程是否频繁进行fullGC并解决

使用的jvm参数为

-Xms100m -Xmx100m -Xmn30m

代码地址见:

1.使用jstat命令分析是否频繁触发fullGC

在这里插入图片描述
可见频繁的进行了full gc

2.使用jmap -histo 1358 查看是否存在大对象

在这里插入图片描述

user对象创建了500个实例,每个实例0.1M,并放在了一个list集合中,将是50M,大于新生代的大小30M,将放在老年代中。长次以往将不断的触发full gc.

解决办法:

调大新生代的大小,让list对象通过Minor gc进行回收掉。

4.发生OOM如何排查?

1.首先增加两个参数便于,当 OOM 发生时自动 dump 堆内存信息
到指定目录。

-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/tmp/heapdump.hprof

2.使用 MAT 等工具载入到 dump 文件, 分析大对象的占用情况, 比如 HashMap 做缓存未清理, 时间长了就会内存溢出, 可以把改为弱引用。

三、jvm调优工具

1.VisualVM

参考资料

1.JDK工具(查看JVM参数、内存使用情况及分析等)

https://www.cnblogs.com/z-sm/p/6745375.html

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值