就HotSpot VM而言吧,ClassLoader在加载题主所举得例子类时,会对class文件的fields部分做解析,参见
Array* ClassFileParser::parse_fields(Symbol* class_name,
bool is_interface,
FieldAllocationCount *fac,
u2* java_fields_count_ptr, TRAPS){
//...
// The field array starts with tuples of shorts
// [access, name index, sig index, initial value index, byte offset].
// A generic signature slot only exists for field with generic
// signature attribute. And the access flag is set with
// JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE for that field. The generic
// signature slots are at the end of the field array and after all
// other fields data.
//
// f1: [access, name index, sig index, initial value index, low_offset, high_offset]
// f2: [access, name index, sig index, initial value index, low_offset, high_offset]
// ...
// fn: [access, name index, sig index, initial value index, low_offset, high_offset]
// [generic signature index]
// [generic signature index]
// ...
//
// Allocate a temporary resource array for field data. For each field,
// a slot is reserved in the temporary array for the generic signature
// index. After parsing all fields, the data are copied to a permanent
// array and any unused slots will be discarded.
//...
}
这个函数的代码部分我直接忽略了,有兴趣可以自己去看,贴出里面的注释部分,可见经过JVM解析后fields的数组的表现形式——数组中的每个用于表示Field域的具体信息是一个tuple,里面包含了JVM规范中对Field部分的具体定义;针对field的属性的解析,请参见:
// Parse attributes for a field.
void ClassFileParser::parse_field_attributes(u2 attributes_count,
bool is_static, u2 signature_index,
u2* constantvalue_index_addr,
bool* is_synthetic_addr,
u2* generic_signature_index_addr,
ClassFileParser::FieldAnnotationCollector* parsed_annotations,
TRAPS) {//...//}
开始回答题主的问题吧,假定题主对Oop-Klass对象二分模型已经有过学习,Class文件中定义的这些Fields都会存储于该类InstanceKlass的_fields成员变量中,这是一个C++类,OpenJDK1.8后位于JVM内存空间中的MetadataSpace——所谓的元空间中;关于InstanceKlass的Layout,参见:
// InstanceKlass layout:
// [C++ vtbl pointer ] Klass
// [subtype cache ] Klass
// [instance size ] Klass
// [java mirror ] Klass
// [super ] Klass
// [access_flags ] Klass
// [name ] Klass
// [first subklass ] Klass
// [next sibling ] Klass
// [array klasses ]
// [methods ]
// [local interfaces ]
// [transitive interfaces ]
// [fields ]
// [constants ]
// [class loader ]
// [source file name ]
// [inner classes ]
// [static field size ]
// [nonstatic field size ]
// [static oop fields size ]
// [nonstatic oop maps size ]
// [has finalize method ]
// [deoptimization mark bit ]
// [initialization state ]
// [initializing thread ]
// [Java vtable length ]
// [oop map cache (stack maps) ]
// [EMBEDDED Java vtable ] size in words = vtable_len
// [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size
// The embedded nonstatic oop-map blocks are short pairs (offset, length)
// indicating where oops are located in instances of this klass.
// [EMBEDDED implementor of the interface] only exist for interface
// [EMBEDDED host klass ] only exist for an anonymous class (JSR 292 enabled)
对于题主提出的:实例变量 public int a=9
对于Non-Static类型的Field,其初始化与Static类型的Field有显著差别,对于Static类型的Member Field,经过上面的ClassParser,其初始化值由Field中元素中的initial value index——位于ConstantPool中的字面常量9的索引决定;但是对于实例变量,在new之前,它在哪里,且听下文继续分解——
假定题主的测试类形如:
public class NonStaticFieldTest {
private int i = 9;
public NonStaticFieldTest() {
}
public int getI() {
return this.i;
}
}
javap后的字节码会是这种形状:
public class NonStaticFieldTest {
private int i;
public NonStaticFieldTest();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."":()V
4: aload_0
5: bipush 9
7: putfield #12 // Field i:I
10: return
public int getI();
Code:
0: aload_0
1: getfield #12 // Field i:I
4: ireturn
}
看见那个空的构造函数中的字节码指令集了吧:
5: bipush 9
7: putfield #12 // Field i:I
总结陈词:对于实例变量,它的初始值存在于构造函数的字节码中,等待invokeSpecial指令去初始化;当然这个构造函数函数作为klass的一部分存储于元空间的方法区中。
——完