Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

提供JvmUtils #1698

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
处理内存泄漏问题,大幅度优化性能
  • Loading branch information
loongs-zhang committed Apr 28, 2021
commit e5153a15711b9c5e298f8d753829aa0f74a11b63
9 changes: 3 additions & 6 deletions arthas-beans/src/main/java/com/vdian/vclub/JvmUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import com.taobao.arthas.common.AnsiLog;
import org.scijava.nativelib.NativeLoader;

import java.util.ArrayList;

/**
* @author ZhangZiCheng 2021-02-12
* @since 3.5.1
Expand Down Expand Up @@ -33,7 +31,7 @@ public class JvmUtils {
/**
* 获取某个class在jvm中当前所有存活实例
*/
public static native <T> ArrayList<T> getInstances(Class<T> klass);
public static native <T> T[] getInstances(Class<T> klass);

/**
* 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
Expand All @@ -53,13 +51,12 @@ public class JvmUtils {
/**
* 获取所有已加载的类
*/
public static native ArrayList<Class<?>> getAllLoadedClasses();
public static native Class<?>[] getAllLoadedClasses();

/**
* 包括小类型(如int)
*/
@SuppressWarnings("all")
public static ArrayList<Class> getAllClasses() {
public static Class<?>[] getAllClasses() {
return getInstances(Class.class);
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 31 additions & 27 deletions arthas-beans/src/main/native/src/jni-library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@
#include <jvmti.h>
#include "com_vdian_vclub_JvmUtils.h"

//缓存
static jclass cachedClass = nullptr;

extern "C"
JNIEXPORT jclass JNICALL getClass(JNIEnv *env) {
if (cachedClass == nullptr) {
//通过其签名找到ArrayList的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_com_vdian_vclub_JvmUtils_check(JNIEnv *env, jclass thisClass) {
Expand All @@ -21,14 +36,6 @@ jvmtiEnv *getJvmtiEnv(JNIEnv *env) {
return jvmti;
}

extern "C"
jobject createJavaInstance(JNIEnv *env, jclass javaClass) {
//找到java类的构造方法
jmethodID construct = env->GetMethodID(javaClass, "<init>", "()V");
//生成java类实例
return env->NewObject(javaClass, construct, "");
}

extern "C"
jlong getClassHashCode(JNIEnv *env, jclass javaClass) {
//找到java类的hashCode方法
Expand All @@ -46,7 +53,7 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data)
}

extern "C"
JNIEXPORT jobject JNICALL
JNIEXPORT jobjectArray JNICALL
Java_com_vdian_vclub_JvmUtils_getInstances(JNIEnv *env, jclass thisClass, jclass klass) {

jvmtiEnv *jvmti = getJvmtiEnv(env);
Expand All @@ -69,21 +76,19 @@ Java_com_vdian_vclub_JvmUtils_getInstances(JNIEnv *env, jclass thisClass, jclass

jint count = 0;
jobject *instances;
error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL);
error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, nullptr);
if (error) {
printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error);
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, nullptr);
//添加元素到数组
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"
Expand All @@ -110,7 +115,7 @@ Java_com_vdian_vclub_JvmUtils_sumInstanceSize(JNIEnv *env, jclass thisClass, jcl

jint count = 0;
jobject *instances;
error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL);
error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, nullptr);
if (error) {
printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error);
return JNI_FALSE;
Expand All @@ -122,6 +127,7 @@ Java_com_vdian_vclub_JvmUtils_sumInstanceSize(JNIEnv *env, jclass thisClass, jcl
jvmti->GetObjectSize(instances[i], &size);
sum = sum + size;
}
jvmti->Deallocate(reinterpret_cast<unsigned char *>(instances));
return sum;
}

Expand Down Expand Up @@ -163,7 +169,7 @@ Java_com_vdian_vclub_JvmUtils_countInstances(JNIEnv *env, jclass thisClass, jcla
}

