当前位置: 首页 > news >正文

Field访问对象int字段,对象访问int字段,通过openjdk17 C++源码看对象字段访问原理

在Java反射机制中,访问对象的int类型字段值(如field.getInt(object))的底层实现涉及JVM对内存偏移量的计算与直接内存访问。本文通过分析OpenJDK 17源码,揭示这一过程的核心实现逻辑。


一、字段偏移量计算

1. Java层初始化偏移量

反射访问字段时,会通过UnsafeFieldAccessorImpl初始化字段的偏移量:

UnsafeFieldAccessorImpl(Field field) {this.field = field;if (Modifier.isStatic(field.getModifiers()))fieldOffset = unsafe.staticFieldOffset(field);elsefieldOffset = unsafe.objectFieldOffset(field); // 实例字段偏移量isFinal = Modifier.isFinal(field.getModifiers());
}
  • 对于实例字段,调用Unsafe.objectFieldOffset(field)获取偏移量。

2. 本地方法调用

objectFieldOffset方法通过JNI调用C++实现:

public long objectFieldOffset(Field f) {return objectFieldOffset0(f); // 调用native方法
}
private native long objectFieldOffset0(Field f);
3. C++层计算偏移量

在JVM中,Unsafe_ObjectFieldOffset0最终调用find_field_offset函数:

UNSAFE_ENTRY(jlong, Unsafe_ObjectFieldOffset0(...)) {return find_field_offset(field, 0, THREAD);
} UNSAFE_ENDstatic jlong find_field_offset(...) {oop reflected = JNIHandles::resolve_non_null(field);Klass* k = java_lang_Class::as_Klass(mirror);int slot = java_lang_reflect_Field::slot(reflected); // 获取字段slotint offset = InstanceKlass::cast(k)->field_offset(slot); // 通过slot计算偏移量return field_offset_from_byte_offset(offset);
}
  • 字段slot:反射对象Field中存储的slot值对应类元数据(InstanceKlass)中字段的索引。

  • 偏移量计算InstanceKlass::field_offset(slot)通过slot索引从类元数据中获取字段的实际内存偏移量。


二、通过偏移量访问字段值

1. Java层读取字段值

反射调用getInt时,直接通过偏移量访问内存:

public int getInt(Object obj) {ensureObj(obj);return unsafe.getInt(obj, fieldOffset); // 使用偏移量读取int值
}
2. C++层内存访问

在JVM解释执行字节码时(如GETFIELD),访问字段的逻辑与反射一致:

// 字节码解释执行逻辑片段
case itos:SET_STACK_INT(obj->int_field(field_offset), -1);break;
  • obj->int_field(field_offset):通过偏移量直接从对象内存中读取int值。

  • SET_STACK_INT将值压入操作数栈。


三、关键数据结构与内存布局

1. 对象内存布局(oopDesc)

Java对象在内存中的布局包含对象头(Header)和实例数据(Instance Data):

  • 对象头:存储Mark Word和类指针(Klass*)。

  • 实例数据:字段按声明顺序排列,每个字段的偏移量由类元数据确定。

2. 类元数据(Klass)

InstanceKlass存储类的元信息,包括字段表:

class InstanceKlass : public Klass {// 字段表(fieldDescriptor数组)int field_offset(int slot) const {return field(slot)->offset(); // 获取字段偏移量}
};
3. 反射字段的slot映射

反射对象Field通过slot与类元数据关联:

// 反射Field对象存储slot值
int java_lang_reflect_Field::slot(oop reflect) {return reflect->int_field(_slot_offset);
}
  • slot值在类加载阶段生成,对应字段在类字段表中的索引。


四、性能优化与安全性

1. 偏移量缓存

反射调用Field.getInt()时,偏移量(fieldOffset)在UnsafeFieldAccessorImpl初始化阶段计算并缓存,后续访问无需重复计算。

2. 内存直接访问

通过Unsafe.getInt(obj, offset)绕过Java访问控制,直接操作内存。这种设计虽然高效,但也绕过了语言层面的安全性检查。

3. final字段处理

若字段为finalUnsafeFieldAccessorImpl会标记isFinal,部分JVM实现可能阻止修改(尽管某些场景下仍可通过反射修改)。


五、总结

通过反射访问int字段的流程可概括为:

  1. 计算偏移量:反射初始化阶段通过slot从类元数据获取字段偏移量。

  2. 内存访问:调用Unsafe.getInt()直接读取对象内存。

  3. 字节码执行:解释器/即时编译器使用相同机制访问字段。

这一机制体现了JVM反射与字节码执行在底层实现上的一致性:均依赖预先计算的字段偏移量直接操作内存。理解这一过程有助于优化反射性能,并为分析JVM内存模型提供基础。

openjdk17源码

