<meta charset="utf-8">
1. 使用 Mat 查找问题
jmap -dump:live,format=b,file=[文件名字] pid
例如: jmap -dump:live,format=b,file=heap.hprof 16038
十分钟导出一次分析文件, 导出多个文件使用 mat 进行对比
查看 Leak Suspects 中图形的灰色区域
如果灰色区域 (Remainder) 变大, 说明内存进行了回收, 系统正常, 进行 jvm 调优即可
如果灰色区域 (Remainder) 变小, 说明内存有泄漏, 进行排查
查看 Problem Suspect x 即可. 点击 details 查看详细内容
在 Class name 中可以查看到是对应的 java 类
例如:
clipboard.PNG
2. 使用 jstack 查找问题
jstack pid
打印内容到文件: jstack pid > 文件名
文件里值得关注的呢内容
死锁, Deadlock(重点关注)
执行中, Runnable
等待资源, Waiting on condition(重点关注)
等待获取监视器, Waiting on monitor entry(重点关注)
暂停, Suspended
对象等待中, Object.wait() 或 TIMED_WAITING
阻塞, Blocked(重点关注)
停止, Parked
可以查看到造成死锁, 和进行等待的线程. 根据文件内容可以排查出我们的问题点.
例子: 由于我这个是正常的线程所以没有对应的要等待的文件
clipboard.PNG
3.jvm 调优
java 垃圾回收策略. PNG
详细参数查看另一篇文章
最后调优结果:
未调优前, 内存占比 40%
调优后: 内存占比 8%
调优参数
JAVA_OPTS="-server -Xms4G -Xmx4G -Xss512k -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:CMSFullGCsBeforeCompaction=5 -XX:+HeapDumpOnOutOfMemoryError -Xloggc:/home/xijie/local/apache-tomcat-7.0.75/logs/gc.log -XX:+PrintGCDetails"
查看 gc 的情况,
jstat -gc 12538 5000 查看 gc 状态, 每 5 秒执行一次
显示内容说明如下(部分结果是通过其他其他参数显示的, 暂不说明):
S0C: 年轻代中第一个 survivor(幸存区)的容量 (字节)
S1C: 年轻代中第二个 survivor(幸存区)的容量 (字节)
S0U: 年轻代中第一个 survivor(幸存区)目前已使用空间 (字节)
S1U: 年轻代中第二个 survivor(幸存区)目前已使用空间 (字节)
EC: 年轻代中 Eden(伊甸园)的容量 (字节)
EU: 年轻代中 Eden(伊甸园)目前已使用空间 (字节)
OC:Old 代的容量 (字节)
OU:Old 代目前已使用空间 (字节)
PC:Perm(持久代)的容量 (字节)
PU:Perm(持久代)目前已使用空间 (字节)
YGC: 从应用程序启动到采样时年轻代中 gc 次数
YGCT: 从应用程序启动到采样时年轻代中 gc 所用时间(s)
FGC: 从应用程序启动到采样时 old 代(全 gc)gc 次数
FGCT: 从应用程序启动到采样时 old 代(全 gc)gc 所用时间(s)
GCT: 从应用程序启动到采样时 gc 用的总时间(s)
NGCMN: 年轻代 (young) 中初始化 (最小) 的大小 (字节)
NGCMX: 年轻代 (young) 的最大容量 (字节)
NGC: 年轻代 (young) 中当前的容量 (字节)
OGCMN:old 代中初始化 (最小) 的大小 (字节)
OGCMX:old 代的最大容量 (字节)
OGC:old 代当前新生成的容量 (字节)
PGCMN:perm 代中初始化 (最小) 的大小 (字节)
PGCMX:perm 代的最大容量 (字节)
PGC:perm 代当前新生成的容量 (字节)
S0: 年轻代中第一个 survivor(幸存区)已使用的占当前容量百分比
S1: 年轻代中第二个 survivor(幸存区)已使用的占当前容量百分比
E: 年轻代中 Eden(伊甸园)已使用的占当前容量百分比
O:old 代已使用的占当前容量百分比
P:perm 代已使用的占当前容量百分比
S0CMX: 年轻代中第一个 survivor(幸存区)的最大容量 (字节)
S1CMX : 年轻代中第二个 survivor(幸存区)的最大容量 (字节)
ECMX: 年轻代中 Eden(伊甸园)的最大容量 (字节)
DSS: 当前需要 survivor(幸存区)的容量 (字节)(Eden 区已满)
TT: 持有次数限制
MTT : 最大持有次数限制
扩展
命令 作用
jps 基础工具
jstack 查看某个 Java 进程内的线程堆栈信息
jmap jmap 导出堆内存, 然后使用 jhat 来进行分析
jhat jmap 导出堆内存, 然后使用 jhat 来进行分析
jstat JVM 统计监测工具
hprof hprof 能够展现 CPU 使用率, 统计堆内存使用情况
jstack 主要用来查看某个 Java 进程内的线程堆栈信息. 语法格式如下:
- jstack [option] pid
- jstack [option] executable core
- jstack [option] [server-id@]remote-hostname-or-ip
参数 作用
-l long listings, 会打印出额外的锁信息, 在发生死锁时可以用 jstack -l pid 来观察锁持有情况
-m mixed mode, 不仅会输出 Java 堆栈信息, 还会输出 C/C++ 堆栈信息(比如 Native 方法)
使用例子:
- [esv@bz3esvbs0ap1001 ~]$ jstack 46924
- 2017-09-18 15:23:52
- Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):
- "Attach Listener" #12295 daemon prio=9 os_prio=0 tid=0x00007fc9d8019000 nid=0x2656 waiting on condition [0x0000000000000000]
- java.lang.Thread.State: RUNNABLE
- "nioEventLoopGroup-87-1" #12290 prio=10 os_prio=0 tid=0x00007fc9bc21a800 nid=0x72e runnable [0x00007fc9dc5b8000]
- java.lang.Thread.State: RUNNABLE
- at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
- at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
- at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
- at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x00000000ca3170a0> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x00000000ca3170c0> (a java.util.Collections$UnmodifiableSet)
- locked <0x00000000ca317058> (a sun.nio.ch.EPollSelectorImpl)
- at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
- at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:622)
- at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:310)
- at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
- at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
- at java.lang.Thread.run(Thread.java:748)
- "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fca0c07e800 nid=0xb752 in Object.wait() [0x00007fc9fcdfc000]
- java.lang.Thread.State: WAITING (on object monitor)
- at java.lang.Object.wait(Native Method)
- at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000c4276228> (a java.lang.ref.ReferenceQueue$Lock)
- at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
- at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
- "main" #1 prio=5 os_prio=0 tid=0x00007fca0c008800 nid=0xb74d runnable [0x00007fca12939000]
- java.lang.Thread.State: RUNNABLE
- at java.NET.PlainSocketImpl.socketAccept(Native Method)
- at java.NET.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
- at java.NET.ServerSocket.implAccept(ServerSocket.java:545)
- at java.NET.ServerSocket.accept(ServerSocket.java:513)
- at org.apache.catalina.core.StandardServer.await(StandardServer.java:451)
- at org.apache.catalina.startup.Catalina.await(Catalina.java:777)
- at org.apache.catalina.startup.Catalina.start(Catalina.java:723)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:498)
- at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:321)
- at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:455)
- "VM Thread" os_prio=0 tid=0x00007fca0c072800 nid=0xb750 runnable
- "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fca0c01e000 nid=0xb74e runnable
- "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fca0c01f800 nid=0xb74f runnable
- "VM Periodic Task Thread" os_prio=0 tid=0x00007fca0c0d6000 nid=0xb757 waiting on condition
- JNI global references: 245
另外, jstack 可以定位到线程堆栈, 根据堆栈信息我们可以定位到具体代码, 所以它在 JVM 性能调优中使用得非常多.
用例:
1. 使用 jps 命令, 获取需要调优的进程 id 为 46924.
2. 使用 top -Hp 46924 命令获得最耗费资源的线程号(pid), TIME 列就是各个 Java 线程耗费的 CPU 时间, 这里我们选 58767 线程作为例子.
来源: http://www.jianshu.com/p/b5f70c4d00e8