github: heapaudit
官方介绍: HeapAudit - JVM Memory Profiler for the Real World
使用方法##
跟btrace等一个路数, 实现了attach功能和随java程序启动的agent功能.attach
对于已经启动的Java程序, 拿到pid后执行java -Xbootclasspath/a:/usr/lib/jvm/java-openjdk/lib/tools.jar -jar heapaudit-1.1.5.jar $java_pid -Itest.HeapTest@test.+
Press <enter> to exit HeapAudit...
HEAP: test/HeapTest@test()V x1
- char[3] (24 bytes) x144
- java.lang.Integer[133] (551 bytes) x21
- java.lang.Integer[91] (380 bytes) x64
- java.lang.Integer[43] (188 bytes) x32
- java.lang.Integer[19] (92 bytes) x16
- java.lang.Integer[7] (44 bytes) x8
- java.lang.Integer[1] (22 bytes) x3
- java.lang.StringBuilder (24 bytes) x144
- java.util.HashMap (56 bytes) x1
- java.util.HashMap$Entry[256] (1040 bytes) x2
- java.util.HashMap$Entry[128] (528 bytes) x2
- java.util.HashMap$Entry[64] (272 bytes) x2
- java.util.HashMap$Entry[32] (144 bytes) x2
- java.util.HashMap$Entry[16] (80 bytes) x1
- java.util.HashMap$Entry (32 bytes) x288
- 由于使用到了attach api, 需要找到tools.jar位置. 一般是$JAVA_HOME/lib/tools.jar
- -Itest.HeapTest@test.+ 代表匹配class name为test.HeapTest 方法名为testXXX的方法
- 还有一个有用的选项是-Xtimetou=X 代表几秒后自动断开, 非常实用
- HeapAudit 在退出时会自动恢复修改过的bytecode. 性能损耗很小
随Java程序启动
启动是带上-javaagent 参数java -javaagent:heapaudit.jar XXX
这种实现的目的是在要profile的代码中已经显试加上了profile的代码, 如下:HeapQuantile r = new HeapQuantile();
// 这里仅仅统计当前线程, 使用HeapRecorder.Threading.Local
HeapRecorder.register(r, HeapRecorder.Threading.Local);
MyObject o = new MyObject();
HeapRecorder.unregister(r, HeapRecorder.Threading.Local);
for (HeapQuantile.Stats s: r.tally(HeapRecorder.Threading.Local, true)){
System.out.println(s);
}
这种是侵入性非常大的方式. 必须在代码中引入heapaudit 依赖并添加代码. 灵活性非常低.比较两种方式优劣
| factor | attach | javaagent启动 |
|---|---|---|
| 侵入性 | 低 | 高, 需要修改业务代码, 启动脚本 |
| 性能损耗 | same | same |
| 全局统计 | 支持 | 支持, coding 指定, 无法修改 |
| 按照线程统计 | 不支持 | 支持, coding 指定, 无法修改 |
Alternative
大多数情况下, 我们想知道单次用户请求某个方法分配的内存情况. 并且随着trouble shooting的深入, 需要profile的类肯定会变化. 因此我们需要attach + 单线程统计功能第一个想到的是给HeapAudit提pull request. 不过没有来得及. 第二个就是请出btrace. 关闭unsafe功能后, 直接插入代码. 需要在应用启动时加入heapaudito 的 javaagent 关于关闭btrace的unsafe功能, 参见这里. 建议使用老版本btrace. 刚才尝试新版本btrace无法关闭unsafe.
- 启动目标java 程序:
java -javaagent:heapaudit-1.1.5.jar -cp test.jar test.HeapTest
- attach 上去:
btrace -cp heapaudit-1.1.5.jar $(jps |grep HeapTest|awk '{print $1}') HeapTest_Script.java
// HeapTest_Script.java
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
import com.foursquare.heapaudit.*;
import com.foursquare.heapaudit.HeapRecorder;
@BTrace
public class HeapTest_Script {
@TLS
private static HeapQuantile recorder;
@OnMethod(clazz = "test.HeapTest", method = "test")
public static void onCall_test() {
recorder = new HeapQuantile();
HeapRecorder.register(recorder, HeapRecorder.Threading.Local);
}
@OnMethod(clazz = "test.HeapTest", method = "test", location = @Location(Kind.RETURN))
public static void onReturn_test() {
println(currentThread().getName() + " get test");
HeapRecorder.unregister(recorder, HeapRecorder.Threading.Local);
for (HeapQuantile.Stats s: recorder.tally(HeapRecorder.Threading.Local, true)){
println(s);
}
}
}
总结
好了. 可以quick && dirty的满足需求了.--EOF--