内存溢出分析之工具篇

1.jmeter

压测工具。可对特定接口进行压测,分析tps、响应时间、CPU、内存等性能指标。
内存溢出分析之工具篇_第1张图片

2.JConsole、JVisualVM

jdk 自带可视化工具,可监控CPU、内存、线程等状况。路径: /Library/Java/JavaVirtualMachines/jdk1.8.0_77.jdk/Contents/Home/bin/
jmx (Java Management Extensions)配置:

-Djava.rmi.server.hostname=xxx
-Dcom.sun.management.jmxremote.port=xxx
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=false 

JConsole

首页概览:
内存溢出分析之工具篇_第2张图片
查看内存:
内存溢出分析之工具篇_第3张图片
查看线程:
内存溢出分析之工具篇_第4张图片
MBean:
内存溢出分析之工具篇_第5张图片

JVisualVM

包括 jmx 监控、dump文件分析等功能

1)监控

连接方式和 JConsole 类似,包括CPU、内存、线程等的监控,以及手动执行GC、生成堆dump文件、线程dump文件等功能
CPU、内存等监控:
内存溢出分析之工具篇_第6张图片
线程监控:
内存溢出分析之工具篇_第7张图片
抽样器:可选择CPU或内存抽样
内存溢出分析之工具篇_第8张图片

2)dump 文件分析

包括系统概要、堆中的类(以及对应的实例)分析、线程分析、OQL查询等内容
打开 dump 文件:
内存溢出分析之工具篇_第9张图片
筛选类:
内存溢出分析之工具篇_第10张图片
查看实例对象:
内存溢出分析之工具篇_第11张图片
也可直接查看垃圾回收根节点:
内存溢出分析之工具篇_第12张图片

3.gperftools

官网:https://github.com/gperftools/gperftools/wiki

google 提供的性能分析工具,包括对应用程序CPU、内存等的性能分析。本次主要介绍 heapprofile 工具分析堆外内存。

heapprofile 工具:监控所有的内存分配和释放,内存分配包括: malloc, calloc, realloc, or, new 。底层实现了 malloc 等方法,监控 malloc 等方法的调用。

安装步骤:

1) 安装gcc

sudo yum -y install gcc make
sudo yum -y install gcc gcc-c++

2) 安装 libunwind

wget http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-beta.tar.gz
./configure --prefix=/tmp/google-perftools/local/libunwind
make
make install

3) 安装 gperftools

wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.5/gperftools-2.5.tar.gz
./configure --prefix=/tmp/google-perftools/local/gperftools-2.5/
make
make install

4) 配置 libunwind lib 路径

vi /etc/ld.so.conf.d/usr_local_lib.conf
添加: /tmp/google-perftools/local/libunwind/lib

5) 启动脚本增加配置

export LD_PRELOAD=/tmp/google-perftools/local/gperftools-2.5/lib/libtcmalloc.so
export HEAPPROFILE=/tmp/google-perftools/local/profile/gzip

6) 用法

/tmp/google-perftools/local/gperftools-2.5/bin/pprof --text /usr/local/java/bin/java gzip_7933.0079.heap > text_7933.0079.heap
/tmp/google-perftools/local/gperftools-2.5/bin/pprof --pdf /usr/local/java/bin/java gzip_16205.0093.heap  > pdf_16205.0093.heap 

使用实例

text 工具:

内存溢出分析之工具篇_第13张图片
含义:
第一列: 使用的直接内存大小
第二列: 第一列所占百分比
第三列: 第二列总和
第四列: 程序和调用者使用的内存大小
第五列: 第四列所占百分比

pdf工具:

内存溢出分析之工具篇_第14张图片

4.MAT

eclipse 出的内存分析工具。下载地址: http://www.eclipse.org/mat/downloads.php
注:此处只做简单介绍,更多功能请自行挖掘。

常用分析:

  1. Leak Suspects: 内存泄露分析,包含泄露疑点、系统概述
  2. Histogram: 列举类的对象数量及大小
  3. Dominator Tree: 列举最大的对象
  4. Thread Overview: 展示线程名称、堆栈等信息
  5. Top Consumers: 按类名和包名打印最大的对象
  6. Top Components: 列举大于堆内存1%的组件报告。组件:某个根包下的类集合或被某个类加载器加载的类
  7. OQL: 对象查询语言

首页

内存溢出分析之工具篇_第15张图片

Leak Suspects: 内存泄露分析,包含泄露疑点、系统概述

内存溢出分析之工具篇_第16张图片
系统概述:
内存溢出分析之工具篇_第17张图片
可能泄露的点:
内存溢出分析之工具篇_第18张图片

Histogram:列举类的对象数量及大小

内存溢出分析之工具篇_第19张图片

Dominator Tree: 列举最大的对象

