JVMTI 强制GC

JVM TI (JVM Tool Interface) 是用来开发和监测jvm的编程接口。

开发文档:https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html

参考资料:https://stackoverflow.com/questions/2178296/java-how-do-you-really-force-a-gc-using-jvmtis-forcegargabecollection

https://blog.csdn.net/yczz/article/details/39034223

https://blog.csdn.net/ddelphine/article/details/79695947

JVM TI 提供了许多JVM相关的接口,强制GC的接口是:

jvmtiError ForceGarbageCollection(jvmtiEnv* env)
可以在被监测的测试类中定义原生的native方法,然后用C/C++调用JVM TI提供的接口来实现native方法,最后编译成动态链接库.dll(linux系统为 shared object 文件,.so)

编写简单的agent

源码引用头文件#include<jvmti.h>,该头文件在jdk安装目录的include目录下。#include<jni_md.h>,windows系统中该头文件在jdk安装目录include\win32下,linux系统则在相应的include/linux目录下。在编译生成.dll/.so文件时要添加这两个头文件的搜索路径。

ForceGC.c -fPIC -shared -o forceGC.so
具体编译情况依据系统,语言和编译器

linux环境下 ForceGC.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jvmti.h>
#include <jni_md.h>
#include "ForceGC.h"
/**< 此处定义结构体GobalAgentData用于保存jvmtiEnv指针 */
typedef struct{
jvmtiEnv * jvmti;
}GlobalAgentData;
static GlobalAgentData * gdata;
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char * options, void *reserved){
jvmtiEnv * jvmti = NULL;
jvmtiCapabilities capa;
jvmtiError error;
jint result = (*jvm)->GetEnv(jvm,(void**)&jvmti, JVMTI_VERSION_1_2);
if(result != JNI_OK){
printf("ERROR : Unable to access JVMTI! \n");
}
printf("GC_Agent was loaded success.\n");
(void)memset(&capa,0,sizeof(jvmtiCapabilities));

capa.can_tag_objects = 1;
error = (*jvmti)->AddCapabilities(jvmti,&capa);
//将jvmti保存到全局变量
gdata = (GlobalAgentData*)malloc(sizeof(GlobalAgentData));
gdata->jvmti = jvmti;
return JNI_OK;
}
JNIEXPORT void JNICALL Java_Test_forceGC(JNIEnv* env, jclass thisClass){
jvmtiError error = (*(gdata->jvmti))->ForceGarbageCollection(gdata->jvmti);
if (error == NULL){
printf("\nJVM has been forced to GC.\n");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
ForceGC.h

#include <jni.h>
#ifndef _Included_FinalizeEscapeGC
#define _Included_FinalizeEscapeGC
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_Test_forceGC
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

Test.java测试用类

public class Test {
public static Test SAVE_HOOK = null;

public void isAlive() {
System.out.println("I am still alive");
}

@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method was excuted.");
Test.SAVE_HOOK = this;
}

public native static void forceGC();

public static void main(String[] args) throws Throwable {
SAVE_HOOK = new Test();
SAVE_HOOK = null;

forceGC();

Thread.sleep(500);

if(SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
}else{
System.out.println("Oh... I am dead.");
}

SAVE_HOOK = null;

System.gc();

Thread.sleep(500);

if(SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
}else{
System.out.println("Oh... I am dead.");
}

}
}

最后启动agent
java -agentpath:./forceGC.so Test

结果如下: