Java 对象的内存密语:从字段偏移量计算到 Unsafe 访问的完整链路
在 Java 开发中,我们很少直接关心对象内部的字段是如何排列的——直到我们遇到内存对齐问题、伪共享性能瓶颈,或者使用 sun.misc.Unsafe 进行底层操作。实际上,JVM 在类加载的最后阶段,会精心计算每个字段在对象内存中的偏移量。这个偏移量一旦确定,Unsafe.getXxx(obj, offset) 就能像 C 语言指针一样高速访问数据。
本文将结合 OpenJDK 源码,完整展示一条逻辑链条:
class 字节流 →
KlassFactory→ClassFileParser→ 字段布局 Best‑Fit 算法 → 偏移量编码 → 运行时Unsafe利用偏移量读写字段
通过这条路径,你不仅能理解 JVM 的内部优化策略,还能掌握 @Contended 注解消除伪共享的实现原理。
一、一切的源头:KlassFactory::create_from_stream
JVM 加载一个类(无论是从 .class 文件、JAR 还是网络)最终都会调用 KlassFactory::create_from_stream。这个函数负责将字节流转换成代表类元数据的 InstanceKlass 对象。
cpp
// 源码位置:hotspot/share/classfile/klassFactory.cpp
InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
const ClassLoadInfo& cl_info,
TRAPS) {
// ...
// 1. 如果有 JVMTI 代理,可能先修改字节流(ClassFileLoadHook)
if (!cl_info.is_hidden()) {
stream = check_class_file_load_hook(stream, name, loader_data,
cl_info.protection_domain(),
&cached_class_file, CHECK_NULL);
}
// 2. 核心:创建 ClassFileParser 并解析字节流
ClassFileParser parser(stream, name, loader_data, &cl_info,
ClassFileParser::BROADCAST, CHECK_NULL);
// 3. 生成 InstanceKlass 实例
InstanceKlass* result = parser.create_instance_klass(old_stream != stream,
*cl_inst_info,
CHECK_NULL);
// 4. 缓存 JVMTI 可能修改过的字节码
if (cached_class_file != NULL) {
result->set_cached_class_file(cached_class_file);
}
return result;
}
关键点:
-
ClassFileStream封装了.class文件的字节流,包含魔数、版本、常量池等原始数据。 -
ClassFileParser是整个解析工作的发动机。 -
create_instance_klass会调用解析器内部已经计算好的各种布局信息(字段偏移、虚表大小等)来构造最终的InstanceKlass。
二、ClassFileParser 构造函数:解析与后处理
ClassFileParser 的构造函数接收字节流和各种加载上下文,依次完成:
-
解析字节流:调用
parse_stream(stream, CHECK)读取常量池、字段表、方法表、属性等,填充内部数据结构(如_fields、_methods、_cp)。 -
后处理:调用
post_process_parsed_stream,这是本文的核心。
cpp
// 源码位置:hotspot/share/classfile/classFileParser.cpp
ClassFileParser::ClassFileParser(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
const ClassLoadInfo* cl_info,
Publicity pub_level,
TRAPS) :
// ... 初始化成员变量(省略)
{
// ... 设置验证标志等
parse_stream(stream, CHECK); // ① 解析字节码
post_process_parsed_stream(stream, _cp, CHECK); // ② 后处理
}
post_process_parsed_stream 的任务包括:
-
检查
java.lang.Object不能实现接口。 -
解析超类(如果尚未解析)。
-
计算传递闭包接口列表(
_transitive_interfaces)。 -
对方法进行排序(影响虚表和 Miranda 方法)。
-
计算 vtable 和 itable 大小。
-
最重要的:执行字段布局(
FieldLayoutBuilder)。 -
记录当前类是强/软/弱/虚引用类型。
正是字段布局这一步,决定了每个字段在对象实例中的偏移量,而这个偏移量正是 Unsafe 访问的基础。
三、字段布局的核心:FieldLayoutBuilder::build_layout
FieldLayoutBuilder 负责将已经解析好的字段数组(_fields)转换成具体的偏移量分配方案。它支持两种布局风格:常规布局(当前代码所示)和紧凑布局(历史版本,本文略)。
cpp
// 源码位置:hotspot/share/classfile/fieldLayoutBuilder.cpp
void FieldLayoutBuilder::build_layout() {
compute_regular_layout(); // 只调用常规布局算法
}
void FieldLayoutBuilder::compute_regular_layout() {
bool need_tail_padding = false;
prologue(); // 初始化字段分组(基本类型组、引用组、竞争组)
regular_field_sorting(); // 对基本类型字段按大小降序排序(long/double 排最前)
// 处理整个类被 @Contended 标记的情况
if (_is_contended) {
_layout->set_start(_layout->last_block());
insert_contended_padding(_layout->start()); // 在类开头插入填充,使后续字段对齐到缓存行边界
need_tail_padding = true;
}
// 将无竞争标记的普通字段加入布局
_layout->add(_root_group->primitive_fields()); // 先加基本类型
_layout->add(_root_group->oop_fields()); // 再加引用类型
// 处理每个 @Contended 字段组(例如多个字段共用一个 contention group tag)
if (!_contended_groups.is_empty()) {
for (int i = 0; i < _contended_groups.length(); i++) {
FieldGroup* cg = _contended_groups.at(i);
LayoutRawBlock* start = _layout->last_block();
insert_contended_padding(start); // 组前置填充
_layout->add(cg->primitive_fields(), start);
_layout->add(cg->oop_fields(), start);
need_tail_padding = true;
}
}
// 末尾填充,保证竞争组后面的字段不会因意外共享缓存行
if (need_tail_padding) {
insert_contended_padding(_layout->last_block());
}
// 静态字段布局:先放引用字段,再放基本字段(顺序影响类静态区的排列)
_static_layout->add_contiguously(this->_static_fields->oop_fields());
_static_layout->add(this->_static_fields->primitive_fields());
epilogue(); // 将最终计算的偏移量写入 FieldInfo
}
关键逻辑解读:
-
regular_field_sorting:将基本类型字段(long, double, int, float, char, short, byte, boolean)按大小从大到小排序,这样大字段优先对齐,产生的内存空隙更容易被小字段填补。 -
@Contended处理:Java 8 引入的@Contended注解通过插入填充(padding)使字段独立于一个缓存行(通常 64 字节),避免多线程下的伪共享。JVM 会在标记字段前后各填充约 64 字节(实际大小取决于-XX:ContendedPaddingWidth)。 -
静态字段单独布局:静态字段存储在类元数据的静态区,不在对象实例中,但布局逻辑相似。
四、Best‑Fit 空隙填充算法:FieldLayout::add
FieldLayout::add 负责将一组字段块(LayoutRawBlock)插入到已有的布局链中,它使用一种改进的 Best‑Fit 策略:从后向前扫描空闲块,选择能容纳当前字段的最小空闲块。这样做既减少了内存碎片,又提高了缓存局部性(新字段尽可能放在靠近末尾的已有空隙中)。
cpp
// 源码位置:hotspot/share/classfile/fieldLayout.cpp
void FieldLayout::add(GrowableArray<LayoutRawBlock*>* list, LayoutRawBlock* start) {
if (list == NULL) return;
if (start == NULL) start = this->_start;
bool last_search_success = false;
int last_size = 0;
int last_alignment = 0;
for (int i = 0; i < list->length(); i++) {
LayoutRawBlock* b = list->at(i);
LayoutRawBlock* candidate = NULL;
// 情况1:起始块就是末尾块 → 直接追加
if (start == last_block()) {
candidate = last_block();
}
// 情况2:当前字段大小/对齐与上一个完全相同,且上次搜索失败 → 直接追加(避免重复无用扫描)
else if (b->size() == last_size && b->alignment() == last_alignment && !last_search_success) {
candidate = last_block();
}
else {
last_size = b->size();
last_alignment = b->alignment();
LayoutRawBlock* cursor = last_block()->prev_block();
last_search_success = true;
// 从末尾向前扫描,寻找所有能容纳 b 的空闲块
while (cursor != start) {
if (cursor->kind() == LayoutRawBlock::EMPTY &&
cursor->fit(b->size(), b->alignment())) {
// 选择最小的空闲块(best‑fit)
if (candidate == NULL || cursor->size() < candidate->size()) {
candidate = cursor;
}
}
cursor = cursor->prev_block();
}
if (candidate == NULL) { // 没找到合适空隙
candidate = last_block(); // 追加到末尾
last_search_success = false;
}
}
// 将字段块插入到选中的空闲块中
insert_field_block(candidate, b);
}
}
值得注意的优化:
-
记录上一次字段的尺寸和对齐,如果本次字段完全相同且上次搜索无果,则不再扫描(因为布局没有变化,结果必然相同)。
-
扫描方向从后向前,因为新字段更有可能在末尾附近找到空隙,减少对前面已稳定布局的扰动。
-
选用最小合适空闲块(best‑fit)而非最先找到的(first‑fit),能更有效地控制碎片。
五、对齐处理与偏移量写入:insert_field_block
当选定了空闲块 slot 来放置字段 block 后,insert_field_block 负责对齐调整和最终偏移量的赋值。
cpp
LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawBlock* block) {
assert(slot->kind() == LayoutRawBlock::EMPTY, "只能插入到空闲块中");
// 检查 slot 的起始偏移是否满足 block 的对齐要求
if (slot->offset() % block->alignment() != 0) {
int adjustment = block->alignment() - (slot->offset() % block->alignment());
// 创建一个小的填充块(EMPTY)来补齐对齐
LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment);
insert(slot, adj); // 先插入填充块
}
insert(slot, block); // 再插入真正的字段块
// 如果原空闲块被完全用尽,则将其从链表中移除
if (slot->size() == 0) {
remove(slot);
}
// 关键:将计算出的字段偏移量写回 FieldInfo 结构
FieldInfo::from_field_array(_fields, block->field_index())->set_offset(block->offset());
return block;
}
对齐逻辑:
-
假设
slot起始偏移是 6,而block需要 8 字节对齐,则adjustment = 8 - (6 % 8) = 2。JVM 会先创建一个 2 字节的空白填充块,使 slot 剩余部分的起始偏移变为 8,满足对齐。 -
字段块的
offset()方法会返回经过对齐调整后的最终偏移量。 -
这个偏移量最终被写入
FieldInfo,供运行时访问。
六、偏移量的编码存储:FieldInfo::set_offset
FieldInfo 是 JVM 内部描述字段的结构,它存储在一个 Array<u2> 中(每个 u2 为 16 位)。为了在有限空间内保存偏移量(32 位)以及额外的标志位,JVM 采用了位打包技巧。
cpp
// 源码位置:hotspot/share/oops/fieldInfo.hpp
static FieldInfo* from_field_array(Array<u2>* fields, int index) {
// 每个 FieldInfo 占用 field_slots 个 u2 元素
return ((FieldInfo*)fields->adr_at(index * field_slots));
}
void set_offset(u4 val) {
// 左移 FIELDINFO_TAG_SIZE 位(通常为 2),为标记位留出空间
val = val << FIELDINFO_TAG_SIZE;
// 将低 16 位存入第一个 u2,并打上 TAG_OFFSET 标记
_shorts[low_packed_offset] = extract_low_short_from_int(val) | FIELDINFO_TAG_OFFSET;
// 高 16 位存入第二个 u2
_shorts[high_packed_offset] = extract_high_short_from_int(val);
}
为什么需要左移 2 位?
-
因为偏移量总是 4 字节对齐的(至少 JVM 保证对象内字段偏移按字长对齐),最低 2 位总是 0。
-
JVM 将这些空闲的比特位用来存储标记(比如
FIELDINFO_TAG_OFFSET表示该槽位存的是偏移量,不是其他类型的数据)。 -
这样在访问时,右移 2 位并清除标记即可恢复原始偏移量,节省了额外的存储空间。
七、运行时:Unsafe 如何利用偏移量访问字段
当 Java 代码通过 Unsafe.getChar(obj, offset) 读取字段时,实际上直接使用了解析阶段计算好的偏移量。
java
// 源码位置:jdk/src/share/classes/sun/misc/Unsafe.java (实际在 Unsafe 实现类中)
public char getChar(Object obj) throws IllegalArgumentException {
ensureObj(obj);
return unsafe.getChar(obj, fieldOffset);
}
abstract class UnsafeFieldAccessorImpl {
UnsafeFieldAccessorImpl(Field field) {
this.field = field;
if (Modifier.isStatic(field.getModifiers()))
fieldOffset = unsafe.staticFieldOffset(field); // 静态字段偏移
else
fieldOffset = unsafe.objectFieldOffset(field); // 实例字段偏移
isFinal = Modifier.isFinal(field.getModifiers());
}
}
unsafe.objectFieldOffset(field) 是一个 native 方法,它最终会调用 objectFieldOffset0,从 Field 对象持有的 FieldInfo 中读取之前 set_offset 写入的偏移量。
java
public long objectFieldOffset(Field f) {
if (f == null) throw new NullPointerException();
return objectFieldOffset0(f);
}
private native long objectFieldOffset0(Field f);
在 JVM 内部,objectFieldOffset0 会找到对应字段的 FieldInfo,读取其中的偏移量(右移 2 位去掉标记),然后返回给 Java 层。此后,Unsafe 的所有 get* 和 put* 操作都直接基于这个数值,绕过了 Java 的可见性和访问控制检查,速度堪比 C 语言的内存读写。
八、总结:从字节码到直接内存访问的完整闭环
整条链路展示了 JVM 设计中的几个精妙之处:
-
解耦与延迟:字段偏移量的计算发生在类加载后处理阶段,与字节码解析解耦;偏移量的存储紧凑且带标记,节省内存。
-
优化第一:基本类型字段降序排列、best‑fit 空隙填充、对齐调整,都是为了减少对象大小并提高访问效率。
-
并发感知:
@Contended注解的填充机制直接融合在布局算法中,体现了 JVM 对多核时代的适应。 -
底层访问友好:
Unsafe直接消费这些偏移量,使得 JVM 内部和 JDK 底层库(如AtomicLong)能够实现极高性能的字段访问。
当你下次使用 Unsafe 或者好奇某个 long 字段在内存中的位置时,不妨回想一下:这一切都源于 ClassFileParser 在加载那一刻的一次次扫描、比较和填充。理解这段代码,不仅有助于性能调优,更能让你对 Java 平台的底层魅力有更深刻的认知。
#源码
public char getChar(Object obj) throws IllegalArgumentException {
ensureObj(obj);
return unsafe.getChar(obj, fieldOffset);
}
UnsafeFieldAccessorImpl(Field field) {
this.field = field;
if (Modifier.isStatic(field.getModifiers()))
fieldOffset = unsafe.staticFieldOffset(field);
else
fieldOffset = 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);
InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
const ClassLoadInfo& cl_info,
TRAPS) {
assert(stream != NULL, "invariant");
assert(loader_data != NULL, "invariant");
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
JvmtiCachedClassFileData* cached_class_file = NULL;
ClassFileStream* old_stream = stream;
// increment counter
THREAD->statistical_info().incr_define_class_count();
// Skip this processing for VM hidden classes
if (!cl_info.is_hidden()) {
stream = check_class_file_load_hook(stream,
name,
loader_data,
cl_info.protection_domain(),
&cached_class_file,
CHECK_NULL);
}
ClassFileParser parser(stream,
name,
loader_data,
&cl_info,
ClassFileParser::BROADCAST, // publicity level
CHECK_NULL);
const ClassInstanceInfo* cl_inst_info = cl_info.class_hidden_info_ptr();
InstanceKlass* result = parser.create_instance_klass(old_stream != stream, *cl_inst_info, CHECK_NULL);
assert(result != NULL, "result cannot be null with no pending exception");
if (cached_class_file != NULL) {
// JVMTI: we have an InstanceKlass now, tell it about the cached bytes
result->set_cached_class_file(cached_class_file);
}
JFR_ONLY(ON_KLASS_CREATION(result, parser, THREAD);)
#if INCLUDE_CDS
if (Arguments::is_dumping_archive()) {
ClassLoader::record_result(THREAD, result, stream);
}
#endif // INCLUDE_CDS
return result;
}
ClassFileParser::ClassFileParser(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
const ClassLoadInfo* cl_info,
Publicity pub_level,
TRAPS) :
_stream(stream),
_class_name(NULL),
_loader_data(loader_data),
_is_hidden(cl_info->is_hidden()),
_can_access_vm_annotations(cl_info->can_access_vm_annotations()),
_orig_cp_size(0),
_super_klass(),
_cp(NULL),
_fields(NULL),
_methods(NULL),
_inner_classes(NULL),
_nest_members(NULL),
_nest_host(0),
_permitted_subclasses(NULL),
_record_components(NULL),
_local_interfaces(NULL),
_transitive_interfaces(NULL),
_combined_annotations(NULL),
_class_annotations(NULL),
_class_type_annotations(NULL),
_fields_annotations(NULL),
_fields_type_annotations(NULL),
_klass(NULL),
_klass_to_deallocate(NULL),
_parsed_annotations(NULL),
_fac(NULL),
_field_info(NULL),
_method_ordering(NULL),
_all_mirandas(NULL),
_vtable_size(0),
_itable_size(0),
_num_miranda_methods(0),
_rt(REF_NONE),
_protection_domain(cl_info->protection_domain()),
_access_flags(),
_pub_level(pub_level),
_bad_constant_seen(0),
_synthetic_flag(false),
_sde_length(false),
_sde_buffer(NULL),
_sourcefile_index(0),
_generic_signature_index(0),
_major_version(0),
_minor_version(0),
_this_class_index(0),
_super_class_index(0),
_itfs_len(0),
_java_fields_count(0),
_need_verify(false),
_relax_verify(false),
_has_nonstatic_concrete_methods(false),
_declares_nonstatic_concrete_methods(false),
_has_final_method(false),
_has_contended_fields(false),
_has_finalizer(false),
_has_empty_finalizer(false),
_has_vanilla_constructor(false),
_max_bootstrap_specifier_index(-1) {
_class_name = name != NULL ? name : vmSymbols::unknown_class_name();
_class_name->increment_refcount();
assert(_loader_data != NULL, "invariant");
assert(stream != NULL, "invariant");
assert(_stream != NULL, "invariant");
assert(_stream->buffer() == _stream->current(), "invariant");
assert(_class_name != NULL, "invariant");
assert(0 == _access_flags.as_int(), "invariant");
// Figure out whether we can skip format checking (matching classic VM behavior)
if (DumpSharedSpaces) {
// verify == true means it's a 'remote' class (i.e., non-boot class)
// Verification decision is based on BytecodeVerificationRemote flag
// for those classes.
_need_verify = (stream->need_verify()) ? BytecodeVerificationRemote :
BytecodeVerificationLocal;
}
else {
_need_verify = Verifier::should_verify_for(_loader_data->class_loader(),
stream->need_verify());
}
// synch back verification state to stream
stream->set_verify(_need_verify);
// Check if verification needs to be relaxed for this class file
// Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376)
_relax_verify = relax_format_check_for(_loader_data);
parse_stream(stream, CHECK);
post_process_parsed_stream(stream, _cp, CHECK);
}
void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const stream,
ConstantPool* cp,
TRAPS) {
assert(stream != NULL, "invariant");
assert(stream->at_eos(), "invariant");
assert(cp != NULL, "invariant");
assert(_loader_data != NULL, "invariant");
if (_class_name == vmSymbols::java_lang_Object()) {
check_property(_local_interfaces == Universe::the_empty_instance_klass_array(),
"java.lang.Object cannot implement an interface in class file %s",
CHECK);
}
// We check super class after class file is parsed and format is checked
if (_super_class_index > 0 && NULL == _super_klass) {
Symbol* const super_class_name = cp->klass_name_at(_super_class_index);
if (_access_flags.is_interface()) {
// Before attempting to resolve the superclass, check for class format
// errors not checked yet.
guarantee_property(super_class_name == vmSymbols::java_lang_Object(),
"Interfaces must have java.lang.Object as superclass in class file %s",
CHECK);
}
Handle loader(THREAD, _loader_data->class_loader());
_super_klass = (const InstanceKlass*)
SystemDictionary::resolve_super_or_fail(_class_name,
super_class_name,
loader,
_protection_domain,
true,
CHECK);
}
if (_super_klass != NULL) {
if (_super_klass->has_nonstatic_concrete_methods()) {
_has_nonstatic_concrete_methods = true;
}
if (_super_klass->is_interface()) {
classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD);
return;
}
}
// Compute the transitive list of all unique interfaces implemented by this class
_transitive_interfaces =
compute_transitive_interfaces(_super_klass,
_local_interfaces,
_loader_data,
CHECK);
assert(_transitive_interfaces != NULL, "invariant");
// sort methods
_method_ordering = sort_methods(_methods);
_all_mirandas = new GrowableArray<Method*>(20);
Handle loader(THREAD, _loader_data->class_loader());
klassVtable::compute_vtable_size_and_num_mirandas(&_vtable_size,
&_num_miranda_methods,
_all_mirandas,
_super_klass,
_methods,
_access_flags,
_major_version,
loader,
_class_name,
_local_interfaces);
// Size of Java itable (in words)
_itable_size = _access_flags.is_interface() ? 0 :
klassItable::compute_itable_size(_transitive_interfaces);
assert(_fac != NULL, "invariant");
assert(_parsed_annotations != NULL, "invariant");
_field_info = new FieldLayoutInfo();
FieldLayoutBuilder lb(class_name(), super_klass(), _cp, _fields,
_parsed_annotations->is_contended(), _field_info);
lb.build_layout();
// Compute reference typ
_rt = (NULL ==_super_klass) ? REF_NONE : _super_klass->reference_type();
}
void FieldLayoutBuilder::build_layout() {
compute_regular_layout();
}
// Computation of regular classes layout is an evolution of the previous default layout
// (FieldAllocationStyle 1):
// - primitive fields are allocated first (from the biggest to the smallest)
// - then oop fields are allocated, either in existing gaps or at the end of
// the layout
void FieldLayoutBuilder::compute_regular_layout() {
bool need_tail_padding = false;
prologue();
regular_field_sorting();
if (_is_contended) {
_layout->set_start(_layout->last_block());
// insertion is currently easy because the current strategy doesn't try to fill holes
// in super classes layouts => the _start block is by consequence the _last_block
insert_contended_padding(_layout->start());
need_tail_padding = true;
}
_layout->add(_root_group->primitive_fields());
_layout->add(_root_group->oop_fields());
if (!_contended_groups.is_empty()) {
for (int i = 0; i < _contended_groups.length(); i++) {
FieldGroup* cg = _contended_groups.at(i);
LayoutRawBlock* start = _layout->last_block();
insert_contended_padding(start);
_layout->add(cg->primitive_fields(), start);
_layout->add(cg->oop_fields(), start);
need_tail_padding = true;
}
}
if (need_tail_padding) {
insert_contended_padding(_layout->last_block());
}
_static_layout->add_contiguously(this->_static_fields->oop_fields());
_static_layout->add(this->_static_fields->primitive_fields());
epilogue();
}
// Insert a set of fields into a layout using a best-fit strategy.
// For each field, search for the smallest empty slot able to fit the field
// (satisfying both size and alignment requirements), if none is found,
// add the field at the end of the layout.
// Fields cannot be inserted before the block specified in the "start" argument
void FieldLayout::add(GrowableArray<LayoutRawBlock*>* list, LayoutRawBlock* start) {
if (list == NULL) return;
if (start == NULL) start = this->_start;
bool last_search_success = false;
int last_size = 0;
int last_alignment = 0;
for (int i = 0; i < list->length(); i ++) {
LayoutRawBlock* b = list->at(i);
LayoutRawBlock* cursor = NULL;
LayoutRawBlock* candidate = NULL;
// if start is the last block, just append the field
if (start == last_block()) {
candidate = last_block();
}
// Before iterating over the layout to find an empty slot fitting the field's requirements,
// check if the previous field had the same requirements and if the search for a fitting slot
// was successful. If the requirements were the same but the search failed, a new search will
// fail the same way, so just append the field at the of the layout.
else if (b->size() == last_size && b->alignment() == last_alignment && !last_search_success) {
candidate = last_block();
} else {
// Iterate over the layout to find an empty slot fitting the field's requirements
last_size = b->size();
last_alignment = b->alignment();
cursor = last_block()->prev_block();
assert(cursor != NULL, "Sanity check");
last_search_success = true;
while (cursor != start) {
if (cursor->kind() == LayoutRawBlock::EMPTY && cursor->fit(b->size(), b->alignment())) {
if (candidate == NULL || cursor->size() < candidate->size()) {
candidate = cursor;
}
}
cursor = cursor->prev_block();
}
if (candidate == NULL) {
candidate = last_block();
last_search_success = false;
}
assert(candidate != NULL, "Candidate must not be null");
assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block");
assert(candidate->fit(b->size(), b->alignment()), "Candidate must be able to store the block");
}
insert_field_block(candidate, b);
}
}
LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawBlock* block) {
assert(slot->kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks");
if (slot->offset() % block->alignment() != 0) {
int adjustment = block->alignment() - (slot->offset() % block->alignment());
LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment);
insert(slot, adj);
}
insert(slot, block);
if (slot->size() == 0) {
remove(slot);
}
FieldInfo::from_field_array(_fields, block->field_index())->set_offset(block->offset());
return block;
}
static FieldInfo* from_field_array(Array<u2>* fields, int index) {
return ((FieldInfo*)fields->adr_at(index * field_slots));
}
void set_offset(u4 val) {
val = val << FIELDINFO_TAG_SIZE; // make room for tag
_shorts[low_packed_offset] = extract_low_short_from_int(val) | FIELDINFO_TAG_OFFSET;
_shorts[high_packed_offset] = extract_high_short_from_int(val);
}
InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook,
const ClassInstanceInfo& cl_inst_info,
TRAPS) {
if (_klass != NULL) {
return _klass;
}
InstanceKlass* const ik =
InstanceKlass::allocate_instance_klass(*this, CHECK_NULL);
if (is_hidden()) {
mangle_hidden_class_name(ik);
}
fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL);
assert(_klass == ik, "invariant");
return ik;
}
void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
bool changed_by_loadhook,
const ClassInstanceInfo& cl_inst_info,
TRAPS) {
assert(ik != NULL, "invariant");
// Set name and CLD before adding to CLD
ik->set_class_loader_data(_loader_data);
ik->set_name(_class_name);
// Add all classes to our internal class loader list here,
// including classes in the bootstrap (NULL) class loader.
const bool publicize = !is_internal();
_loader_data->add_class(ik, publicize);
set_klass_to_deallocate(ik);
assert(_field_info != NULL, "invariant");
assert(ik->static_field_size() == _field_info->_static_field_size, "sanity");
assert(ik->nonstatic_oop_map_count() == _field_info->oop_map_blocks->_nonstatic_oop_map_count,
"sanity");
assert(ik->is_instance_klass(), "sanity");
assert(ik->size_helper() == _field_info->_instance_size, "sanity");
// Fill in information already parsed
ik->set_should_verify_class(_need_verify);
// Not yet: supers are done below to support the new subtype-checking fields
ik->set_nonstatic_field_size(_field_info->_nonstatic_field_size);
ik->set_has_nonstatic_fields(_field_info->_has_nonstatic_fields);
assert(_fac != NULL, "invariant");
ik->set_static_oop_field_count(_fac->count[STATIC_OOP]);
// this transfers ownership of a lot of arrays from
// the parser onto the InstanceKlass*
apply_parsed_class_metadata(ik, _java_fields_count);
// can only set dynamic nest-host after static nest information is set
if (cl_inst_info.dynamic_nest_host() != NULL) {
ik->set_nest_host(cl_inst_info.dynamic_nest_host());
}
// note that is not safe to use the fields in the parser from this point on
assert(NULL == _cp, "invariant");
assert(NULL == _fields, "invariant");
assert(NULL == _methods, "invariant");
assert(NULL == _inner_classes, "invariant");
assert(NULL == _nest_members, "invariant");
assert(NULL == _combined_annotations, "invariant");
assert(NULL == _record_components, "invariant");
assert(NULL == _permitted_subclasses, "invariant");
if (_has_final_method) {
ik->set_has_final_method();
}
ik->copy_method_ordering(_method_ordering, CHECK);
// The InstanceKlass::_methods_jmethod_ids cache
// is managed on the assumption that the initial cache
// size is equal to the number of methods in the class. If
// that changes, then InstanceKlass::idnum_can_increment()
// has to be changed accordingly.
ik->set_initial_method_idnum(ik->methods()->length());
ik->set_this_class_index(_this_class_index);
if (_is_hidden) {
// _this_class_index is a CONSTANT_Class entry that refers to this
// hidden class itself. If this class needs to refer to its own methods
// or fields, it would use a CONSTANT_MethodRef, etc, which would reference
// _this_class_index. However, because this class is hidden (it's
// not stored in SystemDictionary), _this_class_index cannot be resolved
// with ConstantPool::klass_at_impl, which does a SystemDictionary lookup.
// Therefore, we must eagerly resolve _this_class_index now.
ik->constants()->klass_at_put(_this_class_index, ik);
}
ik->set_minor_version(_minor_version);
ik->set_major_version(_major_version);
ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods);
ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods);
if (_is_hidden) {
ik->set_is_hidden();
}
// Set PackageEntry for this_klass
oop cl = ik->class_loader();
Handle clh = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(cl));
ClassLoaderData* cld = ClassLoaderData::class_loader_data_or_null(clh());
ik->set_package(cld, NULL, CHECK);
const Array<Method*>* const methods = ik->methods();
assert(methods != NULL, "invariant");
const int methods_len = methods->length();
check_methods_for_intrinsics(ik, methods);
// Fill in field values obtained by parse_classfile_attributes
if (_parsed_annotations->has_any_annotations()) {
_parsed_annotations->apply_to(ik);
}
apply_parsed_class_attributes(ik);
// Miranda methods
if ((_num_miranda_methods > 0) ||
// if this class introduced new miranda methods or
(_super_klass != NULL && _super_klass->has_miranda_methods())
// super class exists and this class inherited miranda methods
) {
ik->set_has_miranda_methods(); // then set a flag
}
// Fill in information needed to compute superclasses.
ik->initialize_supers(const_cast<InstanceKlass*>(_super_klass), _transitive_interfaces, CHECK);
ik->set_transitive_interfaces(_transitive_interfaces);
ik->set_local_interfaces(_local_interfaces);
_transitive_interfaces = NULL;
_local_interfaces = NULL;
// Initialize itable offset tables
klassItable::setup_itable_offset_table(ik);
// Compute transitive closure of interfaces this class implements
// Do final class setup
OopMapBlocksBuilder* oop_map_blocks = _field_info->oop_map_blocks;
if (oop_map_blocks->_nonstatic_oop_map_count > 0) {
oop_map_blocks->copy(ik->start_of_nonstatic_oop_maps());
}
if (_has_contended_fields || _parsed_annotations->is_contended() ||
( _super_klass != NULL && _super_klass->has_contended_annotations())) {
ik->set_has_contended_annotations(true);
}
// Fill in has_finalizer, has_vanilla_constructor, and layout_helper
set_precomputed_flags(ik);
// check if this class can access its super class
check_super_class_access(ik, CHECK);
// check if this class can access its superinterfaces
check_super_interface_access(ik, CHECK);
// check if this class overrides any final method
check_final_method_override(ik, CHECK);
// reject static interface methods prior to Java 8
if (ik->is_interface() && _major_version < JAVA_8_VERSION) {
check_illegal_static_method(ik, CHECK);
}
// Obtain this_klass' module entry
ModuleEntry* module_entry = ik->module();
assert(module_entry != NULL, "module_entry should always be set");
// Obtain java.lang.Module
Handle module_handle(THREAD, module_entry->module());
// Allocate mirror and initialize static fields
// The create_mirror() call will also call compute_modifiers()
java_lang_Class::create_mirror(ik,
Handle(THREAD, _loader_data->class_loader()),
module_handle,
_protection_domain,
cl_inst_info.class_data(),
CHECK);
assert(_all_mirandas != NULL, "invariant");
// Generate any default methods - default methods are public interface methods
// that have a default implementation. This is new with Java 8.
if (_has_nonstatic_concrete_methods) {
DefaultMethods::generate_default_methods(ik,
_all_mirandas,
CHECK);
}
// Add read edges to the unnamed modules of the bootstrap and app class loaders.
if (changed_by_loadhook && !module_handle.is_null() && module_entry->is_named() &&
!module_entry->has_default_read_edges()) {
if (!module_entry->set_has_default_read_edges()) {
// We won a potential race
JvmtiExport::add_default_read_edges(module_handle, THREAD);
}
}
ClassLoadingService::notify_class_loaded(ik, false /* not shared class */);
if (!is_internal()) {
ik->print_class_load_logging(_loader_data, module_entry, _stream);
if (ik->minor_version() == JAVA_PREVIEW_MINOR_VERSION &&
ik->major_version() == JVM_CLASSFILE_MAJOR_VERSION &&
log_is_enabled(Info, class, preview)) {
ResourceMark rm;
log_info(class, preview)("Loading class %s that depends on preview features (class file version %d.65535)",
ik->external_name(), JVM_CLASSFILE_MAJOR_VERSION);
}
if (log_is_enabled(Debug, class, resolve)) {
ResourceMark rm;
// print out the superclass.
const char * from = ik->external_name();
if (ik->java_super() != NULL) {
log_debug(class, resolve)("%s %s (super)",
from,
ik->java_super()->external_name());
}
// print out each of the interface classes referred to by this class.
const Array<InstanceKlass*>* const local_interfaces = ik->local_interfaces();
if (local_interfaces != NULL) {
const int length = local_interfaces->length();
for (int i = 0; i < length; i++) {
const InstanceKlass* const k = local_interfaces->at(i);
const char * to = k->external_name();
log_debug(class, resolve)("%s %s (interface)", from, to);
}
}
}
}
JFR_ONLY(INIT_ID(ik);)
// If we reach here, all is well.
// Now remove the InstanceKlass* from the _klass_to_deallocate field
// in order for it to not be destroyed in the ClassFileParser destructor.
set_klass_to_deallocate(NULL);
// it's official
set_klass(ik);
debug_only(ik->verify();)
}
// Transfer ownership of metadata allocated to the InstanceKlass.
void ClassFileParser::apply_parsed_class_metadata(
InstanceKlass* this_klass,
int java_fields_count) {
assert(this_klass != NULL, "invariant");
_cp->set_pool_holder(this_klass);
this_klass->set_constants(_cp);
this_klass->set_fields(_fields, java_fields_count);
this_klass->set_methods(_methods);
this_klass->set_inner_classes(_inner_classes);
this_klass->set_nest_members(_nest_members);
this_klass->set_nest_host_index(_nest_host);
this_klass->set_annotations(_combined_annotations);
this_klass->set_permitted_subclasses(_permitted_subclasses);
this_klass->set_record_components(_record_components);
// Delay the setting of _local_interfaces and _transitive_interfaces until after
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
// its _super. If an OOM occurs while loading the current klass, its _super field
// may not have been set. When GC tries to free the klass, the _transitive_interfaces
// may be deallocated mistakenly in InstanceKlass::deallocate_interfaces(). Subsequent
// dereferences to the deallocated _transitive_interfaces will result in a crash.
// Clear out these fields so they don't get deallocated by the destructor
clear_class_metadata();
}
更多推荐



所有评论(0)