分类 Troubleshooting 相关 下的文章

使用MAT 分析heap dump

  1. 下载及设置
    官方下载: https://www.eclipse.org/mat/
    JDK 最好用最新的 JDK, 因为最新的基本优化最多.
    根据你分析的 heap dump 的大小, 有时候需要调整 MAT 的 heap 的大小. 这个参数在 MAT 根目录的 MemoryAnalyzer.ini 文件里面. 我经常分析 30G 以下的 heap, 基本设置为 27G, 是 JVM 使用压缩指针来加速.
    sjc-sreop-001.png

    另外, 对于 HPROF 的 dump 来说, 我经常设置为非严格 parse, 因为有时候有点错误, 不影响分析:
    sjc-sreop-002.png

    文档: MAT 自带文档在 Help -> Help Contents 菜单里面, 或者在线版本的文档

  2. 分析
    正常情况下当你打开一个 heap dump 之后, 它会问你是否自动诊断内存泄漏, 如果你不是为了诊断内存泄漏, 可以取消这步.
    Histogram: 是按照类的实例数量聚集, 能很快发现包含大量实例的类. 一般情况下 char[] 或者 String 都在最上面, 这基本没有问题.
    Dominator Tree: 对于诊断内存泄漏非常有用, 如果能抓到一个对象 dominate 很多实例, 基本你找到了问题所在.
    OQL: 就像查询数据库的 SQL 语言, 非常方便的查找任何对象, 实例;
    Threads: 查看当前heap的所有线程, 对于发现某个对象是怎么被创建, 或引用的非常有帮助.

更多关于如何使用 OQL: Java heap dump OQL samples - where
------ 未完待续 -------

jmap & jhat

jmap 用来输出 JVM 的heap 相关信息, 或者生成 heap dump. 可以对一个正在运行的 JVM 进程使用这个命令, 也可以对一个 core dump. 它的功能基本都被 jcmd 命令所替代

jmap -histo <pid>  //jcmd <pid> help GC.class_histogram
jmap -histo:live <pid>   //jcmd <pid> help GC.class_histogram -all
jmap -clstats <pid>   //jcmd <pid> GC.class_stats
jmap -finalizerinfo <pid>  //jcmd <pid> GC.finalizer_info
jmap -dump:format=b,file=/tmp/heapdump.hprfo <pid>  //jcmd <pid> GC.heap_dump
jmap -dump:live,format=b,file=/tmp/heapdump.hprfo <pid>  //jcmd <pid> GC.heap_dump -all

jhat 对 heap dump 分析, 然后起一个本地 web 服务器, 开 7000 端口, 使用户可以在浏览器通过 OQL 查看 heap 里面的信息. 因为功能不够强大, 基本都用其他工具, 比如 MAT 或 Java VisualVM.

java.util.concurrent.ReentrantLock 与 synchronized 的对比

两者都可以实现同步, 在有些方面二者还是有很多区别. synchronized 是 java 的一个语法级别的特性, ReentrantLock 是 util 里面的一个辅助类.

相同点:

  1. 二者都可以实现锁;
  2. 二者都可以实现 生产者/消费者模式 wait/notify/notifyAll, await/signal/signalAll

不同点:

  1. synchronized 必须是一对 {} 中间的部分, 2 个 synchronized 的代码块无法交叉, ReentrantLock 的写法更灵活, 可以交叉;
  2. Lock 可以设置是否回应 interrupt;
  3. Lock 可以使用 tryLock() 如果不能获得锁, 立马返回;
  4. Lock 可以使用 tryLock(timeout) 可以设置尝试获得锁的最长等待时间;
  5. 一个 Lock 可以通过 newCondition 设置多个条件队列, 让他们等待不同的事件;
  6. ReentrantLock 可以设置是否是公平锁;
  7. synchronized 在 JDK 6 优化之后, 可以实现锁粗化, 偏向锁, 根据逃逸分析锁消除;

如何生成 java heap dump

Java heap 是某个时间点上 JVM 内存的一个瞬时镜像(snapshot), 通过工具查看内存里面的各种对象以及他们之间的关系, 对于分析内存问题非常有帮助. 常见的 heap dump 都是二进制的 hprof 格式, 获得之后一般通过 jhat, JVisualVM 或者 MAT 分析. 那么第一步, 如何获得 heap dump 呢? 本文将介绍常见的获得 heap dump 的一些方法.

  1. jcmd
    jcmd 是 jdk 自带的一个小工具, 推荐使用. 要通过 jcmd 获得 heap dump, 首先要得到该进程的 ID. 获得进程 ID 之后, 可以通过 jcmd 命令获得 heap dump.
    LM-SHC-16501315:Downloads xiatian$ jcmd
    42596 
    98797 sun.tools.jcmd.JCmd
    LM-SHC-16501315:Downloads xiatian$ jcmd 42596 GC.heap_dump /tmp/heap.hprof
    42596:
    Heap dump file created
  1. jmap
    jmap 是 JDK 从早期开始一直附带的一个小工具, 使用下面的命令来获取 heap dump:
    jmap -dump:[live],format=b,file=<file-path>
    其中的 live 是可选, 代表是否包含即将被 GC 的对象, 若包含 live, 则不包含即将被 GC 的对象.
    在我的使用经验中, 有时候 jmap 会出现不能 attach 到目标进程的问题.
    例子:
LM-SHC-16501315:Downloads xiatian$ jmap -dump:live,format=b,file=/tmp/heapdump.hprof 42596
Dumping heap to /private/tmp/heapdump.hprof ...
Heap dump file created
  1. JVisualVM
    若在桌面环境, 并且已经安装 JDK, 可以使用 JDK 自带 GUI 工具 JVisualVM.
    heap.png

  2. JConsole
    JConsole 也是 JDK 自带 GUI 工具
    jconsole.png

  3. 可以在 JVM 启动时添加如下参数, 当发生 OOM 时候, 自动产生 heap dump:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-or-dir-path>
  1. 编程的方式: 可以通过使用 MBean 去操作, 产生 heap dump.
public static void dumpHeap(String filePath, boolean live) throws IOException {
    MBeanServer server = ManagementFactory.getPlatformMBeanServer();
    HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(
      server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
    mxBean.dumpHeap(filePath, live);
}

有时候, 你会看到 core dump 和 heap dump, 2个不一样的dump, 那么2个区别是什么呢?
Java 常见的三种 dump 文件: Core Dump, heap dump, thread dump

jstack

jstack 用来打印当前 Java 进程的所有 stacktrace 信息, 包括每层栈的类名, 方法名, 行号, 以及线程的状态, 当前线程拥有的锁, 还能打印部分 native stracktrace.

jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@]remote-hostname-or-IP

最常使用的还是跟 pid 去打印当钱进程的栈. 比如:

jstack -l 42596
jstack -l 42596 > /tmp/stack.txt
jstack -m 42596 // 打印包括 native 栈在内的所有栈. 
jstack -F 42956 //Solaris & Linux 上 force 打印栈

这个功能可以通过最新的 jcmd 来实现.

jcmd  42596 Thread.print