UnsafeFieldAccessorImpl(Field field) {this.field = field;if (Modifier.isStatic(field.getModifiers()))fieldOffset = unsafe.staticFieldOffset(field);elsefieldOffset = unsafe.objectFieldOffset(field);isFinal = Modifier.isFinal(field.getModifiers());}public long objectFieldOffset(Field f) {if (f == null) {throw new NullPointerException();}return objectFieldOffset0(f);}private native long objectFieldOffset0(Field f);public int getInt(Object obj) throws IllegalArgumentException {ensureObj(obj);return unsafe.getInt(obj, fieldOffset);}} else if (type == Integer.TYPE) {return new UnsafeIntegerFieldAccessorImpl(field);}C++代码
{CC "objectFieldOffset0", CC "(" FLD ")J",           FN_PTR(Unsafe_ObjectFieldOffset0)},UNSAFE_ENTRY(jlong, Unsafe_ObjectFieldOffset0(JNIEnv *env, jobject unsafe, jobject field)) {return find_field_offset(field, 0, THREAD);
} UNSAFE_ENDstatic jlong find_field_offset(jobject field, int must_be_static, TRAPS) {assert(field != NULL, "field must not be NULL");oop reflected   = JNIHandles::resolve_non_null(field);oop mirror      = java_lang_reflect_Field::clazz(reflected);Klass* k        = java_lang_Class::as_Klass(mirror);int slot        = java_lang_reflect_Field::slot(reflected);int modifiers   = java_lang_reflect_Field::modifiers(reflected);if (must_be_static >= 0) {int really_is_static = ((modifiers & JVM_ACC_STATIC) != 0);if (must_be_static != really_is_static) {THROW_0(vmSymbols::java_lang_IllegalArgumentException());}}int offset = InstanceKlass::cast(k)->field_offset(slot);return field_offset_from_byte_offset(offset);
}int     field_offset      (int index) const { return field(index)->offset(); }int java_lang_reflect_Field::slot(oop reflect) {return reflect->int_field(_slot_offset);
}void java_lang_reflect_Field::set_slot(oop reflect, int value) {reflect->int_field_put(_slot_offset, value);
}oop Reflection::new_field(fieldDescriptor* fd, TRAPS) {Symbol*  field_name = fd->name();oop name_oop = StringTable::intern(field_name, CHECK_NULL);Handle name = Handle(THREAD, name_oop);Symbol*  signature  = fd->signature();InstanceKlass* holder = fd->field_holder();Handle type = new_type(signature, holder, CHECK_NULL);Handle rh  = java_lang_reflect_Field::create(CHECK_NULL);java_lang_reflect_Field::set_clazz(rh(), fd->field_holder()->java_mirror());java_lang_reflect_Field::set_slot(rh(), fd->index());inline jint oopDesc::int_field(int offset) const                    { return HeapAccess<>::load_at(as_oop(), offset);  }
inline jint oopDesc::int_field_raw(int offset) const                { return RawAccess<>::load_at(as_oop(), offset);   }
inline void oopDesc::int_field_put(int offset, jint value)          { HeapAccess<>::store_at(as_oop(), offset, value); }case stos:SET_STACK_INT(obj->short_field(field_offset), -1);break;case itos:SET_STACK_INT(obj->int_field(field_offset), -1);#define DEFINE_GETSETOOP(java_type, Type) \\
UNSAFE_ENTRY(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \return MemoryAccess<java_type>(thread, obj, offset).get(); \
} UNSAFE_END \T get() {if (_obj == NULL) {GuardUnsafeAccess guard(_thread);T ret = RawAccess<>::load(addr());return normalize_for_read(ret);} else {T ret = HeapAccess<>::load_at(_obj, _offset);return normalize_for_read(ret);}}

相关文章:

  • 97AB-ASEMI机器人功率器件专用97AB
  • 模型上下文协议(MCP)深度解析:大模型从“思考者“进化为“行动者“
  • 01 C++概述
  • 2025 SAP专精特新企业高峰论坛 | 工博科技以SAP公有云+AI赋能新质生产力​
  • 15、项目搭建:绘制城堡蓝图——React 19 工程配置
  • 在android 系统上qnn sdk转换,运行模型示例
  • Shell脚本-嵌套循环应用案例
  • 塔能科技:点亮节能之光,赋能工厂与城市
  • 013几何数学——算法备赛
  • 科技助力防灾减灾:卫星电话走进应急救援队伍
  • Python创意爱心代码分享指南
  • ​LangChain、LlamaIndex、MCP、Spring AI、Ollama​ 和 ​DeepSeek​ 的定义、关系及典型架构设计
  • 完美解决.NET Framework 4.0 中 System.Drawing 库不支持 WebP 格式的图像处理
  • Docker 获取 Python 镜像操作指南
  • Dots:动态实现GPUECSAnimationBaker的受击变红效果
  • 不同参数大小的DeepSeekR1模型对Java中new FileInputStream(“test.txt“).seek(100);语法错误的检查
  • WPF之Button控件详解
  • Golang|外观模式和具体逻辑
  • 【杂谈】-人工智能驱动的网络安全威胁:新一代网络钓鱼
  • 第33周JavaSpringCloud微服务 分布式综合应用
  • 法院为“外卖骑手”人身权益撑腰:依法认定实际投保人地位
  • 吕国范任河南省人民政府副省长
  • 影子调查丨危房之下,百余住户搬离梦嘉商贸楼
  • 央行副行长谈美债和美元波动:单一市场、单一资产的变动,对外储影响总体有限
  • 在上海生活8年,13岁英国女孩把城市记忆写进歌里
  • 首映|《人生开门红》:段子背后都是案子