mirror of
https://gitee.com/arthas/arthas.git
synced 2024-11-30 03:07:37 +08:00
return array in VmTool native method, fix jni memory leak problem #1781
This commit is contained in:
parent
932340eac6
commit
6623ef44c6
@ -1,7 +1,5 @@
|
||||
package arthas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author ZhangZiCheng 2021-02-12
|
||||
* @author hengyunabc 2021-04-26
|
||||
@ -46,7 +44,7 @@ public class VmTool implements VmToolMXBean {
|
||||
/**
|
||||
* 获取某个class在jvm中当前所有存活实例
|
||||
*/
|
||||
private static native <T> ArrayList<T> getInstances0(Class<T> klass);
|
||||
private static native <T> T[] getInstances0(Class<T> klass);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
|
||||
@ -66,13 +64,12 @@ public class VmTool implements VmToolMXBean {
|
||||
/**
|
||||
* 获取所有已加载的类
|
||||
*/
|
||||
private static native ArrayList<Class<?>> getAllLoadedClasses0();
|
||||
private static native Class<?>[] getAllLoadedClasses0();
|
||||
|
||||
/**
|
||||
* 包括小类型(如int)
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public static ArrayList<Class> getAllClasses() {
|
||||
public static Class<?>[] getAllClasses() {
|
||||
return getInstances0(Class.class);
|
||||
}
|
||||
|
||||
@ -82,7 +79,7 @@ public class VmTool implements VmToolMXBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ArrayList<T> getInstances(Class<T> klass) {
|
||||
public <T> T[] getInstances(Class<T> klass) {
|
||||
return getInstances0(klass);
|
||||
}
|
||||
|
||||
@ -102,7 +99,7 @@ public class VmTool implements VmToolMXBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<Class<?>> getAllLoadedClasses() {
|
||||
public Class<?>[] getAllLoadedClasses() {
|
||||
return getAllLoadedClasses0();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package arthas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* VmTool interface for JMX server. How to register VmTool MBean:
|
||||
*
|
||||
@ -24,7 +22,7 @@ public interface VmToolMXBean {
|
||||
/**
|
||||
* 获取某个class在jvm中当前所有存活实例
|
||||
*/
|
||||
public <T> ArrayList<T> getInstances(Class<T> klass);
|
||||
public <T> T[] getInstances(Class<T> klass);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
|
||||
@ -44,5 +42,5 @@ public interface VmToolMXBean {
|
||||
/**
|
||||
* 获取所有已加载的类
|
||||
*/
|
||||
public ArrayList<Class<?>> getAllLoadedClasses();
|
||||
public Class<?>[] getAllLoadedClasses();
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: arthas_VmTool
|
||||
* Method: check
|
||||
* Method: check0
|
||||
* Signature: ()Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0
|
||||
@ -17,42 +17,42 @@ JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0
|
||||
|
||||
/*
|
||||
* Class: arthas_VmTool
|
||||
* Method: getInstances
|
||||
* Signature: (Ljava/lang/Class;)Ljava/util/ArrayList;
|
||||
* Method: getInstances0
|
||||
* Signature: (Ljava/lang/Class;)[Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_arthas_VmTool_getInstances
|
||||
JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getInstances0
|
||||
(JNIEnv *, jclass, jclass);
|
||||
|
||||
/*
|
||||
* Class: arthas_VmTool
|
||||
* Method: sumInstanceSize
|
||||
* Method: sumInstanceSize0
|
||||
* Signature: (Ljava/lang/Class;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize
|
||||
JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize0
|
||||
(JNIEnv *, jclass, jclass);
|
||||
|
||||
/*
|
||||
* Class: arthas_VmTool
|
||||
* Method: getInstanceSize
|
||||
* Method: getInstanceSize0
|
||||
* Signature: (Ljava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize
|
||||
JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0
|
||||
(JNIEnv *, jclass, jobject);
|
||||
|
||||
/*
|
||||
* Class: arthas_VmTool
|
||||
* Method: countInstances
|
||||
* Method: countInstances0
|
||||
* Signature: (Ljava/lang/Class;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances
|
||||
JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances0
|
||||
(JNIEnv *, jclass, jclass);
|
||||
|
||||
/*
|
||||
* Class: arthas_VmTool
|
||||
* Method: getAllLoadedClasses
|
||||
* Signature: ()Ljava/util/ArrayList;
|
||||
* Method: getAllLoadedClasses0
|
||||
* Signature: ()[Ljava/lang/Class;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses
|
||||
JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -4,6 +4,21 @@
|
||||
#include <jvmti.h>
|
||||
#include "arthas_VmTool.h"
|
||||
|
||||
//缓存
|
||||
static jclass cachedClass = NULL;
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jclass JNICALL getClass(JNIEnv *env) {
|
||||
if (cachedClass == NULL) {
|
||||
//通过其签名找到Class的Class
|
||||
jclass theClass = env->FindClass("java/lang/Class");
|
||||
//放入缓存
|
||||
cachedClass = static_cast<jclass>(env->NewGlobalRef(theClass));
|
||||
env->DeleteLocalRef(theClass);
|
||||
}
|
||||
return cachedClass;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_arthas_VmTool_check0(JNIEnv *env, jclass thisClass) {
|
||||
@ -46,7 +61,7 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data)
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) {
|
||||
|
||||
jvmtiEnv *jvmti = getJvmtiEnv(env);
|
||||
@ -75,15 +90,13 @@ Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
//通过其签名找到ArrayList的Class
|
||||
jclass arrayListClass = env->FindClass("java/util/ArrayList");
|
||||
jobject arrayList = createJavaInstance(env, arrayListClass);
|
||||
jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
//添加元素到ArrayList实例
|
||||
jobjectArray array = env->NewObjectArray(count, klass, NULL);
|
||||
//添加元素到数组
|
||||
for (int i = 0; i < count; i++) {
|
||||
env->CallObjectMethod(arrayList, addMethod, instances[i]);
|
||||
env->SetObjectArrayElement(array, i, instances[i]);
|
||||
}
|
||||
return arrayList;
|
||||
jvmti->Deallocate(reinterpret_cast<unsigned char *>(instances));
|
||||
return array;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@ -122,6 +135,7 @@ Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass)
|
||||
jvmti->GetObjectSize(instances[i], &size);
|
||||
sum = sum + size;
|
||||
}
|
||||
jvmti->Deallocate(reinterpret_cast<unsigned char *>(instances));
|
||||
return sum;
|
||||
}
|
||||
|
||||
@ -172,7 +186,7 @@ Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass)
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses0
|
||||
JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0
|
||||
(JNIEnv *env, jclass thisClass) {
|
||||
|
||||
jvmtiEnv *jvmti = getJvmtiEnv(env);
|
||||
@ -186,13 +200,11 @@ JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses0
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
//通过其签名找到ArrayList的Class
|
||||
jclass arrayListClass = env->FindClass("java/util/ArrayList");
|
||||
jobject arrayList = createJavaInstance(env, arrayListClass);
|
||||
jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
//添加元素到ArrayList实例
|
||||
jobjectArray array = env->NewObjectArray(count, getClass(env), NULL);
|
||||
//添加元素到数组
|
||||
for (int i = 0; i < count; i++) {
|
||||
env->CallObjectMethod(arrayList, addMethod, classes[i]);
|
||||
env->SetObjectArrayElement(array, i, classes[i]);
|
||||
}
|
||||
return arrayList;
|
||||
jvmti->Deallocate(reinterpret_cast<unsigned char *>(classes));
|
||||
return array;
|
||||
}
|
@ -6,8 +6,11 @@ import com.taobao.arthas.common.VmToolUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 以下本地测试的jvm参数均为:-Xms128m -Xmx128m
|
||||
*/
|
||||
public class VmToolTest {
|
||||
|
||||
/**
|
||||
@ -22,23 +25,18 @@ public class VmToolTest {
|
||||
* after instances->[]
|
||||
*/
|
||||
@Test
|
||||
public void test01() {
|
||||
public void testIsSnapshot() {
|
||||
try {
|
||||
String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
System.err.println(path);
|
||||
|
||||
String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath();
|
||||
VmTool vmtool = VmTool.getInstance(libPath);
|
||||
|
||||
VmTool vmtool = initVmTool();
|
||||
//调用native方法,获取已加载的类,不包括小类型(如int)
|
||||
ArrayList<Class<?>> allLoadedClasses = vmtool.getAllLoadedClasses();
|
||||
System.out.println("allLoadedClasses->" + allLoadedClasses.size());
|
||||
Class<?>[] allLoadedClasses = vmtool.getAllLoadedClasses();
|
||||
System.out.println("allLoadedClasses->" + allLoadedClasses.length);
|
||||
|
||||
//通过下面的例子,可以看到getInstances(Class<T> klass)拿到的是当前存活的所有对象
|
||||
WeakReference<VmToolTest> weakReference1 = new WeakReference<VmToolTest>(new VmToolTest());
|
||||
WeakReference<VmToolTest> weakReference2 = new WeakReference<VmToolTest>(new VmToolTest());
|
||||
System.out.println(weakReference1.get() + " " + weakReference2.get());
|
||||
ArrayList<VmTool> beforeInstances = vmtool.getInstances(VmTool.class);
|
||||
VmTool[] beforeInstances = vmtool.getInstances(VmTool.class);
|
||||
System.out.println("before instances->" + beforeInstances);
|
||||
System.out.println("size->" + vmtool.getInstanceSize(weakReference1.get()));
|
||||
System.out.println("count->" + vmtool.countInstances(VmTool.class));
|
||||
@ -48,10 +46,102 @@ public class VmToolTest {
|
||||
System.gc();
|
||||
Thread.sleep(100);
|
||||
System.out.println(weakReference1.get() + " " + weakReference2.get());
|
||||
ArrayList<VmTool> afterInstances = vmtool.getInstances(VmTool.class);
|
||||
VmTool[] afterInstances = vmtool.getInstances(VmTool.class);
|
||||
System.out.println("after instances->" + afterInstances);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private VmTool initVmTool() {
|
||||
String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
System.err.println(path);
|
||||
|
||||
String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath();
|
||||
return VmTool.getInstance(libPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInstancesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
WeakReference<Object[]> reference = new WeakReference<Object[]>(vmtool.getInstances(Object.class));
|
||||
Object[] instances = reference.get();
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " instance size:" + (instances == null ? 0 : instances.length) + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
instances = null;
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSumInstancesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
long sum = vmtool.sumInstanceSize(Object.class);
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " sum:" + sum + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCountInstancesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
long count = vmtool.countInstances(Object.class);
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " count:" + count + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllLoadedClassesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
Class<?>[] allLoadedClasses = vmtool.getAllLoadedClasses();
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " class size:" + allLoadedClasses.length + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
allLoadedClasses = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import arthas.VmTool;
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc 2021-04-27
|
||||
* @author ZhangZiCheng 2021-04-29
|
||||
*
|
||||
*/
|
||||
//@formatter:off
|
||||
@ -163,7 +164,7 @@ public class VmToolCommand extends AnnotatedCommand {
|
||||
process.end(-1, "Found more than one class: " + matchedClasses + ".");
|
||||
return;
|
||||
} else {
|
||||
ArrayList<?> instances = vmToolInstance().getInstances(matchedClasses.get(0));
|
||||
Object[] instances = vmToolInstance().getInstances(matchedClasses.get(0));
|
||||
Object value = instances;
|
||||
if (express != null) {
|
||||
Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader);
|
||||
|
5
pom.xml
5
pom.xml
@ -217,11 +217,6 @@
|
||||
<artifactId>zt-zip</artifactId>
|
||||
<version>1.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.scijava</groupId>
|
||||
<artifactId>native-lib-loader</artifactId>
|
||||
<version>2.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package arthas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author ZhangZiCheng 2021-02-12
|
||||
* @author hengyunabc 2021-04-26
|
||||
@ -46,7 +44,7 @@ public class VmTool implements VmToolMXBean {
|
||||
/**
|
||||
* 获取某个class在jvm中当前所有存活实例
|
||||
*/
|
||||
private static native <T> ArrayList<T> getInstances0(Class<T> klass);
|
||||
private static native <T> T[] getInstances0(Class<T> klass);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
|
||||
@ -66,13 +64,12 @@ public class VmTool implements VmToolMXBean {
|
||||
/**
|
||||
* 获取所有已加载的类
|
||||
*/
|
||||
private static native ArrayList<Class<?>> getAllLoadedClasses0();
|
||||
private static native Class<?>[] getAllLoadedClasses0();
|
||||
|
||||
/**
|
||||
* 包括小类型(如int)
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public static ArrayList<Class> getAllClasses() {
|
||||
public static Class<?>[] getAllClasses() {
|
||||
return getInstances0(Class.class);
|
||||
}
|
||||
|
||||
@ -82,7 +79,7 @@ public class VmTool implements VmToolMXBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ArrayList<T> getInstances(Class<T> klass) {
|
||||
public <T> T[] getInstances(Class<T> klass) {
|
||||
return getInstances0(klass);
|
||||
}
|
||||
|
||||
@ -102,7 +99,7 @@ public class VmTool implements VmToolMXBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<Class<?>> getAllLoadedClasses() {
|
||||
public Class<?>[] getAllLoadedClasses() {
|
||||
return getAllLoadedClasses0();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package arthas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* VmTool interface for JMX server. How to register VmTool MBean:
|
||||
*
|
||||
@ -24,7 +22,7 @@ public interface VmToolMXBean {
|
||||
/**
|
||||
* 获取某个class在jvm中当前所有存活实例
|
||||
*/
|
||||
public <T> ArrayList<T> getInstances(Class<T> klass);
|
||||
public <T> T[] getInstances(Class<T> klass);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
|
||||
@ -44,5 +42,5 @@ public interface VmToolMXBean {
|
||||
/**
|
||||
* 获取所有已加载的类
|
||||
*/
|
||||
public ArrayList<Class<?>> getAllLoadedClasses();
|
||||
public Class<?>[] getAllLoadedClasses();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user