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

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);}}

http://www.xdnf.cn/news/2842.html

相关文章:

  • J-Link RTT打印输出调试信息
  • 深入蜂窝物联网:第二章 深度解读 NB-IoT:协议栈、部署与典型应用
  • 两地三中心
  • MySQL数据库(14)—— 使用C操作MySQL
  • 【ACL系列论文写作指北03-相关工作怎么写】-展示视野与定位创新
  • leetcode283-移动零
  • 第二章 信息技术发展(2.2 新一代信息技术及应用)
  • Linux428 chmod 0xxx 1xxx 2xxx 4xxx;umask;chown 属主属组 软件包rpm
  • ECharts散点图-散点图20,附视频讲解与代码下载
  • php数据库连接
  • Docker安装的mysql限制ip访问
  • [三分钟]web自动化测试(三):selenium自动化测试常用函数(下)
  • 基于蓝牙Beacon人员导航方案
  • 【Linux】第十二章 安装和更新软件包
  • 第七章:Server/Client Communication
  • 增量抽取的场景下,周期快照表最新分区的数据是如何生成?
  • 安卓开发学习随记
  • OpenCV 图形API(69)图像与通道拼接函数------将一个 GMat 类型的对象转换为另一个具有不同深度GMat对象函数convertTo()
  • vue3使其另一台服务器上的x.html,实现x.html调用中的函数,并向其传递数据。
  • kylin v10 + argo + ascend 310p多机多卡 pytorch distributed 训练
  • JavaWeb学习打卡-Day4-会话技术、JWT、Filter、Interceptor
  • WPF之Label控件详解
  • GoLand包的爆红问题解决
  • Coupang火箭计划深度攻略:eBay卖家突破韩国市场的三维数据作战模型
  • 面试算法高频08-动态规划-03
  • InitializingBean接口和@PostConstruct-笔记
  • Spring系列四:AOP切面编程 第二部分
  • EasyGBS国标GB28181设备管理软件打造园区安防高效解决方案
  • 【C++】类和对象(4)
  • 开源CMS系统的SEO优化功能主要依赖哪些插件?