JVM性能优化入门指南
兵器谱
jps
列出正在运行的虚拟机进程,用法如下:
jps [-option] [hostid]
选项 | 作用 |
---|---|
q | 只输出LVMID,省略主类的名称 |
m | 输出main method的参数 |
l | 输出完全的包名,应用主类名,jar的完全路径名 |
v | 输出jvm参数 |
jstat
监视虚拟机运行状态信息,使用方式:
jstat -<option> <pid> [interval[s|ms]]
选项 | 作用 |
---|---|
gc | 输出每个堆区域的当前可用空间以及已用空间,GC执行的总次数,GC操作累计所花费的时间。 |
gccapactiy | 输出每个堆区域的最小空间限制(ms)/最大空间限制(mx),当前大小,每个区域之上执行GC的次数。(不输出当前已用空间以及GC执行时间)。 |
gccause | 输出-gcutil提供的信息以及最后一次执行GC的发生原因和当前所执行的GC的发生原因。 |
gcnew | 输出新生代空间的GC性能数据。 |
gcnewcapacity | 输出新生代空间的大小的统计数据。 |
gcold | 输出老年代空间的GC性能数据。 |
gcoldcapacity | 输出老年代空间的大小的统计数据。 |
gcpermcapacity | 输出持久带空间的大小的统计数据。 |
gcutil | 输出每个堆区域使用占比,以及GC执行的总次数和GC操作所花费的事件。 |
比如:
jstat -gc 28389 1s
每隔1秒输出一次JVM运行信息:
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
52416.0 52416.0 4744.9 0.0 419456.0 28180.6 2621440.0 439372.6 131072.0 33564.8 160472 1760.603 61 2.731 1763.334
列 | 说明 | jstat参数 |
---|---|---|
S0C | Survivor0空间的大小。单位KB。 | -gc -gccapacity -gcnew -gcnewcapacity |
S1C | Survivor1空间的大小。单位KB。 | -gc -gccapacity -gcnew -gcnewcapacity |
S0U | Survivor0已用空间的大小。单位KB。 | -gc -gcnew |
S1U | Survivor1已用空间的大小。单位KB。 | -gc -gcnew |
EC | Eden空间的大小。单位KB。 | -gc -gccapacity -gcnew -gcnewcapacity |
EU | Eden已用空间的大小。单位KB。 | -gc-gcnew |
OC | 老年代空间的大小。单位KB。 | -gc -gccapacity -gcold -gcoldcapacity |
OU | 老年代已用空间的大小。单位KB。 | -gc -gcold |
PC | 持久代空间的大小。单位KB。 | -gc -gccapacity -gcold -gcoldcapacity -gcpermcapacity |
PU | 持久代已用空间的大小。单位KB。 | -gc -gcold |
YGC | 新生代空间GC时间发生的次数。 | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
YGCT | 新生代GC处理花费的时间。 | -gc-gcnew-gcutil-gccause |
FGC | full GC发生的次数。 | -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
FGCT | full GC操作花费的时间。 | -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
GCT | GC操作花费的总时间。 | -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
NGCMN | 新生代最小空间容量,单位KB。 | -gccapacity -gcnewcapacity |
NGCMX | 新生代最大空间容量,单位KB。 | -gccapacity -gcnewcapacity |
NGC | 新生代当前空间容量,单位KB。 | -gccapacity -gcnewcapacity |
OGCMN | 老年代最小空间容量,单位KB。 | -gccapacity-gcoldcapacity |
OGCMX | 老年代最大空间容量,单位KB。 | -gccapacity-gcoldcapacity |
OGC | 老年代当前空间容量制,单位KB。 | -gccapacity -gcoldcapacity |
PGCMN | 持久代最小空间容量,单位KB。 | -gccapacity -gcpermcapacity |
PGCMX | 持久代最大空间容量,单位KB。 | -gccapacity -gcpermcapacity |
PGC | 持久代当前空间容量,单位KB。 | -gccapacity -gcpermcapacity |
PC | 持久代当前空间大小,单位KB。 | -gccapacity-gcpermcapacity |
PU | 持久代当前已用空间大小,单位KB。 | -gc -gcold |
LGCC | 最后一次GC发生的原因。 | -gccause |
GCC | 当前GC发生的原因。 | -gccause |
TT | 老年化阈值。被移动到老年代之前,在新生代空存活的次数。 | -gcnew |
MTT | 最大老年化阈值。被移动到老年代之前,在新生代空存活的次数。 | -gcnew |
DSS | 幸存者区所需空间大小,单位KB。 | -gcnew |
jmap
生成堆存储快照,使用方式:
jmap [ -option ] <pid>
选项 | 作用 |
---|---|
dump | 生成堆存储快照,格式为:-dump:[live, ]format=b, file=<filename>,live说明是否只dump出存活的对象。 |
heap | 显示java堆详细信息,如使用那种回收器、参数配置、分代状况等。 |
histo | 显示堆中对象统计信息,包括类、实例数量、合计容量。 |
jstack
生成虚拟机当前时刻的线程快照,帮助定位线程出现长时间停顿的原因,用法:
jstack <pid>
Monitor
Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者Class的锁。每一个对象都有,也仅有一个 monitor。下面这个图,描述了线程和 Monitor之间关系,以及线程的状态转换图:
进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁,但并未得到。
拥有者(The Owner):表示线程成功竞争到对象锁。
等待区(Wait Set):表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。
线程状态
- NEW,未启动的。不会出现在Dump中。
- RUNNABLE,在虚拟机内执行的。
- BLOCKED,等待获得监视器锁。
- WATING,无限期等待另一个线程执行特定操作。
- TIMED_WATING,有时限的等待另一个线程的特定操作。
- TERMINATED,已退出的。
举个例子:
package com.jiuyan.mountain.test; import java.util.concurrent.TimeUnit; /**
* Hello world!
*
*/
public class App { public static void main(String[] args) throws InterruptedException {
MyTask task = new MyTask();
Thread t1 = new Thread(task);
t1.setName("t1");
Thread t2 = new Thread(task);
t2.setName("t2");
t1.start();
t2.start();
} } class MyTask implements Runnable { private Integer mutex; public MyTask() {
mutex = 1;
} @Override
public void run() {
synchronized (mutex) {
while(true) {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} }
线程状态:
"t2" prio=10 tid=0x00007f7b2013a800 nid=0x67fb waiting for monitor entry [0x00007f7b17087000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuyan.mountain.test.MyTask.run(App.java:35)
- waiting to lock <0x00000007d6b6ddb8> (a java.lang.Integer)
at java.lang.Thread.run(Thread.java:745) "t1" prio=10 tid=0x00007f7b20139000 nid=0x67fa waiting on condition [0x00007f7b17188000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
t1没有抢到锁,所以显示BLOCKED。t2抢到了锁,但是处于睡眠中,所以显示TIMED_WAITING,有限等待某个条件来唤醒。
把睡眠的代码去掉,线程状态变成了:
"t2" prio=10 tid=0x00007fa0a8102800 nid=0x6a15 waiting for monitor entry [0x00007fa09e37a000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuyan.mountain.test.MyTask.run(App.java:35)
- waiting to lock <0x0000000784206650> (a java.lang.Integer)
at java.lang.Thread.run(Thread.java:745) "t1" prio=10 tid=0x00007fa0a8101000 nid=0x6a14 runnable [0x00007fa09e47b000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(Native Method)
t1显示RUNNABLE,说明正在运行,这里需要额外说明一下,如果这个线程正在查询数据库,但是数据库发生死锁,虽然线程显示在运行,实际上并没有工作,对于IO型的线程别只用线程状态来判断工作是否正常。
把MyTask的代码小改一下,线程拿到锁之后执行wait,释放锁,进入等待区。
public void run() {
synchronized (mutex) {
if(mutex == 1) {
try {
mutex.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
线程状态如下:
"t2" prio=10 tid=0x00007fc5a8112800 nid=0x5a58 in Object.wait() [0x00007fc59b58c000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method) "t1" prio=10 tid=0x00007fc5a8111000 nid=0x5a57 in Object.wait() [0x00007fc59b68d000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
两个线程都显示WAITING,这次是无限期的,需要重新获得锁,所以后面跟了on object monitor。
再来个死锁的例子:
package com.jiuyan.mountain.test; import java.util.concurrent.TimeUnit; /**
* Hello world!
*
*/
public class App { public static void main(String[] args) throws InterruptedException {
MyTask task1 = new MyTask(true);
MyTask task2 = new MyTask(false);
Thread t1 = new Thread(task1);
t1.setName("t1");
Thread t2 = new Thread(task2);
t2.setName("t2");
t1.start();
t2.start();
} } class MyTask implements Runnable { private boolean flag; public MyTask(boolean flag) {
this.flag = flag;
} @Override
public void run() {
if(flag) {
synchronized (Mutex.mutex1) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (Mutex.mutex2) {
System.out.println("ok");
}
}
} else {
synchronized (Mutex.mutex2) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (Mutex.mutex1) {
System.out.println("ok");
}
}
}
} } class Mutex {
public static Integer mutex1 = 1;
public static Integer mutex2 = 2;
}
线程状态:
"t2" prio=10 tid=0x00007f5f9c122800 nid=0x3874 waiting for monitor entry [0x00007f5f67efd000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuyan.mountain.test.MyTask.run(App.java:55)
- waiting to lock <0x00000007d6c45bd8> (a java.lang.Integer)
- locked <0x00000007d6c45be8> (a java.lang.Integer)
at java.lang.Thread.run(Thread.java:745) "t1" prio=10 tid=0x00007f5f9c121000 nid=0x3873 waiting for monitor entry [0x00007f5f67ffe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuyan.mountain.test.MyTask.run(App.java:43)
- waiting to lock <0x00000007d6c45be8> (a java.lang.Integer)
- locked <0x00000007d6c45bd8> (a java.lang.Integer)
at java.lang.Thread.run(Thread.java:745) Found one Java-level deadlock:
=============================
"t2":
waiting to lock monitor 0x00007f5f780062c8 (object 0x00000007d6c45bd8, a java.lang.Integer),
which is held by "t1"
"t1":
waiting to lock monitor 0x00007f5f78004ed8 (object 0x00000007d6c45be8, a java.lang.Integer),
which is held by "t2"
这个有点像哲学家就餐问题,每个线程都持有对方需要的锁,那就运行不下去了。
调优策略
两个基本原则:
- 将转移到老年代的对象数量降到最少。
- 减少Full GC的执行时间。目标是Minor GC时间在100ms以内,Full GC时间在1s以内。
主要调优参数:
设定堆内存大小,这是最基本的。
- -Xms:启动JVM时的堆内存空间。
- -Xmx:堆内存最大限制。
设定新生代大小。
新生代不宜太小,否则会有大量对象涌入老年代。
- -XX:NewRatio:新生代和老年代的占比。
- -XX:NewSize:新生代空间。
- -XX:SurvivorRatio:伊甸园空间和幸存者空间的占比。
- -XX:MaxTenuringThreshold:对象进入老年代的年龄阈值。
设定垃圾回收器
年轻代:-XX:+UseParNewGC。
老年代:-XX:+UseConcMarkSweepGC。
CMS可以将STW时间降到最低,但是不对内存进行压缩,有可能出现“并行模式失败”。比如老年代空间还有300MB空间,但是一些10MB的对象无法被顺序的存储。这时候会触发压缩处理,但是CMS GC模式下的压缩处理时间要比Parallel GC长很多。
G1采用”标记-整理“算法,解决了内存碎片问题,建立了可预测的停顿时间类型,能让使用者指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。
JVM性能优化入门指南的更多相关文章
- 《Java性能优化权威指南》
<Java性能优化权威指南> 基本信息 原书名:Java performance 原出版社: Addison-Wesley Professional 作者: (美)Charlie Hunt ...
- 读书笔记系列之java性能优化权威指南 一 第一章
主题:java性能优化权威指南 pdf 版本:英文版 Java Performance Tuning 忽略:(0~24页)Performance+Acknowledge 1.Strategies, A ...
- JVM性能优化读后笔记
java性能优化权威指南读后笔记 三重境界 1.花似雾中看:对于遇到的额问题还看不清,不知道真真假假,是是非非. 2.悠然见南山:虽然刚开始对这个领域还不清楚,但随着时间推移,你对它有许多自己的见解, ...
- 听说 JVM 性能优化很难?今天我小试了一把!
文章首发于公众号「陈树义」及个人博客 shuyi.tech,欢迎关注访问. 对于 Java 开发的同学来说,JVM 性能优化可以说是比较难掌握的知识点.这不仅因为 JVM 性能优化需要掌握晦涩难懂的 ...
- [原创]Java性能优化权威指南读书思维导图
[原创]Java性能优化权威指南读书思维导图 书名:Java性能优化权威指南 原书名:Java performance 作者: (美)Charlie Hunt Binu John 译者: 柳飞 ...
- jvm性能优化及内存分区
jvm性能优化及内存分区 2012-09-17 15:51:37 分类: Java Some of the default values for Sun JVMs are listed below. ...
- [原创]Java性能优化权威指南读书思维导图4
[原创]Java性能优化权威指南读书思维导图4
- [原创]Java性能优化权威指南读书思维导图3
[原创]Java性能优化权威指南读书思维导图3
- [原创]Java性能优化权威指南读书思维导图2
[原创]Java性能优化权威指南读书思维导图2
随机推荐
- FineUI参考手册(离线版)现已免费提供下载!
http://fineui.com/bbs/forum.php?mod=viewthread&tid=3473
- SQL2005SP4补丁安装时错误: -2146233087 MSDTC 无法读取配置信息。。。错误代码1603的解决办法
是在安装slq2005sp3和sp4补丁的时候碰到的问题. 起先是碰到的错误1603的问题,但网上搜索的1603的解决办法都试过了,google也用了,外文论坛也读了,依然没有能解决这个问题. 其实一 ...
- VS代码管理插件AnkhSvn
下载AnKHSvn软件,启动VS,源代码管理工具选择AnKHSvn 在视图-〉其它窗口-〉Pending Change->提交变化的更改
- 面向OPENCL的ALTERA SDK
面向OPENCL的ALTERA SDK 使用面向开放计算语言 (OpenCL™) 的 Altera® SDK,用户可以抽象出传统的硬件 FPGA 开发流程,采用更快.更高层面的软件开发流程.在基于 x ...
- MD5加密与验证
package com.study; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException ...
- Ceph: validate that the RBD cache is active
Ceph: validate that the RBD cache is active Quick and simple test to validate if the RBD cache is en ...
- Qt做动画旋转旋转图片
今天看到百度首页的音乐播放有个图片可以旋转,感觉很酷就用qt做了一个类似的,关键时刻还是要发挥数学功底,静下心来,写一写,画一画,编程对数学分析能力要求还是不小的,以后要经常锻炼数学分析能力啊! Wi ...
- MySQL数据库my.cnf性能参数如何调优
提供一个MySQL 5.6版本适合在1GB内存VPS上的my.cnf配置文件.配置文件可以到这里下载:: 下载my.cnf [client] port = 3306 socket = /tmp/mys ...
- ActiveMQ_安全配置(五)
如果Activemq不加安全配置,那么任何知道队列所在服务器IP的人都可以发送接收队列消息 安全配置主要是通过配置账号密码增强安全性 参考资料:http://activemq.apache.org/s ...
- 【HDU 2089】不要62
http://acm.hdu.edu.cn/showproblem.php?pid=2089 数位dp,参照了打野的博客 预处理出f数组,f[i][j]表示第i位为数字j时的可行的数字总数. 对于区间 ...