JVM性能调优

1 基本概念

JVM内存:JVM内存能够分为堆区和非堆区。java

堆区:经过new的方式建立的对象(类实例)所占用的内存空间,在 Java 中,堆被划分红两个不一样的区域:新生代 ( Young )、老年代 (Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。apache

非堆区:非堆区即为代码、常量、外部访问(如文件访问流所占资源)等。windows

新生代 ( Young ):新生代用来存放JVM刚分配的Java对象。tomcat

老年代 (Old):新生代中通过垃圾回收没有回收掉的对象将被Copy到老年代。jvm

Eden:用来存放JVM刚分配的对象。工具

Survivror:两个Survivor空间同样大,当Eden中的对象通过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当知足某个条件,好比Copy次数,就会被Copy到Old。显然,Survivor只是增长了对象在年轻代中的逗留时间,增长了被垃圾回收的可能性。性能

垃圾回收:当新生代内存满时,会引起一次普通GC,该GC仅回收新生代。须要强调的时,年轻代尽是指Eden代满,Survivor满不会引起GC。测试

当老年代满时会引起Full GC,Full GC将会同时回收新生代、老年代。优化

##2 JVM参数spa

-Xms 初始堆大小 一般会将 -Xms 与 -Xmx两个参数的配置相同的值,其目的是为了可以在java垃圾回收机制清理完堆区后不须要从新分隔计算堆区的大小而浪费资源。

-Xmx 最大堆大小

-XX:newSize 新生代初始内存的大小

-XX:MaxnewSize 新生代最大内存的大小

-Xmn 新生代大小 对 -XX:newSize、-XX:MaxnewSize两个参数的同时配置, 即-XX:newSize = -XX:MaxnewSize = -Xmn

-Xss 每一个线程的堆栈大小 JDK1.5+ 每一个线程堆栈大小为 1M

-XX:NewRatio 新生代与老年代的比例 如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3 -XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值 默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10

-XX:PermSize 非堆区初始内存大小 慎重的考虑软件所须要的非堆区内存大小,由于此处内存是不会被java垃圾回收机制进行处理的;而且最大堆内存与最大非堆内存的和绝对不可以超出操做系统的可用内存

-XX:MaxPermSize 非堆区最大内存大小

##3 Tomcat配置 经屡次测试,在此机器上使用如下JVM参数可达到较优的性能,在${ CATALINA_HOME}\bin\ catalina.bat中添加如下配置,并使用startup.bat启动tomcat

set JAVA_OPTS=%JAVA_OPTS% -Xms1024m -Xmx1024m -Xmn384m -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m

备注:jvisualvm监控不到经过windows服务启动的tomcat,jvisualvm是经过pid来查找本地Java应用的,pid文件存放在临时目录下的hsperfdata_<username>文件夹下;而使用windows服务启动的Tomcat的临时目录使用的是系统临时目录。故这次内存调优使用startup.bat的方式启动Tomcat。

##4 内存监控 ###4.1 jvisualvm 须要远程监控时须在远程机器的启动参数中添加如下配置:

-Dcom.sun.management.jmxremote.port=8999
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=false

在${JAVA_HOME}\bin下打开jvisualvm.exe,添加远程主机并添加JMX链接

添加JMX链接

jvisualvm

###4.2 jconsole

在${JAVA_HOME}\bin下打开jconsole.exe,并新建链接

添加链接

jconsole

###4.3 jps 命令格式:jps [options ] [ hostid ]

[options]选项 :

-q:仅输出VM标识符,不包括classname,jar name,arguments in main method

-m:输出main method的参数

-l:输出彻底的包名,应用主类名,jar的彻底路径名

-v:输出jvm参数

-V:输出经过flag文件传递到JVM中的参数(.hotspotrc文件或-XX:Flags=所指定的文件

-Joption:传递参数到vm,例如:-J-Xms512m

[hostid]:

[protocol:][[//]hostname][:port][/servername]

命令的输出格式 :

lvmid [ [ classname| JARfilename | "Unknown"] [ arg* ] [ jvmarg* ] ]

使用jps命令查看正在运行的jvm进程,找到tomcat进程的pid

C:\Users\dev01>jps -l
4468 org.apache.catalina.startup.Bootstrap

###4.4 jstat 语法结构:

Usage: jstat -help|-options

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

参数解释:

Options — 选项,咱们通常使用 -gcutil 查看gc状况

vmid — VM的进程号,即当前运行的java进程号

interval– 间隔时间,单位为秒或者毫秒

count — 打印次数,若是缺省则打印无数次

在远程主机上使用jstat命令可查看JVM内存状况

C:\Users\dev01> jstat -gc 4468
S0C  S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT              GCT

52416.0 52416.0  0.0   24837.2 419456.0 305254.0  524288.0   75588.2   131072.0 58023.9    365    4.633   0      0.000  4.633

S0 — Heap上的 Survivor space 0 区已使用空间的百分比

S1 — Heap上的 Survivor space 1 区已使用空间的百分比

E — Heap上的 Eden space 区已使用空间的百分比

O — Heap上的 Old space 区已使用空间的百分比

P — Perm space 区已使用空间的百分比

S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)

EC、EU:Eden区容量和使用量

OC、OU:年老代容量和使用量

PC、PU:永久代容量和使用量

YGC、YGT:年轻代GC次数和GC耗时

FGC、FGCT:Full GC次数和Full GC耗时

GCT:GC总耗时

C:\Users\dev01>jstat -gcutil 4468 1000 10
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  1.70   0.00 100.00 100.00  51.56  19155  325.924 55782 86487.063 86812.987
100.00   0.00 100.00 100.00  51.56  19155  325.924 55783 86487.063 86812.987
100.00   0.00 100.00 100.00  51.56  19155  325.924 55783 86487.063 86812.987
100.00   0.00 100.00 100.00  51.57  19155  325.924 55784 86488.853 86814.777
100.00   0.00 100.00 100.00  51.57  19155  325.924 55784 86488.853 86814.777
100.00   0.00 100.00 100.00  51.56  19155  325.924 55785 86490.632 86816.556
100.00   0.00 100.00 100.00  51.56  19155  325.924 55785 86490.632 86816.556
100.00   0.00 100.00 100.00  51.56  19155  325.924 55786 86492.414 86818.337
100.00   0.00 100.00 100.00  51.56  19155  325.924 55786 86492.414 86818.337
  0.00   0.00 100.00 100.00  51.56  19155  325.924 55787 86494.207 86820.131

YGCT/YGC=0.017秒(17毫秒),新生代空间上的GC平均花费17毫秒。

FGC/FGCT=1.55秒,Full GC平均花费1.55秒,平均花费时间不算长,但Full GC花费的总时间为86494.207 (24小时),而服务只运行了70小时,这个结果显然不能让人接受。

##5 分析dump文件 在JVM启动参数中添加-XX:+HeapDumpOnOutOfMemoryError,在发生OOME错误时,虚拟机会Dump出当前的堆转存快照,本次调优中使用手动Dump,监控JVM堆使用状况,在达到最大值以前手动Dump出dump文件。手动Dump生成的dump文件默认保存置%USERPROFILE%\AppData\Local\Temp\visualvm.dat\下。

堆dump

分析dump文件的工具备不少种,在此咱们使用MAT(Memory Analyze Tool)进行分析工做。使用MAT工具打开dump文件,能够在Overview中清晰的看到内存使用状况。

overview

在生成的default_report中,能够看到MAT已经帮咱们找出了问题:0x1c491278占用了830M

default_report

点击Details连接,在Dominator Tree中能够看到ResultMap占用了大量内存

Dominator Tree

查看工程代码发现ResultMap中的数据并无被清理掉,添加清理ResultMap的代码,从新运行并监控服务。

C:\Users\dev01>jstat -gcutil 6804 1000 10
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
  0.00  53.29  64.59  93.17  54.58  35721  747.320     6    3.417  750.736
  0.00  53.29  80.07  93.17  54.58  35721  747.320     6    3.417  750.736
  0.00  53.29  95.00  93.17  54.58  35721  747.320     6    3.417  750.736
 54.38   0.00   8.48  93.18  54.58  35722  747.341     6    3.417  750.758
 54.38   0.00  23.79  93.18  54.58  35722  747.341     6    3.417  750.758
 54.38   0.00  37.50  93.18  54.58  35722  747.341     6    3.417  750.758
 54.38   0.00  53.72  93.18  54.58  35722  747.341     6    3.417  750.758
 54.38   0.00  67.74  93.18  54.58  35722  747.341     6    3.417  750.758
 54.38   0.00  83.52  93.18  54.58  35722  747.341     6    3.417  750.758
 54.38   0.00  98.45  93.18  54.58  35722  747.341     6    3.417  750.758

YGCT/YGC=0.021秒(21毫秒),新生代空间上的GC平均花费21毫秒。

FGC/FGCT=0.57秒,服务运行了72小时,Full GC了5次,平均花费0.57秒。

##6 总结 大多数Java应用不须要进行GC优化,而且多数出现GC问题的Java应用是代码问题致使的,并不是参数错误,GC优化是最后万不得已的手段;在配置JVM参数时须要进行屡次测试,经过不断的试验和试错,分析并找到最合适的参数。

相关文章
相关标签/搜索