JVM 字段布局揭秘:Best‑Fit 算法如何为每个字段精准分配偏移量

作为 Java 开发者,你是否思考过这样一个问题:当我们编写 obj.longField 时,JVM 是如何极快地定位到内存中那个 long 类型数据的?答案就藏在类加载的最后阶段------字段布局 。JVM 的 ClassFileParser 会像一个严谨的仓库管理员,把类的每个字段安排到对象内存中的确切位置,并记录下偏移量。而这个偏移量,最终会被 sun.misc.Unsafe 直接使用,实现近乎 C 语言指针的高效访问。

本文将带您深入 OpenJDK 源码,从 ClassFileParser::post_process_parsed_stream 出发,逐层剖析字段布局的核心算法 ------ Best‑Fit 空隙填充 ,并展示偏移量如何被编码存储,最终被 Unsafe 利用。这不仅仅是一段算法分析,更是理解 JVM 内存优化哲学的一扇窗口。


一、后处理入口:post_process_parsed_stream 的职责

ClassFileParser 完成字节流的基本解析(常量池、字段表、方法表等)后,会调用 post_process_parsed_stream 进行收尾工作。这个函数的任务包括:校验 java.lang.Object 没有实现接口、解析超类、计算接口传递闭包、排序方法、计算虚表和接口表大小,以及最重要的------字段布局

cpp

scss 复制代码
// 源码位置:hotspot/share/classfile/classFileParser.cpp
void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const stream,
                                                 ConstantPool* cp,
                                                 TRAPS) {
  // ... 省略超类解析、接口传递闭包计算等代码 ...

  // 准备字段布局信息结构
  _field_info = new FieldLayoutInfo();
  // 创建字段布局建造者,执行真正的布局算法
  FieldLayoutBuilder lb(class_name(), super_klass(), _cp, _fields,
                        _parsed_annotations->is_contended(), _field_info);
  lb.build_layout();

  // 计算引用类型(REF_NONE, REF_SOFT, REF_WEAK, REF_PHANTOM)
  _rt = (NULL ==_super_klass) ? REF_NONE : _super_klass->reference_type();
}

注意到 _parsed_annotations->is_contended() 传给了 FieldLayoutBuilder。这正是 Java 8 引入的 @Contended 注解的用武之地------当类或字段被该注解标记时,布局器会插入额外的填充(padding),避免多线程环境下的伪共享(false sharing)。


二、FieldLayoutBuilder:常规布局的总控

FieldLayoutBuilder::build_layout 简单地调用了 compute_regular_layout,这是目前 JVM 默认的字段布局策略(另一种"紧凑布局"已不再使用)。

cpp

scss 复制代码
// 源码位置:hotspot/share/classfile/fieldLayoutBuilder.cpp
void FieldLayoutBuilder::build_layout() {
  compute_regular_layout();   // 常规布局入口
}

void FieldLayoutBuilder::compute_regular_layout() {
  bool need_tail_padding = false;
  prologue();                 // 1. 初始化字段分组(primitive/oop/contended)
  regular_field_sorting();    // 2. 基本类型字段按大小降序排序

  // 3. 处理整个类被 @Contended 标记的情况
  if (_is_contended) {
    _layout->set_start(_layout->last_block());
    insert_contended_padding(_layout->start());   // 在类开头插入填充
    need_tail_padding = true;
  }

  // 4. 添加普通字段(无竞争组): 先基本类型,后引用类型
  _layout->add(_root_group->primitive_fields());
  _layout->add(_root_group->oop_fields());

  // 5. 处理每个 @Contended 字段组
  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;
    }
  }

  // 6. 尾部填充,避免竞争组后的字段意外共享缓存行
  if (need_tail_padding) {
    insert_contended_padding(_layout->last_block());
  }

  // 7. 静态字段布局(单独处理)
  _static_layout->add_contiguously(this->_static_fields->oop_fields());
  _static_layout->add(this->_static_fields->primitive_fields());

  epilogue();  // 8. 将最终偏移量写回 FieldInfo 数组
}

