作为 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:把long、double等大字段排在同类字段的前面,以便它们优先获得良好的对齐,后续小字段更容易填补空隙。这是经典的"大块优先"分配策略。@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.Unsafe 的 objectFieldOffset 正是桥梁。
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 位、清除标记后返回)。获得偏移量后,Unsafe 的 getChar(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 内部的 AtomicLong、ConcurrentHashMap 等类都大量依赖 Unsafe 的字段偏移量访问。
七、设计哲学与性能考量
通过以上源码分析,我们可以总结出 JVM 字段布局的几条核心设计原则:
- 时间换空间,但只在类加载时:Best‑Fit 算法需要扫描链表,但类加载是低频操作,运行时的高频字段访问才是关键。因此,在加载阶段花一些 CPU 换来更紧凑的对象布局(减少内存占用、提高缓存命中率)是绝对值得的。
- 对齐优先,填充为辅 :基本类型字段的自然对齐是硬件高效访问的前提。JVM 宁愿插入少量填充(
adjustment)也要保证字段对齐,因为非对齐访问可能导致总线事务加倍甚至异常。 - 并发友好 :
@Contended的填充机制直接融入布局器,而非通过运行时额外 padding,保证了受保护的字段在内存中真正隔离,从根本上消除伪共享。 - 空间紧凑性 :通过基本类型降序排列 + Best‑Fit 空隙填充,显著减少了对象内部的"气泡"。例如,一个类如果有
byte和long字段,默认顺序可能产生大量填充,而重排后long在先,byte可以填入其后的空隙中。 - 元数据压缩 :
FieldInfo中的偏移量编码利用了最低位天然为 0 的特点来存储标记,体现了 JVM 对每个字节的精打细算。
八、结语
从 ClassFileParser 的 post_process_parsed_stream 到 FieldLayoutBuilder 的 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);
}