基于SA,实现一个对象统计工具
2018-04-30 本文已影响4人
志哥666
前几天分享了一下基于Attach API实现一个jstack功能的demo, 于是有圈友私下问我,什么是SA?如果基于SA如何实现jstack?
本篇片呢就介绍下SA,以及使用SA来开发一个统计JVM堆里对象的工具。
简介
SA全称Serviceability Agent,它是hotspot虚拟机开发人员,为了调试虚拟机而开发的一个组件。它是利用操作系统底层API来读取一个进程的数据。
它与Attach API最大的不同是:attach通过连接到目标JVM进程,让目标JVM进程执行相关命令来实现;而sa是独立于目标JVM进程运行。
之前我们介绍的HSDB就是基于SA来实现的。SA只能支持读取一个进程的快照信息,换句话说,当采用SA连接到目标进程时,目标进程会暂停住,直到断开连接后目标进程才会继续运行。所以切勿在线上环境使用基于SA的工具。
对象统计工具
有了以上对SA的了解,接下来就看看如何基于SA开发一个统计对象的工具。
jdk通过sa-jdi.jar提供了相关的工具类,我们可以基于这些类很简单的实现这个功能。直接上代码:
public class TestSA extends Tool {
@Override
public void run() {
Map<Klass,Value> map = new HashMap<>();
ObjectHeap objectHeap = VM.getVM().getObjectHeap();
objectHeap.iterate(new HeapVisitor() {//遍历堆里的对象
@Override
public void prologue(long l) {
}
@Override
public boolean doObj(Oop oop) {
Klass klass = oop.getKlass();//获取对象的class
Value value = map.get(klass);
if(value == null){
value = new Value();
map.put(klass,value);
}
value.increase();//对象数目加一
value.add(oop.getObjectSize());//对象大小
return false;
}
@Override
public void epilogue() {
}
});
for(Map.Entry<Klass,Value> entry:map.entrySet()){
StringBuilder sb = new StringBuilder("name:");
sb.append(entry.getKey().getName().asString())
.append("---count:").append(entry.getValue().getCount())
.append("---size:").append(entry.getValue().getSize());
System.out.println(sb.toString());
}
}
private class Value{
private int count = 0;
private long size = 0;
public void increase(){
count++;
}
public void add(long size){
this.size+=size;
}
public int getCount(){
return count;
}
public long getSize(){
return size;
}
}
public static void main(String[] args) {
TestSA testSA = new TestSA();
testSA.execute(args);
}
}
我们只需要继承Tool类(一定是sun.jvm.hotspot.tools.Tool哦),然后重写run方法,在run方法里调用相关api来获取数据。
运行这个类时,记得要用管理员权限哦,还要指定要统计的jvm进程的pid。如下是我运行这个类的命令:
sudo java -cp .:$JAVA_HOME/lib/sa-jdi.jar sa.TestSA 1288
还需要强调的是,这个命令要在sa.TestSA类所在的目录下运行,否则会报找不到TestSA的错误。
总结
就这么简单,你就可以实现一个统计堆里对象数以及对象大小的工具。
加入知识星球,可以有更多的交流,更多的学习和更快的提高。