策略要点

  • regular_field_sorting :把 longdouble 等大字段排在同类字段的前面,以便它们优先获得良好的对齐,后续小字段更容易填补空隙。这是经典的"大块优先"分配策略。
  • @Contended 填充insert_contended_padding 会在指定位置前后插入约 64 字节的空白(可通过 -XX:ContendedPaddingWidth 调节),确保该字段独占一个缓存行。
  • 静态字段独立布局 :静态字段存储在类元数据区(不在对象实例内),布局逻辑相似但使用 add_contiguously(顺序追加,不尝试填充空隙,因为静态区通常不追求极致压缩)。

三、Best‑Fit 算法的灵魂:FieldLayout::add

FieldLayout 管理一个由 LayoutRawBlock 组成的双向链表,每个块要么是已被占用的字段块,要么是 EMPTY 空闲块。add 方法负责将一组字段块(如所有基本类型字段)插入到布局中,它实现了 Best‑Fit 空隙填充------从后向前扫描空闲块,选择能容纳当前字段的最小空闲块。

cpp

ini 复制代码
// 源码位置: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())) {
          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 :选择最小的合适空闲块,能最大程度减少碎片,避免大空隙被小字段浪费。
  • 优化记忆 :记录上一个字段的 (size, alignment) 以及搜索是否成功,若连续相同且上次失败,这次直接追加,省去无谓的链表遍历。

四、对齐与插入:insert_field_block 的精细动作

当选好一个空闲块 slot 后,insert_field_block 负责处理对齐调整,并最终将字段块嵌入。

cpp

scss 复制代码
LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawBlock* block) {
  assert(slot->kind() == LayoutRawBlock::EMPTY, "只能插入到空闲块");

  // 1. 检查对齐:若 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);       // 将填充块插入到 slot 之前(或之内,取决于实现)
  }

  // 2. 插入真正的字段块
  insert(slot, block);

  // 3. 如果原空闲块被完全用尽,则将其从链表中删除
  if (slot->size() == 0) {
    remove(slot);
  }

  // 4. 将最终计算出的偏移量写回 FieldInfo(关键!)
  FieldInfo::from_field_array(_fields, block->field_index())->set_offset(block->offset());
  return block;
}

对齐示例

假设空闲块起始偏移为 6,字段需要 8 字节对齐。adjustment = 8 - (6 % 8) = 2。JVM 会先创建一个 2 字节的空白块,使得剩下的空间起始偏移变为 8,然后再放置字段块。这样字段的 offset() 将返回 8,满足对齐要求。空白块本身仍作为空闲块留在链表中,可能被后面更小的字段利用。


五、偏移量的编码存储:FieldInfo::set_offset

每个字段的元数据(名称、类型、访问标志、偏移量等)存储在 FieldInfo 结构中,它实际是一个 Array<u2>(无符号 16 位整数数组)。为了在有限的空间内同时存储 32 位偏移量和一个标记位,JVM 使用了精巧的位打包技巧。

cpp

scss 复制代码
// 源码位置:hotspot/share/oops/fieldInfo.hpp
static FieldInfo* from_field_array(Array<u2>* fields, int index) {
  // 每个 FieldInfo 占用 field_slots 个 u2(通常是 4 个)
  return ((FieldInfo*)fields->adr_at(index * field_slots));
}