jint count = 0;
error = jvmti->GetObjectsWithTags(1, &tag, &count, NULL, NULL);
error = jvmti->GetObjectsWithTags(1, &tag, &count, nullptr, nullptr);
if (error) {
printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error);
return JNI_FALSE;
Expand All @@ -172,7 +178,7 @@ Java_com_vdian_vclub_JvmUtils_countInstances(JNIEnv *env, jclass thisClass, jcla
}

extern "C"
JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getAllLoadedClasses
JNIEXPORT jobjectArray JNICALL Java_com_vdian_vclub_JvmUtils_getAllLoadedClasses
(JNIEnv *env, jclass thisClass) {

jvmtiEnv *jvmti = getJvmtiEnv(env);
Expand All @@ -186,13 +192,11 @@ JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getAllLoadedClasses
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), nullptr);
//添加元素到数组
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;
}
101 changes: 96 additions & 5 deletions arthas-beans/src/test/java/com/vdian/vclub/JvmUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import org.junit.Test;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;

/**
* 以下测试的jvm参数均为:-Xms128m -Xmx128m
*/
public class JvmUtilsTest {

/**
Expand All @@ -22,14 +25,14 @@ public class JvmUtilsTest {
public void test01() {
try {
//调用native方法,获取已加载的类,不包括小类型(如int)
ArrayList<Class<?>> allLoadedClasses = JvmUtils.getAllLoadedClasses();
System.out.println("allLoadedClasses->" + allLoadedClasses.size());
Class<?>[] allLoadedClasses = JvmUtils.getAllLoadedClasses();
System.out.println("allLoadedClasses->" + allLoadedClasses.length);

//通过下面的例子,可以看到getInstances(Class<T> klass)拿到的是当前存活的所有对象
WeakReference<JvmUtils> weakReference1 = new WeakReference<JvmUtils>(new JvmUtils());
WeakReference<JvmUtils> weakReference2 = new WeakReference<JvmUtils>(new JvmUtils());
System.out.println(weakReference1.get() + " " + weakReference2.get());
ArrayList<JvmUtils> beforeInstances = JvmUtils.getInstances(JvmUtils.class);
JvmUtils[] beforeInstances = JvmUtils.getInstances(JvmUtils.class);
System.out.println("before instances->" + beforeInstances);
System.out.println("size->" + JvmUtils.getInstanceSize(weakReference1.get()));
System.out.println("count->" + JvmUtils.countInstances(JvmUtils.class));
Expand All @@ -39,10 +42,98 @@ public void test01() {
System.gc();
Thread.sleep(100);
System.out.println(weakReference1.get() + " " + weakReference2.get());
ArrayList<JvmUtils> afterInstances = JvmUtils.getInstances(JvmUtils.class);
JvmUtils[] afterInstances = JvmUtils.getInstances(JvmUtils.class);
System.out.println("after instances->" + afterInstances);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 没问题
*/
@Test
public void testGetInstancesMemoryLeak() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final AtomicLong totalTime = new AtomicLong();
for (int i = 1; i <= 200000; i++) {
long start = System.currentTimeMillis();
WeakReference<Object[]> reference = new WeakReference<Object[]>(JvmUtils.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;
if (i % 100 == 0) {
//fixme 如果注释掉下面这行代码,被动gc,仍然会挂掉,不知道为什么
System.gc();
}
}
}

/**
* 没问题
*/
@Test
public void testSumInstancesMemoryLeak() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final AtomicLong totalTime = new AtomicLong();
for (int i = 1; i <= 200000; i++) {
long start = System.currentTimeMillis();
long sum = JvmUtils.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() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final AtomicLong totalTime = new AtomicLong();
for (int i = 1; i <= 200000; i++) {
long start = System.currentTimeMillis();
long count = JvmUtils.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() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final AtomicLong totalTime = new AtomicLong();
for (int i = 1; i <= 200000; i++) {
long start = System.currentTimeMillis();
Class<?>[] allLoadedClasses = JvmUtils.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;
}
}

}