在 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 的构造函数接收字节流和各种加载上下文,依次完成:

  1. 解析字节流:调用 parse_stream(stream, CHECK) 读取常量池、字段表、方法表、属性等,填充内部数据结构(如 _fields_methods_cp)。

  2. 后处理:调用 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 设计中的几个精妙之处:

  1. 解耦与延迟:字段偏移量的计算发生在类加载后处理阶段,与字节码解析解耦;偏移量的存储紧凑且带标记,节省内存。

  2. 优化第一:基本类型字段降序排列、best‑fit 空隙填充、对齐调整,都是为了减少对象大小并提高访问效率。

  3. 并发感知@Contended 注解的填充机制直接融合在布局算法中,体现了 JVM 对多核时代的适应。

  4. 底层访问友好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();
}

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