修改openjdk17源码,手搓native方法调用java method方法(无参,无返回值)
修改openjdk17源码,jvm.cpp新增一个native方法,传入对象和要被调用的方法(无参,无返回值)
主要功能是通过 JNI 接口和 HotSpot VM 的内部 API,从 Java 层获取方法的元数据,然后在 C++ 层调用该 Java 方法。它涉及到对象的转换、方法的查找、参数的准备以及方法的实际调用。
JVM_ENTRY(void, JVM_invokeJavaMethod(JNIEnv *env, jobject obj1, jobject obj2, jobject obj3))
JVM_ENTRY
宏:用于定义一个 JNI 函数入口点。void
表示该函数不返回任何值。- 参数:
JNIEnv *env
:JNI 环境指针,用于调用 JNI 函数。jobject obj1
:第一个 Java 对象参数。jobject obj2
:第二个 Java 对象参数。jobject obj3
:第三个 Java 对象参数。
代码主体
-
解析 JNI 对象:
oop oopObj1 = JNIHandles::resolve_non_null(obj1);
- 将
jobject
类型的obj1
转换为oop
类型的oopObj1
。JNIHandles::resolve_non_null
是一个 JNI 函数,用于将 JNI 对象句柄转换为 HotSpot VM 的内部对象表示(oop
)。
- 将
oop oopObj2 = JNIHandles::resolve_non_null(obj2);
- 将
jobject
类型的obj2
转换为oop
类型的oopObj2
。
- 将
oop oopObj3 = JNIHandles::resolve_non_null(obj3);
- 将
jobject
类型的obj3
转换为oop
类型的oopObj3
。
- 将
-
创建
Handle
对象:Handle receiver(THREAD, oopObj2);
- 创建一个
Handle
对象receiver
,封装oopObj2
。Handle
是 HotSpot VM 中用于管理对象引用的类,确保对象在多线程环境中安全地被访问。
- 创建一个
Handle method_mirror(THREAD, oopObj3);
- 创建一个
Handle
对象method_mirror
,封装oopObj3
。
- 创建一个
-
获取方法信息:
int slot = java_lang_reflect_Method::slot(method_mirror());
- 从
method_mirror
中获取方法的槽位(slot)。方法槽位是一个整数,表示方法在类的虚方法表中的位置。
- 从
oop mirror = java_lang_reflect_Method::clazz(method_mirror());
- 从
method_mirror
中获取方法所属的类的oop
表示。
- 从
-
获取类和方法:
InstanceKlass* klass = InstanceKlass::cast(java_lang_Class::as_Klass(mirror));
- 将
mirror
转换为InstanceKlass*
类型的klass
。InstanceKlass
是 HotSpot VM 中表示 Java 类的内部类。
- 将
Method* m = klass->method_with_idnum(slot);
- 从
klass
中获取与slot
对应的Method*
对象m
。Method*
是 HotSpot VM 中表示 Java 方法的内部类。
- 从
methodHandle method(THREAD, m);
- 创建一个
methodHandle
对象method
,封装m
。methodHandle
是 HotSpot VM 中用于管理方法引用的类。
- 创建一个
-
打印调试信息:
std::cout << "@@@@yym%%%%custom " << "obj1:" << oopObj1 << " mirror:" << mirror << "----custom" << std::endl;
- 打印调试信息,输出
oopObj1
和mirror
的地址。
- 打印调试信息,输出
-
准备调用参数:
JavaCallArguments java_args(method->size_of_parameters());
- 创建一个
JavaCallArguments
对象java_args
,用于存储调用方法时的参数。method->size_of_parameters()
返回方法参数的数量。
- 创建一个
java_args.push_oop(receiver);
- 将
receiver
添加到java_args
中,作为方法调用的第一个参数(通常是this
指针)。
- 将
-
调用 Java 方法:
JavaValue result(T_VOID);
- 创建一个
JavaValue
对象result
,用于存储方法调用的结果。T_VOID
表示方法没有返回值。
- 创建一个
JavaCalls::call(&result, method, &java_args, CHECK);
- 调用
JavaCalls::call
函数,执行方法调用。&result
是结果的存储位置,method
是要调用的方法,&java_args
是方法的参数列表,CHECK
是一个宏,用于处理可能的异常。
- 调用
##源码
##src/java.base/share/classes/java/lang/MyPrintStringInfo.java文件
/**
* invokeJavaMethod
* @param obj invokeJavaMethod
* @param method method
**/
public native void invokeJavaMethod(Object obj, Object method);
##src/hotspot/share/include/jvm.h文件
//yym-gaizao
JNIEXPORT void JNICALL
JVM_invokeJavaMethod(JNIEnv *env, jobject obj1, jobject obj2, jobject obj3);
##src/hotspot/share/prims/jvm.cpp
//yym-gaizao
JVM_ENTRY(void, JVM_invokeJavaMethod(JNIEnv *env, jobject obj1, jobject obj2, jobject obj3))//
oop oopObj1 = JNIHandles::resolve_non_null(obj1);
oop oopObj2 = JNIHandles::resolve_non_null(obj2);
oop oopObj3 = JNIHandles::resolve_non_null(obj3);
// Handle mh = (Handle)oopObj2;
Handle receiver(THREAD, oopObj2);
Handle method_mirror(THREAD, oopObj3);
int slot = java_lang_reflect_Method::slot(method_mirror());
oop mirror = java_lang_reflect_Method::clazz(method_mirror());
InstanceKlass* klass = InstanceKlass::cast(java_lang_Class::as_Klass(mirror));
Method* m = klass->method_with_idnum(slot);
methodHandle method(THREAD, m);
std::cout << "@@@@yym%%%%custom " << "obj1:" << oopObj1 << " mirror:" << mirror << "----custom" << std::endl;
JavaCallArguments java_args(method->size_of_parameters());
java_args.push_oop(receiver);
// JavaCallArguments args; // No arguments
JavaValue result(T_VOID);
JavaCalls::call(&result, method, &java_args, CHECK); // Static call (no args)
JVM_END
##make/data/hotspot-symbols/symbols-unix
# yym-gaizao
JVM_invokeJavaMethod
##/home/yym/openjdk17/jdk17-master/build/linux-x86_64-server-slowdebug/support/headers/java.base/java_lang_MyPrintStringInfo.h
/*
* Class: java_lang_MyPrintStringInfo
* Method: invokeJavaMethod
* Signature: (Ljava/lang/Object;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_java_lang_MyPrintStringInfo_invokeJavaMethod
(JNIEnv *, jobject, jobject, jobject);
##/home/yym/openjdk17/jdk17-master/src/java.base/share/native/libjava/MyPrintStringInfo.c
JNIEXPORT void JNICALL Java_java_lang_MyPrintStringInfo_invokeJavaMethod
(JNIEnv *env, jobject obj1, jobject obj2, jobject obj3)
{
return JVM_invokeJavaMethod(env, obj1, obj2, obj3);
}
##测试代码
public class MiBigObject {
public static int CONSTANT_TYPE = 1234567890;
public void printlnTest() {
System.out.println("printlnTest");
}
}
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class OutOfMemoryTest {
public static void main(String[] args) throws Exception {
List<MiBigObject> miBigObjects = new ArrayList<>(260000);
System.out.println(miBigObjects);
MyPrintStringInfo myPrintStringInfo = new MyPrintStringInfo();
//myPrintStringInfo.printStringTable();
int yym = MiBigObject.CONSTANT_TYPE;
//myPrintStringInfo.mirrorClassOOPKlassEq(MiBigObject.class);
MiBigObject miBigObject = (MiBigObject)myPrintStringInfo.newJavaObject(MiBigObject.class);
try {
Method method = MiBigObject.class.getDeclaredMethod("printlnTest");
myPrintStringInfo.invokeJavaMethod(miBigObject,method);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(miBigObject);
System.in.read();
}
}