void set_offset(u4 val) {
  // 左移 FIELDINFO_TAG_SIZE 位(通常为 2),为标记位预留空间
  val = val << FIELDINFO_TAG_SIZE;
  // 低 16 位存入第一个 u2,并标记为 OFFSET_TAG
  _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 字节对齐的(对象内字段起始地址至少是 4 的倍数),最低 2 位始终为 0。JVM 利用这两个空闲比特位来存储标记(比如 FIELDINFO_TAG_OFFSET 表示这个槽位存储的是偏移量,而不是其他数据)。当需要读取偏移量时,将两个 u2 合并成 u4,右移 2 位并清除标记即可恢复真实值。这样无需额外的字段来区分数据类型,节省了内存。


六、运行时:Unsafe 如何拿到偏移量并访问

一旦偏移量被写入 FieldInfo,Java 层的 java.lang.reflect.Field 就可以通过 native 方法获取它。sun.misc.UnsafeobjectFieldOffset 正是桥梁。

java

java 复制代码
// 源码位置:jdk/src/share/classes/sun/misc/Unsafe.java(实际实现类)
public long objectFieldOffset(Field f) {
    if (f == null) throw new NullPointerException();
    return objectFieldOffset0(f);
}
private native long objectFieldOffset0(Field f);

在 JVM 内部,objectFieldOffset0 会从 Field 对象对应的 FieldInfo 中取出偏移量(右移 2 位、清除标记后返回)。获得偏移量后,UnsafegetChar(obj, offset) 等方法直接通过地址运算访问内存:

java

csharp 复制代码
public char getChar(Object obj, long offset) {
    // 底层是 JVM intrinsic 或 unsafe.cpp 中的代码,直接计算地址:obj_addr + offset
    return unsafe.getChar(obj, offset);
}

这种机制使得 Unsafe 可以像 C 语言的指针一样读写字段,绕过了 Java 的访问控制和类型检查,性能极高。JDK 内部的 AtomicLongConcurrentHashMap 等类都大量依赖 Unsafe 的字段偏移量访问。


七、设计哲学与性能考量

通过以上源码分析,我们可以总结出 JVM 字段布局的几条核心设计原则:

  1. 时间换空间,但只在类加载时:Best‑Fit 算法需要扫描链表,但类加载是低频操作,运行时的高频字段访问才是关键。因此,在加载阶段花一些 CPU 换来更紧凑的对象布局(减少内存占用、提高缓存命中率)是绝对值得的。
  2. 对齐优先,填充为辅 :基本类型字段的自然对齐是硬件高效访问的前提。JVM 宁愿插入少量填充(adjustment)也要保证字段对齐,因为非对齐访问可能导致总线事务加倍甚至异常。
  3. 并发友好@Contended 的填充机制直接融入布局器,而非通过运行时额外 padding,保证了受保护的字段在内存中真正隔离,从根本上消除伪共享。
  4. 空间紧凑性 :通过基本类型降序排列 + Best‑Fit 空隙填充,显著减少了对象内部的"气泡"。例如,一个类如果有 bytelong 字段,默认顺序可能产生大量填充,而重排后 long 在先,byte 可以填入其后的空隙中。
  5. 元数据压缩FieldInfo 中的偏移量编码利用了最低位天然为 0 的特点来存储标记,体现了 JVM 对每个字节的精打细算。

八、结语

ClassFileParserpost_process_parsed_streamFieldLayoutBuilder 的 Best‑Fit 算法,再到 Unsafe 的直接内存访问,我们完整地追踪了一个 Java 字段从字节流中的定义到运行时高速访问的全过程。理解这一过程,不仅有助于我们写出更高效的代码(比如合理排列字段顺序以减少对象大小),还能让我们在使用 Unsafe 或分析伪共享问题时做到心中有数。

JVM 的源码是一座宝库,每一个看似平凡的函数背后都可能蕴藏着深邃的算法与工程权衡。希望本文能激发你对 JVM 内部机制的兴趣,下次编写一个包含许多字段的类时,不妨想想:JVM 的布局器正在怎样安排它们在内存中的"座次"呢?

#源码

public 复制代码
        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);
  }  
  


  
相关推荐
小bo波4 小时前
Java反射机制——运行时"透视"类的秘密
java·jvm·反射·源码分析·动态代理·进阶·spring底层·框架原理
程序猿阿伟4 小时前
《拆解Chrome存储架构:浏览痕迹的残留死角与清除路径》
jvm·chrome·架构
于指尖飞舞5 小时前
java后端面试题(jvm极简)
java·开发语言·jvm
鹅城剑仙5 小时前
JVM 内存模型与 GC 调优实战指南
jvm
Javatutouhouduan5 小时前
2026年Java面试核心讲(终极版)全网首次开源!
java·jvm·java多线程·java面试·后端开发·java程序员·java八股文
程序员二叉15 小时前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
小马爱打代码20 小时前
面试题:内存模型与垃圾回收深度解析
jvm
cfm_29141 天前
JVM底层源码深度解析:读写屏障(Read/Write Barrier)
jvm
wuminyu1 天前
Java世界中StringTable源码剖析
java·linux·c语言·jvm·c++