引言
在 Java Native Interface(JNI)编程中,jfieldID是一个核心概念,它充当了 Java 层字段与 Native 层之间的“桥梁”。许多开发者知道通过GetFieldID获取jfieldID,然后通过GetIntField等函数读取字段值,但对其内部的实现机制——尤其是 HotSpot JVM 如何编码和使用jfieldID——却知之甚少。
本文将通过剖析 OpenJDK 17 源码,结合java.io.FileDescriptor中fd字段的实际案例,深入探讨jfieldID的创建、编码、以及 JVM 提供的快速路径优化,帮助读者建立从 Java 层到 Native 层的完整调用链认知。
一、从实际案例出发:IOUtil 中的 jfieldID 缓存
在sun.nio.ch.IOUtil类的静态初始化块中,有这样一段代码:
java
static { jdk.internal.loader.BootLoader.loadLibrary("net"); jdk.internal.loader.BootLoader.loadLibrary("nio"); initIDs(); // 调用 native 方法初始化字段 ID IOV_MAX = iovMax(); }对应的 native 函数Java_sun_nio_ch_IOUtil_initIDs实现如下:
c
JNIEXPORT void JNICALL Java_sun_nio_ch_IOUtil_initIDs(JNIEnv *env, jclass clazz) { CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor")); CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I")); }fd_fdID是一个全局静态jfieldID变量,用于缓存FileDescriptor类中名为fd、类型为int的字段 ID。后续在fdval函数中直接使用:
c
jint fdval(JNIEnv *env, jobject fdo) { return (*env)->GetIntField(env, fdo, fd_fdID); }这种“一次查找,多次复用”的模式是 JNI 编程的最佳实践,因为GetFieldID需要遍历类的字段,开销较大。但jfieldID到底是什么?它仅仅是一个不透明的指针吗?我们继续深入。
二、jfieldID 的本质:一个编码的整数
在jni.h中,jfieldID被声明为:
c
struct _jfieldID; typedef struct _jfieldID *jfieldID;
这是一个不完整类型的前向声明,实际上,HotSpot JVM 并没有为实例字段的jfieldID分配真正的_jfieldID结构体。相反,它被编码成一个整数值,存储了字段的偏移量和元数据。
在jfieldIDWorkaround.hpp中,我们可以找到详细的位布局注释:
text
Bit-format of a jfieldID (most significant first): address:30 instance=0:1 checked=0:1 offset:30 instance=1:1 checked=0:1 klass:23 offset:7 instance=1:1 checked=1:1
对于实例字段(instance=1),jfieldID的低 2 位分别用于:
bit 0 (
checked):标记是否启用字段校验(VerifyJNIFields)。bit 1 (
instance):固定为 1,表示这是一个实例字段。
其余高 30 位(或 30 位中的部分)存储字段的字节偏移量(offset)。如果开启了校验,还会将类的哈希值编码到高位中,用于防止jfieldID被误用于其他类。
正因为jfieldID本质上是一个整数,所以我们可以直接对它进行位移操作来提取偏移量。这也是后续快速路径优化的基础。
三、jfieldID 的创建:jni_GetFieldID 调用链
当我们调用(*env)->GetFieldID(env, clazz, "fd", "I")时,JVM 最终会执行jni_GetFieldID函数(位于jni.cpp)。其核心流程如下:
解析类:将
jclass转换为Klass*。查找符号:在 SymbolTable 中查找字段名和签名字符串。
确保类初始化:调用
k->initialize()。查找字段描述符:通过
InstanceKlass::find_field获取fieldDescriptor,其中包含字段的偏移量fd.offset()。编码生成 jfieldID:调用
jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset())。
cpp
JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig)) // ... 省略查找和校验 ... ret = jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset()); return ret; JNI_END
to_instance_jfieldID的实现如下:
cpp
static jfieldID to_instance_jfieldID(Klass* k, int offset) { intptr_t as_uint = ((offset & large_offset_mask) << offset_shift) | instance_mask_in_place; if (VerifyJNIFields) { as_uint |= encode_klass_hash(k, offset); } jfieldID result = (jfieldID) as_uint; // 校验和断言 return result; }offset_shift通常是 2(因为低 2 位被标志位占用)。instance_mask_in_place将 bit 1 置 1。如果
VerifyJNIFields开启,还会通过encode_klass_hash将类的哈希值编码进高位。
这样,返回的jfieldID就不再是一个内存地址,而是一个自描述的编码值。
四、字段值的读取:慢速路径与快速路径
4.1 慢速路径:jni_GetIntField 的通用实现
当我们在fdval中调用(*env)->GetIntField(env, fdo, fd_fdID)时,JVM 会通过函数指针调用相应的实现。默认情况下,这个函数指针指向的是由宏DEFINE_GETFIELD展开的jni_GetIntField。
该宏展开后的核心逻辑如下:
cpp
JNI_ENTRY_NO_PRESERVE(jint, jni_GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID)) { oop o = JNIHandles::resolve_non_null(obj); Klass* k = o->klass(); int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); // 可选的 JVMTI 事件处理 ret = o->int_field(offset); return ret; } JNI_ENDfrom_instance_jfieldID负责从jfieldID中提取偏移量:
cpp
static intptr_t from_instance_jfieldID(Klass* k, jfieldID id) { if (VerifyJNIFields) { verify_instance_jfieldID(k, id); // 校验类哈希 } return raw_instance_offset(id); } static intptr_t raw_instance_offset(jfieldID id) { uintptr_t result = (uintptr_t) id >> address_shift; // address_shift = 2 if (VerifyJNIFields && is_checked_jfieldID(id)) { result &= small_offset_mask; // 7 位偏移 } return (intptr_t)result; }得到偏移量后,o->int_field(offset)实际上是从对象基址加上偏移量读取 4 个字节。这个路径包含了 JNI 边界检查、JVM 状态切换(ThreadInVMfromNative)、JVMTI 事件等,开销较大。
4.2 快速路径:JNI_FastGetField 生成的汇编代码
HotSpot 提供了一个强大的优化机制:在满足一定条件时,将jni_NativeInterface结构体中的函数指针替换为手写的汇编快速路径。这是在quicken_jni_functions()中完成的:
cpp
void quicken_jni_functions() { if (UseFastJNIAccessors && !VerifyJNIFields && !CheckJNICalls) { address func = JNI_FastGetField::generate_fast_get_int_field(); if (func != (address)-1) { jni_NativeInterface.GetIntField = (GetIntField_t)func; } // ... 其他类型 } }快速路径的核心生成函数是JNI_FastGetField::generate_fast_get_int_field0,我们重点关注其生成的汇编逻辑(以T_INT为例):
asm
; 1. 检查安全点 mov32 rcounter, [SafepointSynchronize::safepoint_counter_addr()] testb rcounter, 1 jnz slow ; 2. 检查 JVMTI 字段访问计数 cmp32 [JvmtiExport::get_field_access_count_addr()], 0 jnz slow ; 3. 解析 jfieldID 获取偏移量(右移 2 位) mov roffset, c_rarg2 ; c_rarg2 = fieldID shrptr roffset, 2 ; offset = fieldID >> 2 ; 4. 解析 jobject 为 oop call try_resolve_jobject_in_native ; 5. 直接内存读取 movl rax, [robj + roffset] ; 从对象基址 + 偏移量取 int 值 ; 6. 再次检查安全点是否发生变化 cmp32 rcounter, [SafepointSynchronize::safepoint_counter_addr()] jne slow ret slow: jmp jni_GetIntField_addr ; 跳转到慢速路径
这个快速路径的巧妙之处在于:
偏移量提取:直接通过
shrptr右移 2 位,省去了 C++ 函数调用的开销。内存读取:一条
movl指令完成字段值的获取,无需经过oop的成员函数。安全点检查:仅检查两次安全点计数(前后各一次),确保在读字段期间未发生安全点。
条件编译:只有在
UseFastJNIAccessors为 true 且未开启字段校验时才会启用,保证了正确性。
通过这个快速路径,GetIntField的性能可以接近 C 语言直接访问结构体成员,极大地提升了 JNI 字段访问的效率。
五、静态字段与实例字段的区别
值得特别指出的是,上述编码和快速路径优化仅适用于实例字段。对于静态字段,jfieldID的处理方式完全不同。
在jni_GetStaticFieldID中,JVM 会创建一个真实的JNIid对象(定义在jniid.hpp中),并将jfieldID设置为指向该对象的指针。当使用GetStaticIntField时,JVM 会解引用这个指针以获取偏移量等信息。
cpp
// 静态字段的 jfieldID 是真实指针 JNIid* id = (JNIid*)fieldID; int offset = id->offset();
因此,实例字段的jfieldID是一个“伪指针”(编码整数),而静态字段的jfieldID是一个“真指针”。这也是为什么在jfieldIDWorkaround中需要通过低位标志位来区分字段类型。
六、总结
通过上述源码分析,我们可以清晰地勾勒出jfieldID从创建到使用的完整链路:
Java 层静态初始化:
IOUtil类加载时调用initIDs()。Native 层缓存:
Java_sun_nio_ch_IOUtil_initIDs通过GetFieldID获取FileDescriptor.fd的jfieldID,并存入全局变量fd_fdID。jfieldID 生成:
jni_GetFieldID查找字段偏移量,并通过to_instance_jfieldID将偏移量编码为整数(左移 + 置标志位)。字段值读取(慢速路径):
fdval调用GetIntField,JVM 通过from_instance_jfieldID解码偏移量,再通过int_field读取值。字段值读取(快速路径):JVM 启动时根据条件替换函数指针,快速路径直接汇编实现
fieldID >> 2+ 内存读取,极大提升性能。
这种设计体现了 HotSpot JVM 在保持 JNI 规范兼容性的同时,对性能的极致追求。理解jfieldID的编码和快速路径机制,不仅有助于写出更高效的 JNI 代码,也能让我们对 JVM 内部实现有更深刻的认知。
##源码
//sun.nio.ch.IOUtil静态代码块 static { jdk.internal.loader.BootLoader.loadLibrary("net"); jdk.internal.loader.BootLoader.loadLibrary("nio"); initIDs(); IOV_MAX = iovMax(); } //sun.nio.ch.SocketDispatcher静态代码块 static native void initIDs(); static jfieldID fd_fdID; /* for jint 'fd' in java.io.FileDescriptor */ JNIEXPORT void JNICALL Java_sun_nio_ch_IOUtil_initIDs(JNIEnv *env, jclass clazz) { CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor")); CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I")); } jint fdval(JNIEnv *env, jobject fdo) { return (*env)->GetIntField(env, fdo, fd_fdID); } // Structure containing all jni functions struct JNINativeInterface_ jni_NativeInterface = { NULL, NULL, NULL, NULL, jni_GetVersion, jni_DefineClass, jni_FindClass, jni_FromReflectedMethod, jni_FromReflectedField, jni_ToReflectedMethod, jni_GetSuperclass, jni_IsAssignableFrom, jni_ToReflectedField, jni_Throw, jni_ThrowNew, jni_ExceptionOccurred, jni_ExceptionDescribe, jni_ExceptionClear, jni_FatalError, jni_PushLocalFrame, jni_PopLocalFrame, jni_NewGlobalRef, jni_DeleteGlobalRef, jni_DeleteLocalRef, jni_IsSameObject, jni_NewLocalRef, jni_EnsureLocalCapacity, jni_AllocObject, jni_NewObject, jni_NewObjectV, jni_NewObjectA, jni_GetObjectClass, jni_IsInstanceOf, jni_GetMethodID, jni_CallObjectMethod, jni_CallObjectMethodV, jni_CallObjectMethodA, jni_CallBooleanMethod, jni_CallBooleanMethodV, jni_CallBooleanMethodA, jni_CallByteMethod, jni_CallByteMethodV, jni_CallByteMethodA, jni_CallCharMethod, jni_CallCharMethodV, jni_CallCharMethodA, jni_CallShortMethod, jni_CallShortMethodV, jni_CallShortMethodA, jni_CallIntMethod, jni_CallIntMethodV, jni_CallIntMethodA, jni_CallLongMethod, jni_CallLongMethodV, jni_CallLongMethodA, jni_CallFloatMethod, jni_CallFloatMethodV, jni_CallFloatMethodA, jni_CallDoubleMethod, jni_CallDoubleMethodV, jni_CallDoubleMethodA, jni_CallVoidMethod, jni_CallVoidMethodV, jni_CallVoidMethodA, jni_CallNonvirtualObjectMethod, jni_CallNonvirtualObjectMethodV, jni_CallNonvirtualObjectMethodA, jni_CallNonvirtualBooleanMethod, jni_CallNonvirtualBooleanMethodV, jni_CallNonvirtualBooleanMethodA, jni_CallNonvirtualByteMethod, jni_CallNonvirtualByteMethodV, jni_CallNonvirtualByteMethodA, jni_CallNonvirtualCharMethod, jni_CallNonvirtualCharMethodV, jni_CallNonvirtualCharMethodA, jni_CallNonvirtualShortMethod, jni_CallNonvirtualShortMethodV, jni_CallNonvirtualShortMethodA, jni_CallNonvirtualIntMethod, jni_CallNonvirtualIntMethodV, jni_CallNonvirtualIntMethodA, jni_CallNonvirtualLongMethod, jni_CallNonvirtualLongMethodV, jni_CallNonvirtualLongMethodA, jni_CallNonvirtualFloatMethod, jni_CallNonvirtualFloatMethodV, jni_CallNonvirtualFloatMethodA, jni_CallNonvirtualDoubleMethod, jni_CallNonvirtualDoubleMethodV, jni_CallNonvirtualDoubleMethodA, jni_CallNonvirtualVoidMethod, jni_CallNonvirtualVoidMethodV, jni_CallNonvirtualVoidMethodA, jni_GetFieldID, jni_GetObjectField, jni_GetBooleanField, jni_GetByteField, jni_GetCharField, jni_GetShortField, jni_GetIntField, jni_GetLongField, jni_GetFloatField, jni_GetDoubleField, jni_SetObjectField, jni_SetBooleanField, jni_SetByteField, jni_SetCharField, jni_SetShortField, jni_SetIntField, jni_SetLongField, jni_SetFloatField, jni_SetDoubleField, jni_GetStaticMethodID, jni_CallStaticObjectMethod, jni_CallStaticObjectMethodV, jni_CallStaticObjectMethodA, jni_CallStaticBooleanMethod, jni_CallStaticBooleanMethodV, jni_CallStaticBooleanMethodA, jni_CallStaticByteMethod, jni_CallStaticByteMethodV, jni_CallStaticByteMethodA, jni_CallStaticCharMethod, jni_CallStaticCharMethodV, jni_CallStaticCharMethodA, jni_CallStaticShortMethod, jni_CallStaticShortMethodV, jni_CallStaticShortMethodA, jni_CallStaticIntMethod, jni_CallStaticIntMethodV, jni_CallStaticIntMethodA, jni_CallStaticLongMethod, jni_CallStaticLongMethodV, jni_CallStaticLongMethodA, jni_CallStaticFloatMethod, jni_CallStaticFloatMethodV, jni_CallStaticFloatMethodA, jni_CallStaticDoubleMethod, jni_CallStaticDoubleMethodV, jni_CallStaticDoubleMethodA, jni_CallStaticVoidMethod, jni_CallStaticVoidMethodV, jni_CallStaticVoidMethodA, jni_GetStaticFieldID, jni_GetStaticObjectField, jni_GetStaticBooleanField, jni_GetStaticByteField, jni_GetStaticCharField, jni_GetStaticShortField, jni_GetStaticIntField, jni_GetStaticLongField, jni_GetStaticFloatField, jni_GetStaticDoubleField, jni_SetStaticObjectField, jni_SetStaticBooleanField, jni_SetStaticByteField, jni_SetStaticCharField, jni_SetStaticShortField, jni_SetStaticIntField, jni_SetStaticLongField, jni_SetStaticFloatField, jni_SetStaticDoubleField, jni_NewString, jni_GetStringLength, jni_GetStringChars, jni_ReleaseStringChars, jni_NewStringUTF, jni_GetStringUTFLength, jni_GetStringUTFChars, jni_ReleaseStringUTFChars, jni_GetArrayLength, jni_NewObjectArray, jni_GetObjectArrayElement, jni_SetObjectArrayElement, jni_NewBooleanArray, jni_NewByteArray, jni_NewCharArray, jni_NewShortArray, jni_NewIntArray, jni_NewLongArray, jni_NewFloatArray, jni_NewDoubleArray, jni_GetBooleanArrayElements, jni_GetByteArrayElements, jni_GetCharArrayElements, jni_GetShortArrayElements, jni_GetIntArrayElements, jni_GetLongArrayElements, jni_GetFloatArrayElements, jni_GetDoubleArrayElements, jni_ReleaseBooleanArrayElements, jni_ReleaseByteArrayElements, jni_ReleaseCharArrayElements, jni_ReleaseShortArrayElements, jni_ReleaseIntArrayElements, jni_ReleaseLongArrayElements, jni_ReleaseFloatArrayElements, jni_ReleaseDoubleArrayElements, jni_GetBooleanArrayRegion, jni_GetByteArrayRegion, jni_GetCharArrayRegion, jni_GetShortArrayRegion, jni_GetIntArrayRegion, jni_GetLongArrayRegion, jni_GetFloatArrayRegion, jni_GetDoubleArrayRegion, jni_SetBooleanArrayRegion, jni_SetByteArrayRegion, jni_SetCharArrayRegion, jni_SetShortArrayRegion, jni_SetIntArrayRegion, jni_SetLongArrayRegion, jni_SetFloatArrayRegion, jni_SetDoubleArrayRegion, jni_RegisterNatives, jni_UnregisterNatives, jni_MonitorEnter, jni_MonitorExit, jni_GetJavaVM, jni_GetStringRegion, jni_GetStringUTFRegion, jni_GetPrimitiveArrayCritical, jni_ReleasePrimitiveArrayCritical, jni_GetStringCritical, jni_ReleaseStringCritical, jni_NewWeakGlobalRef, jni_DeleteWeakGlobalRef, jni_ExceptionCheck, jni_NewDirectByteBuffer, jni_GetDirectBufferAddress, jni_GetDirectBufferCapacity, // New 1_6 features jni_GetObjectRefType, // Module features jni_GetModule }; void quicken_jni_functions() { // Replace Get<Primitive>Field with fast versions if (UseFastJNIAccessors && !VerifyJNIFields && !CheckJNICalls) { address func; func = JNI_FastGetField::generate_fast_get_boolean_field(); if (func != (address)-1) { jni_NativeInterface.GetBooleanField = (GetBooleanField_t)func; } func = JNI_FastGetField::generate_fast_get_byte_field(); if (func != (address)-1) { jni_NativeInterface.GetByteField = (GetByteField_t)func; } func = JNI_FastGetField::generate_fast_get_char_field(); if (func != (address)-1) { jni_NativeInterface.GetCharField = (GetCharField_t)func; } func = JNI_FastGetField::generate_fast_get_short_field(); if (func != (address)-1) { jni_NativeInterface.GetShortField = (GetShortField_t)func; } func = JNI_FastGetField::generate_fast_get_int_field(); if (func != (address)-1) { jni_NativeInterface.GetIntField = (GetIntField_t)func; } func = JNI_FastGetField::generate_fast_get_long_field(); if (func != (address)-1) { jni_NativeInterface.GetLongField = (GetLongField_t)func; } func = JNI_FastGetField::generate_fast_get_float_field(); if (func != (address)-1) { jni_NativeInterface.GetFloatField = (GetFloatField_t)func; } func = JNI_FastGetField::generate_fast_get_double_field(); if (func != (address)-1) { jni_NativeInterface.GetDoubleField = (GetDoubleField_t)func; } } } address JNI_FastGetField::generate_fast_get_int_field() { return generate_fast_get_int_field0(T_INT); } address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { const char *name = NULL; switch (type) { case T_BOOLEAN: name = "jni_fast_GetBooleanField"; break; case T_BYTE: name = "jni_fast_GetByteField"; break; case T_CHAR: name = "jni_fast_GetCharField"; break; case T_SHORT: name = "jni_fast_GetShortField"; break; case T_INT: name = "jni_fast_GetIntField"; break; case T_LONG: name = "jni_fast_GetLongField"; break; default: ShouldNotReachHere(); } ResourceMark rm; BufferBlob* blob = BufferBlob::create(name, BUFFER_SIZE); CodeBuffer cbuf(blob); MacroAssembler* masm = new MacroAssembler(&cbuf); address fast_entry = __ pc(); Label slow; ExternalAddress counter(SafepointSynchronize::safepoint_counter_addr()); __ mov32 (rcounter, counter); __ mov (robj, c_rarg1); __ testb (rcounter, 1); __ jcc (Assembler::notZero, slow); if (JvmtiExport::can_post_field_access()) { // Check to see if a field access watch has been set before we // take the fast path. assert_different_registers(rscratch1, robj, rcounter); // cmp32 clobbers rscratch1! __ cmp32(ExternalAddress((address) JvmtiExport::get_field_access_count_addr()), 0); __ jcc(Assembler::notZero, slow); } __ mov (roffset, c_rarg2); __ shrptr(roffset, 2); // offset // Both robj and rtmp are clobbered by try_resolve_jobject_in_native. BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->try_resolve_jobject_in_native(masm, /* jni_env */ c_rarg0, robj, rtmp, slow); DEBUG_ONLY(__ movl(rtmp, 0xDEADC0DE);) assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); speculative_load_pclist[count] = __ pc(); switch (type) { case T_BOOLEAN: __ movzbl (rax, Address(robj, roffset, Address::times_1)); break; case T_BYTE: __ movsbl (rax, Address(robj, roffset, Address::times_1)); break; case T_CHAR: __ movzwl (rax, Address(robj, roffset, Address::times_1)); break; case T_SHORT: __ movswl (rax, Address(robj, roffset, Address::times_1)); break; case T_INT: __ movl (rax, Address(robj, roffset, Address::times_1)); break; case T_LONG: __ movq (rax, Address(robj, roffset, Address::times_1)); break; default: ShouldNotReachHere(); } __ cmp32 (rcounter, counter); __ jcc (Assembler::notEqual, slow); __ ret (0); slowcase_entry_pclist[count++] = __ pc(); __ bind (slow); address slow_case_addr = NULL; switch (type) { case T_BOOLEAN: slow_case_addr = jni_GetBooleanField_addr(); break; case T_BYTE: slow_case_addr = jni_GetByteField_addr(); break; case T_CHAR: slow_case_addr = jni_GetCharField_addr(); break; case T_SHORT: slow_case_addr = jni_GetShortField_addr(); break; case T_INT: slow_case_addr = jni_GetIntField_addr(); break; case T_LONG: slow_case_addr = jni_GetLongField_addr(); break; default: break; } // tail call __ jump (ExternalAddress(slow_case_addr)); __ flush (); return fast_entry; } address jni_GetIntField_addr() { // 慢速路径 return (address)jni_GetIntField; } #define DEFINE_GETFIELD(Return,Fieldname,Result \ , EntryProbe, ReturnProbe) \ \ DT_RETURN_MARK_DECL_FOR(Result, Get##Result##Field, Return \ , ReturnProbe); \ \ JNI_ENTRY_NO_PRESERVE(Return, jni_Get##Result##Field(JNIEnv *env, jobject obj, jfieldID fieldID)) \ \ EntryProbe; \ Return ret = 0;\ DT_RETURN_MARK_FOR(Result, Get##Result##Field, Return, (const Return&)ret);\ \ oop o = JNIHandles::resolve_non_null(obj); \ Klass* k = o->klass(); \ int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); \ /* Keep JVMTI addition small and only check enabled flag here. */ \ if (JvmtiExport::should_post_field_access()) { \ o = JvmtiExport::jni_GetField_probe(thread, obj, o, k, fieldID, false); \ } \ ret = o->Fieldname##_field(offset); \ return ret; \ JNI_END DEFINE_GETFIELD(jboolean, bool, Boolean , HOTSPOT_JNI_GETBOOLEANFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETBOOLEANFIELD_RETURN(_ret_ref)) DEFINE_GETFIELD(jbyte, byte, Byte , HOTSPOT_JNI_GETBYTEFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETBYTEFIELD_RETURN(_ret_ref)) DEFINE_GETFIELD(jchar, char, Char , HOTSPOT_JNI_GETCHARFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETCHARFIELD_RETURN(_ret_ref)) DEFINE_GETFIELD(jshort, short, Short , HOTSPOT_JNI_GETSHORTFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETSHORTFIELD_RETURN(_ret_ref)) DEFINE_GETFIELD(jint, int, Int , HOTSPOT_JNI_GETINTFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETINTFIELD_RETURN(_ret_ref)) DEFINE_GETFIELD(jlong, long, Long , HOTSPOT_JNI_GETLONGFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETLONGFIELD_RETURN(_ret_ref)) // Float and double probes don't return value because dtrace doesn't currently support it DEFINE_GETFIELD(jfloat, float, Float , HOTSPOT_JNI_GETFLOATFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETFLOATFIELD_RETURN()) DEFINE_GETFIELD(jdouble, double, Double , HOTSPOT_JNI_GETDOUBLEFIELD_ENTRY(env, obj, (uintptr_t)fieldID), HOTSPOT_JNI_GETDOUBLEFIELD_RETURN()) //获取jfieldID Expands to: ; extern "C" { jint __stdcall jni_GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID) { JavaThread* thread=JavaThread::thread_from_jni_environment(env); ; ; ThreadInVMfromNative __tiv(thread); HandleMarkCleaner __hm(thread); JavaThread* __the_thread__ = thread; os::verify_stack_alignment(); ; jint ret = 0; ; oop o = JNIHandles::resolve_non_null(obj); Klass* k = o->klass(); int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID); if (JvmtiExport::should_post_field_access()) { o = JvmtiExport::jni_GetField_probe(thread, obj, o, k, fieldID, false); } ret = o->int_field(offset); return ret; } } static intptr_t from_instance_jfieldID(Klass* k, jfieldID id) { #ifndef ASSERT // always verify in debug mode; switchable in anything else if (VerifyJNIFields) #endif // ASSERT { verify_instance_jfieldID(k, id); } return raw_instance_offset(id); } // helper routines: static bool is_checked_jfieldID(jfieldID id) { uintptr_t as_uint = (uintptr_t) id; return ((as_uint & checked_mask_in_place) != 0); } static intptr_t raw_instance_offset(jfieldID id) { uintptr_t result = (uintptr_t) id >> address_shift; if (VerifyJNIFields && is_checked_jfieldID(id)) { result &= small_offset_mask; // cut off the hash bits } return (intptr_t)result; } static intptr_t raw_instance_offset(jfieldID id) { uintptr_t result = (uintptr_t) id >> address_shift; if (VerifyJNIFields && is_checked_jfieldID(id)) { result &= small_offset_mask; // cut off the hash bits } return (intptr_t)result; } //设置jfieldID JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig)) HOTSPOT_JNI_GETFIELDID_ENTRY(env, clazz, (char *) name, (char *) sig); jfieldID ret = 0; DT_RETURN_MARK(GetFieldID, jfieldID, (const jfieldID&)ret); Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); // The class should have been loaded (we have an instance of the class // passed in) so the field and signature should already be in the symbol // table. If they're not there, the field doesn't exist. TempNewSymbol fieldname = SymbolTable::probe(name, (int)strlen(name)); TempNewSymbol signame = SymbolTable::probe(sig, (int)strlen(sig)); if (fieldname == NULL || signame == NULL) { ResourceMark rm; THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); } // Make sure class is initialized before handing id's out to fields k->initialize(CHECK_NULL); fieldDescriptor fd; if (!k->is_instance_klass() || !InstanceKlass::cast(k)->find_field(fieldname, signame, false, &fd)) { ResourceMark rm; THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); } // A jfieldID for a non-static field is simply the offset of the field within the instanceOop // It may also have hash bits for k, if VerifyJNIFields is turned on. ret = jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset()); return ret; JNI_END static jfieldID to_instance_jfieldID(Klass* k, int offset) { intptr_t as_uint = ((offset & large_offset_mask) << offset_shift) | instance_mask_in_place; if (VerifyJNIFields) { as_uint |= encode_klass_hash(k, offset); } jfieldID result = (jfieldID) as_uint; #ifndef ASSERT // always verify in debug mode; switchable in anything else if (VerifyJNIFields) #endif // ASSERT { verify_instance_jfieldID(k, result); } assert(raw_instance_offset(result) == (offset & large_offset_mask), "extract right offset"); return result; }