Java进程的dump文件生成与分析

4 篇文章 0 订阅

JavaDump

Java虚拟机的运行时快照。将Java虚拟机运行时的状态和信息保存到文件

线程Dump

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

堆Dump

包含线程Dump,并包含所有堆对象的状态。二进制格式。

制作JavaDump

JVM参数

-XX:+HeapDumpOnOutOfMemoryError

指示虚拟机在发生内存不足错误时,自动生成堆Dump

命令行制作

在JDK的bin目录下,包含了java命令及其他实用工具

  • jps:查看本机的Java进程信息

使用jps查看Java进程ID(PID);Linux下还可以使用ps命令

jps
jps -l
jps -v
ps -ef|grep java
  • jstack:打印线程的栈信息,制作线程Dump。
jstack <进程ID> >> <输出文件>
jstack 2316 >> c:\thread.txt
## Linux下使用Kill命令制作线程Dump,输出线程Dump到目标Java进程的标准输出
kill -quit <进程ID> 
kill -3 <进程ID>
  • jmap:打印内存映射,制作堆Dump

使用jmap命令制作堆Dump

# 打印存活的对象大小和个数
jmap -histo:live <pid>
# 二进制方式存储堆文件
jmap -dump:format=b,file=文件名.hprof <进程ID>
  • jconsole:简易的可视化控制台

  • jvisualvm:功能强大的控制台

  • jstat:性能监控工具

jstat显示java进程的堆内存使用情况和GC情况

0x01-jstat.jpg

jstat -gcutil -pid 100 10
S0   — Heap上的 Survivor space 0 区已使用空间的百分比
S1   — Heap上的 Survivor space 1 区已使用空间的百分比 
E    — Heap上的 Eden space 区已使用空间的百分比 
O    — Heap上的 Old space 区已使用空间的百分比 
P    — Perm space 区已使用空间的百分比 
YGC  — 从应用程序启动到采样时发生 Young GC 的次数 
YGCT – 从应用程序启动到采样时 Young GC 所用的时间(单位秒) 
FGC  — 从应用程序启动到采样时发生 Full GC 的次数 
FGCT – 从应用程序启动到采样时 Full GC 所用的时间(单位秒) -- 总的fullgc时间
GCT  — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
  • jhat:内存分析工具

分析线程dump

线程Dump的内容

0x01-线程dump内容.jpg
制作时间
Java 版本
线程信息:名称、优先级、标识、状态、堆栈
死锁信息:存在直接Java线程的死锁时才包含。
内存信息:使用kill制作时才包含。
堆栈顶部地址: 上图中Object.wait() [0x000000006aecf000] 或者runnable [0x00007f3f04a28000] 或者waiting on condition [0x00007f3f0492b000] 方括号内的就是顶部地址。每个任务处理完成前不会改变

线程信息

0x01-线程信息.jpg

线程状态

NEW: 未启动的。不会出现在Dump中。
RUNNABLE: 在虚拟机内执行的。
BLOCKED: 受阻塞并等待监视器锁。
WATING: 无限期等待另一个线程执行特定操作。
TIMED_WATING: 有时限的等待另一个线程的特定操作。
TERMINATED: 已退出的。

监视器(Monitor)

0x01-监视器.jpg

当使用synchronized定义同步块时时,监视器是用来控制对象的锁的并发访问的结构。

synchronized(obj){
    // 同步块,只允许一个线程进入
}

synchronized void method(){
    // 同步块,只允许一个线程进入
}

监视器:对象锁的访问控制结构。也指对象的锁。
监视器项:线程的代理人。
进入区:表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则进入拥有者;否则则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。
拥有者:表示某一线程成功竞争到对象锁。
等待区:表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。

调用修饰

locked <地址> 目标
waiting to lock <地址> 目标
waiting on <地址> 目标
parking to wait for <地址> 目标
实例锁: (a 类名)——synchronized对象。
类锁:(a Class for 类名)——静态synchronized方法

  • locked
    通过synchronized关键字,成功获取到了对象的锁,成为监视器的拥有者,在临界区内操作。对象锁是可以线程重入的。
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at com..datasource.PooledConnection.prepareStatement

synchronized (conn) { // conn的类型是T4CConnection
// 同步块操作
}
  • waiting to lock

通过synchronized关键字,没有获取到了对象的锁,线程在监视器的进入区等待。在调用栈顶出现,线程状态为Blocked。