内存溢出分析之工具篇_第20张图片
如查看 java.nio.Bits 对象,分配了3.4G左右堆外内存:
内存溢出分析之工具篇_第21张图片

Thread Overview: 展示线程名称、堆栈等信息

内存溢出分析之工具篇_第22张图片

OQL 查询:可按指定条件查询对象

内存溢出分析之工具篇_第23张图片
实例:根据 GCRoot.type 属性查询不可达对象
内存溢出分析之工具篇_第24张图片
根据文档显示,GCRoot.Type = 2048 为不可达对象类型,因此 OQL 语句如下:

SELECT s, s.key.toString(), s.@GCRootInfo[0].type FROM com.google.common.cache.LocalCache$StrongAccessWriteEntry s WHERE (s.@GCRootInfo[0].type = 2048)

执行结果为:
内存溢出分析之工具篇_第25张图片

常用操作:

  1. List objects with outgoing references: 该对象引用的对象
  2. List objects with incoming references: 引用该对象的对象
  3. Show objects by class with outgoing references: 该类引用的对象
  4. Show objects by class with incoming references: 引用该类的对象
  5. Path To GC Roots: 查看GC路径
  6. Java Basics

List Objects with incoming references: 引用该对象的对象

内存溢出分析之工具篇_第26张图片

Path To GC Roots: 查看GC路径

内存溢出分析之工具篇_第27张图片

5.btrace

sun推出的一款针对 java 的动态、安全的追踪工具。官网地址:https://github.com/btraceio/btrace/wiki
注:该工具使用不当可能导致JVM崩溃,线上脚本需验证后再用;且脚本一旦运行,只有重启应用或 kill 进程才能停止。

简介

在运行的 java 程序中动态(字节码)的插入追踪代码,并对追踪的程序进行热交换。可查看某个方法入参、调用堆栈、响应时间、内存信息等,还有一些限制,如不能创建对象、抛异常等。下载的 btrace 包里有实例,路径: /btrace/samples
btrace 注解类型: https://github.com/btraceio/btrace/wiki/BTrace-Annotations

实例: @OnMethod

监控调用了 java.nio.ByteBuffer 类的 allocateDirect() 方法,@Location(Kind.ENTRY) 含义: 在进入 allocateDirect() 方法时执行 traceExecute() 逻辑。

代码: trace_direct_memory.java

import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;

import static com.sun.btrace.BTraceUtils.println;

/**
 * btrace 追踪直接内存
 *
 * @author wengliemiao
 */
@BTrace
public class trace_direct_memory {

    @OnMethod(
            clazz = "java.nio.ByteBuffer",
            method = "allocateDirect",
            location = @Location(Kind.ENTRY)
    )
    public static void traceExecute(int capacity) {
        println("========================= invoke java.nio.ByteBuffer.allocateDirect(). =========================");
        println("param: " + capacity);
        BTraceUtils.jstack();
        println("Heap:");
        println(BTraceUtils.Sys.Memory.heapUsage());
        println("Non-Heap:");
        println(BTraceUtils.Sys.Memory.nonHeapUsage());
    }
}

运行脚本: btrace.sh

#!/bin/bash
export BTRACE_HOME=/home/admin/tmp/btrace
export PATH=$BTRACE_HOME/bin:$PATH
btrace  trace_direct_memory.java > result.txt

#dubbo服务可使用如下命令获取 pid
#pid = "$(jps -v | grep "order"|cut -d " " -f1)" 
#btrace $pid $currentDir/scripts/trace_direct_memory.java > result.txt

执行命令:
$ ./btrace.sh
注:如果执行脚本时出现: Invalid path 13609 specified: 2/No such file or directory
则说明可能有多个 btrace 命令,此时可以指定 btrace 路径。
运行结果:

========================= invoke java.nio.ByteBuffer.allocateDirect(). =========================
param: 1024
java.nio.ByteBuffer.allocateDirect(ByteBuffer.java)
org.jboss.netty.channel.socket.nio.SocketReceiveBufferPool.acquire(SocketReceiveBufferPool.java:61)
org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:319)
org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:280)
org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:200)
org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
java.lang.Thread.run(Thread.java:748)
Heap:
init = 1073741824(1048576K) used = 144381584(140997K) committed = 1038876672(1014528K) max = 1038876672(1014528K)
Non-Heap:
init = 2555904(2496K) used = 90639560(88515K) committed = 119865344(117056K) max = 1862270976(1818624K)

6.gdb

gdb 可结合 top、pmap 等工具查看进程内存块的内容。
注:该工具会导致应用程序挂起,线上慎用。

相关工具介绍

/proc/${pid}/ 目录下 smaps 等文件查看内存占用相关信息

如: /proc/23282/smaps
内存溢出分析之工具篇_第28张图片

pmap 查看进程的内存映射关系

如:pmap -x 23282
内存溢出分析之工具篇_第29张图片

你可能感兴趣的:(Java,基础)