at com..impl.CacheHolder.isVisibleIn(CacheHolder.java:165)
- waiting to lock <0x0000000097ba9aa8> (a CacheHolder)
at com..impl.CacheGroup$Index.findHolder
at com..impl.ContextImpl.find
at com..BaseDataCenter.findInfo
synchronized (holder) { // holder的类型是CacheHolder
// 临界区操作
}
  • waiting on
    通过synchronized关键字,成功获取到了对象的锁后,调用了wait方法,进去对象的等待区等待。在调用栈顶出现,线程状态为WAITING或TIMED_WATING。
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000da2defb0> (a WorkingThread)
at com..WorkingManager.getWorkToDo
- locked <0x00000000da2defb0> (a WorkingThread)
at com..WorkingThread.run

synchronized(thread) { 
// 同步块操作……
try {
    thread.wait();
catch(InterruptException e) { /* 中断异常处理 */ }
}
  • parking to wait for
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000eb8f35c8> (a FutureTask$Sync)
at java.util.concurrent.locks.LockSupport.park(LockSupport:156)
...
at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock()

park是基本的线程阻塞原语,不通过监视器在对象上阻塞。
随concurrent包会出现的新的机制,与synchronized体系不同。

synchronized模型相关的调用修饰
  • locked <对象地址> (a 类名)

使用synchronized申请对象锁成功,监视器的拥有者。

  • waiting to lock <对象地址> (a 类名)

使用synchronized申请对象锁未成功,在进入区等待。

  • waiting on <对象地址> (a 类名)

使用synchronized申请对象锁成功后,释放锁并在等待区等待

线程动作

runnable:状态一般为RUNNABLE。
in Object.wait():等待区等待,状态为WAITING或TIMED_WAITING。
waiting for monitor entry:进入区等待,状态为BLOCKED。
waiting on condition:等待区等待、被park。
sleeping:休眠的线程,调用了Thread.sleep()。

分析线程Dump的工具

  • Thread Dump Analyzer (TDA)

可直接加载控制台输出,分析出多个线程Dump。
方便的查看各线程的运行状态,调用栈等信息。
查看异常的监视器及相关线程。
提供过滤器定位指定的线程。

  • IBM Thread and Monitor Dump Analyzer (TMDA)

能够分析虚拟机线程的统计信息。
能够方便查看监视器信息及相关依赖的线程。
能够比较多个线程Dump的线程信息,及监视器信息

分析模式

wait on monitor entry 被阻塞的,肯定有问题
runnable 注意IO线程
in Object.wait() 注意非线程池等待

虚拟机执行Full GC时,会阻塞所有的用户线程。因此,即时获取到同步锁的线程也有可能被阻塞。

"wss-635" waiting for monitor entry
  java.lang.Thread.State: BLOCKED (on object monitor)
    at com..impl.CacheHolder.isVisibleIn(CacheHolder.java:165)
    - locked <0x0000000097ba9aa8> (a com..CacheHolder)

如何找到耗用cpu资源的线程

Liunx系统可以使用ps H -eo user,pid,ppid,tid,time,%cpu,cmd --sort=%cpu 输出线程信息,可以将这些信息输出到文本文件以便核对。
在执行ps命令时,同时做线程dump,然后把ps命令中的tid十进制转换为16进制与线程dump中的nid匹配,就能分析出什么线程在占用大量cpu

分析堆Dump

Java虚拟机的内存模型

0x01-内存划分jpg.jpg

三块:年轻代、年老代、永久代。
新创建的对象放在年轻代。
年轻代垃圾回收后,残余的对象会被移动到年老代。
永久代存放类信息等,一般不会被回收。

堆内存可细分为年轻代(新生代)与年老代(旧生代),年青代又可细分为一个Eden区和两个Survivor区(S0,S1),可通过-XX:SurvivorRatio=8设置Eden区和Survivor区大小,S0:S1:Eden的比例是1:1: 8
对象新创建时,都会分配在Eden区,Eden区满后,会触发young gc,清空Eden区,并将仍存活的对象copy到一个Survivor区,下次young gc时,会将Eden区存活的对象和Survivor区存活的对象一块copy到另一个Survivor区,如此循环,总会保证有一个Survivor区是空的,每在Survivor区熬过1次young gc,则对象年龄增加1岁,默认当年龄增加到15岁时(可通过-XX:MaxTenuringThreshold=15设置),若对象仍存活,则会被转移到年老代,当年老代内存满时则会触发full gc。

垃圾回收(GC)的介绍

  • YoungGen GC,新生代回收,Minor GC针对年轻代的回收。毫秒级别
  • Full GC,全回收,Major GC针对全虚拟机内存——包括年轻代、年老代、永久代——的回收。秒级别。会暂停虚拟机的线程

垃圾回收根、对象的保留大

0x01-GC.jpg

从垃圾回收根开始,不再被引用到的对象即为垃圾对象
GC Root一般为线程、会话等对象
从对象上,能递归引用到的对象的合计大小
非准确数,循环引用等造成

内存不足的错误

OutOfMemoryError年老代内存不足。
OutOfMemoryError:PermGen Space永久带内存不足。
OutOfMemoryError:GC overhead limit exceed垃圾回收时间占用系统运行时间的98%或以上。

分析工具

使用MAT分析堆Dump,查看LeakSuspect及DominatorTree。阅读代码,确定对象引用的错误关联而导致的生命周期错误
MAT下载地址

使用飞行记录

  • 飞行记录启动参数
    -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true

  • 飞行记录制作参数:
    jcmd PID JFR.start duration=300s filename=2018-12-12-01.jfr settings=profile

settings=profile这个参数必须加,不加会使用默认模板制作,有些参数无法收集到

飞行记录主要关注几点:TLAB\垃圾回收\线程转储

TLAB

TLAB的全称是Thread Local Allocation Buffer,即线程本地分配缓存区,这是一个线程专用的内存分配区域。TLAB是每个线程独有的,大部分的不需要共享的对象是可以直接在该区直接分配空间的,这样GC也方便。但是遇到大对象时就需要存在Eden区中。

  • 10
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
jstack生成的Thread Dump日志.docx 系统线程状态 (Native Thread Status) 系统线程有如下状态: deadlock 死锁线程,一般指多个线程调用期间进入了相互资源占用,导致一直等待无法释放的情况。 runnable 一般指该线程正在执行状态中,该线程占用了资源,正在处理某个操作,如通过SQL语句查询数据库、对某个文件进行写入等。 blocked 线程正处于阻塞状态,指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。 waiting on condition 线程正处于等待资源或等待某个条件的发生,具体的原因需要结合下面堆栈信息进行分析。 (1)如果堆栈信息明确是应用代码,则证明该线程正在等待资源,一般是大量读取某种资源且该资源采用了资源锁的情况下,线程进入等待状态,等待资源的读取,或者正在等待其他线程的执行等。 (2)如果发现有大量的线程都正处于这种状态,并且堆栈信息中得知正等待网络读写,这是因为网络阻塞导致线程无法执行,很有可能是一个网络瓶颈的征兆: 网络非常繁忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写; 网络可能是空闲的,但由于路由或防火墙等原因,导致包无法正常到达; 所以一定要结合系统的一些性能观察工具进行综合分析,比如netstat统计单位时间的发送包的数量,看是否很明显超过了所在网络带宽的限制;观察CPU的利用率,看系统态的CPU时间是否明显大于用户态的CPU时间。这些都指向由于网络带宽所限导致的网络瓶颈。 (3)还有一种常见的情况是该线程在 sleep,等待 sleep 的时间到了,将被唤醒。 waiting for monitor entry 或 in Object.wait() Moniter 是Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者class的锁,每个对象都有,也仅有一个 Monitor。 从上图可以看出,每个Monitor在某个时刻只能被一个线程拥有,该线程就是 "Active Thread",而其他线程都是 "Waiting Thread",分别在两个队列 "Entry Set"和"Waint Set"里面等待。其中在 "Entry Set" 中等待的线程状态是 waiting for monitor entry,在 "Wait Set" 中等待的线程状态是 in Object.wait()。 (1)"Entry Set"里面的线程。 我们称被 synchronized 保护起来的代码段为临界区,对应的代码如下: synchronized(obj){} 当一个线程申请进入临界区时,它就进入了 "Entry Set" 队列中,这时候有两种可能性: 该Monitor不被其他线程拥有,"Entry Set"里面也没有其他等待的线程。本线程即成为相应类或者对象的Monitor的Owner,执行临界区里面的代码;此时在Thread Dump中显示线程处于 "Runnable" 状态。 该Monitor被其他线程拥有,本线程在 "Entry Set" 队列中等待。此时在Thread Dump中显示线程处于 "waiting for monity entry" 状态。 临界区的设置是为了保证其内部的代码执行的原子性和完整性,但因为临界区在任何时间只允许线程串行通过,这和我们使用多线程的初衷是相反的。如果在多线程程序中大量使用synchronized,或者不适当的使用它,会造成大量线程在临界区的入口等待,造成系统的性能大幅下降。如果在Thread Dump中发现这个情况,应该审视源码并对其进行改进。 (2)"Wait Set"里面的线程 当线程获得了Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(通常是被synchronized的对象)的wait()方法,放弃Monitor,进入 "Wait Set"队列。只有当别的线程在该对象上调用了 notify()或者notifyAll()方法,"Wait Set"队列中的线程才得到机会去竞争,但是只有一个线程获得对象的Monitor,恢复到运行态。"Wait Set"中的线程在Thread Dump中显示的状态为 in Object.wait()。通常来说, 通常来说,当CPU很忙的时候关注 Runnable 状态的线程,反之则关注 waiting for monitor entry 状态的线程。 JVM线程运行状态 (JVM Thread Status)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值