JVM 是如何实现 Java 语言的 对象系统呢?
使用 klass-oop 对象模型
深入探究JVM | klass-oop对象模型研究
HotSpotVM 对象机制实现浅析
CompressedOops
Strongtalk oops
JVM 使用 oop-klass 模型来实现 java 的对象模型。
整个 oop-klass 模型 类层次的定义在 hotspot\src\share\vm\oops\oopsHierarchy.hpp 文件中
oopsHierarchy
oop
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef class oopDesc * oop ;typedef class instanceOopDesc * instanceOop ;typedef class methodOopDesc * methodOop ;typedef class constMethodOopDesc * constMethodOop ;typedef class methodDataOopDesc * methodDataOop ;typedef class arrayOopDesc * arrayOop ;typedef class objArrayOopDesc * objArrayOop ;typedef class typeArrayOopDesc * typeArrayOop ;typedef class constantPoolOopDesc * constantPoolOop ;typedef class constantPoolCacheOopDesc * constantPoolCacheOop ;typedef class klassOopDesc * klassOop ;typedef class markOopDesc * markOop ;typedef class compiledICHolderOopDesc * compiledICHolderOop ;
klass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Klass ;class instanceKlass ;class instanceMirrorKlass ;class instanceRefKlass ;class methodKlass ;class constMethodKlass ;class methodDataKlass ;class klassKlass ;class instanceKlassKlass ;class arrayKlassKlass ;class objArrayKlassKlass ;class typeArrayKlassKlass ;class arrayKlass ;class objArrayKlass ;class typeArrayKlass ;class constantPoolKlass ;class constantPoolCacheKlass ;class compiledICHolderKlass ;
oop的声明 hotspot\src\share\vm\oops\oop.hpp,其实现文件在 oop.cpp 和 oop.inline.hpp 文件中。
klass的声明 hotspot\src\share\vm\oops\klass.hpp,其实现文件 klass.cpp 中。
oop中实例数据的存取:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 inline void * oopDesc::field_base(int offset) const { return (void *)&((char *)this )[offset]; }inline jint* oopDesc::int_field_addr(int offset) const { return (jint*) field_base(offset); }inline jint oopDesc::int_field(int offset) const { return *int_field_addr(offset); }inline void oopDesc::int_field_put(int offset, jint contents) { *int_field_addr(offset) = contents; }inline jint oopDesc::int_field_acquire(int offset) const { return OrderAccess::load_acquire(int_field_addr(offset)); }inline void oopDesc::release_int_field_put(int offset, jint contents) { OrderAccess::release_store(int_field_addr(offset), contents); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 oop ---> +-----------+ ---} | _mark | | +-----------+ |---> header | _metadata | | {--- +-----------+ ---} | | 0x238AB801|---> +-----------+ | +-----------+ | field1 | | | ... | +-----------+ | +-----------+ data<---| | ... | | +-----------+ | | 0x338AB801|---> +-----------+ | +-----------+ | fieldN | | | ... | +-----------+ {--- +-----------+
可以看到所有的 Java 对象的字段,最终是以字段的地址的列表存储在 oop 中,然后通过 offset (可以认为是 索引)获得字段所在的地址。然后使用该地址来访问对象的字段。这个 offset 应该和 字段在java 文件中的声明顺序相对应。
现在,比较有趣的就是如何针对不同的 java 类,分配不同的内存,因为不同的 java 类,拥有的字段也不同。所以 data 区域的大小自然不同。
1 2 3 4 5 6 7 8 class C1 { private int i; } class C2 { private int i; private double d; }
当执行:1 2 C1 c1 = new C1(); C2 c2 = new C2();
c1 所对应的 oop 该如何分配内存呢?下面是猜测实现:
c2 和 c1 明显所占内存不同。其实也简单。当 C1 和 C2 被载入 JVM 的时候,会创建其所对应的 klass 类的对应,这个对象就是用来描述 C1 和 C2 这种 java 类的,自然这个类当然知道,C1 和 C2 内部所含有字段个数,以及所需要的内存大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 oop c1Oop; oop c2Oop; Klass c1Klass; Klass c2Klass; void * c1ptr = malloc (c1Klass.size());c1Oop = (opp)c1ptr; void * c2ptr = malloc (c2Klass.size());c2Oop = (oop)c2ptr;
通过强制类型转换,解决了两个问题:
不同的 JAVA 类大小不同,所以对应的 oop 大小也应该不同,需要动态分配
保证的类型安全
下面来验证上面的推理: oops/instanceKlass.cpp instanceKlass 是 instanceOop 类相对应。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 instanceOop instanceKlass::allocate_instance(TRAPS) { assert(!oop_is_instanceMirror(), "wrong allocation path" ); bool has_finalizer_flag = has_finalizer(); int size = size_helper(); KlassHandle h_k (THREAD, as_klassOop()) ; instanceOop i; i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL); if (has_finalizer_flag && !RegisterFinalizersAtInit) { i = register_finalizer(i, CHECK_NULL); } return i; }
可以看到这个方法使用 CollectedHeap::obj_allocate 来分配 oop 对象。
1 2 3 4 5 6 7 8 9 10 11 oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) { debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed" ); assert(size >= 0 , "int won't convert to size_t" ); HeapWord* obj = common_mem_allocate_init(size, false , CHECK_NULL); post_allocation_setup_obj(klass, obj, size); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 JVM_ENTRY(jobject, JVM_NewInstance(JNIEnv *env, jclass cls)) JVMWrapper("JVM_NewInstance" ); Handle mirror (THREAD, JNIHandles::resolve_non_null(cls)) ; methodOop resolved_constructor = java_lang_Class::resolved_constructor(mirror()); if (resolved_constructor == NULL ) { klassOop k = java_lang_Class::as_klassOop(mirror()); if (java_lang_Class::is_primitive(mirror())) { const char * msg = "" ; if (mirror == Universe::bool_mirror()) msg = "java/lang/Boolean" ; else if (mirror == Universe::char_mirror()) msg = "java/lang/Character" ; else if (mirror == Universe::float_mirror()) msg = "java/lang/Float" ; else if (mirror == Universe::double_mirror()) msg = "java/lang/Double" ; else if (mirror == Universe::byte_mirror()) msg = "java/lang/Byte" ; else if (mirror == Universe::short_mirror()) msg = "java/lang/Short" ; else if (mirror == Universe::int_mirror()) msg = "java/lang/Integer" ; else if (mirror == Universe::long_mirror()) msg = "java/lang/Long" ; THROW_MSG_0(vmSymbols::java_lang_NullPointerException(), msg); } Klass::cast(k)->check_valid_for_instantiation(false , CHECK_NULL); instanceKlassHandle klass (THREAD, k) ; klass->initialize(CHECK_NULL); resolved_constructor = klass->find_method(vmSymbols::object_initializer_name(), vmSymbols::void_method_signature()); if (resolved_constructor == NULL ) { ResourceMark rm (THREAD) ; THROW_MSG_0(vmSymbols::java_lang_InstantiationException(), klass->external_name()); } java_lang_Class::set_resolved_constructor(mirror(), resolved_constructor); } assert(resolved_constructor != NULL , "sanity check" ); methodHandle constructor = methodHandle(THREAD, resolved_constructor); instanceKlassHandle klass (THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls))) ; assert(klass->is_initialized() || klass->is_being_initialized(), "sanity check" ); klassOop caller_klass = NULL ; if (UsePrivilegedStack) { caller_klass = thread->security_get_caller_class(2 ); if (!Reflection::verify_class_access(caller_klass, klass(), false ) || !Reflection::verify_field_access(caller_klass, klass(), klass(), constructor->access_flags(), false , true )) { ResourceMark rm (THREAD) ; THROW_MSG_0(vmSymbols::java_lang_IllegalAccessException(), klass->external_name()); } } Handle receiver = klass->allocate_instance_handle(CHECK_NULL); JavaCalls::call_default_constructor(thread, constructor, receiver, CHECK_NULL); jobject res = JNIHandles::make_local(env, receiver()); if (JvmtiExport::should_post_vm_object_alloc()) { JvmtiExport::post_vm_object_alloc(JavaThread::current(), receiver()); } return res; JVM_END JVM_ENTRY(jobject, JVM_NewInstance(JNIEnv *env, jclass cls)) JVMWrapper("JVM_NewInstance" ); Handle mirror (THREAD, JNIHandles::resolve_non_null(cls)) ; methodOop resolved_constructor = java_lang_Class::resolved_constructor(mirror()); methodHandle constructor = methodHandle(THREAD, resolved_constructor); instanceKlassHandle klass (THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(cls))) ; Handle receiver = klass->allocate_instance_handle(CHECK_NULL); JavaCalls::call_default_constructor(thread, constructor, receiver, CHECK_NULL); jobject res = JNIHandles::make_local(env, receiver()); return res; JVM_END
JVM_NewInstance 使用 klass->allocate_instance_handle 来分配内存,其实现代码如下:
1 2 3 instanceHandle allocate_instance_handle (TRAPS) { return instanceHandle(THREAD, allocate_instance(THREAD)); }
对象分配 使用 java 语言的 new 关键字分配对象的实现过程:
参考:<<HotSpot实战>> 3.3 创建对象
hotspot\src\share\vm\interpreter\interpreterRuntime.cpp InterpreterRuntime::_new
interpreterRuntime.cpp中有很多 java 关键实现,
例如:monitorenter 指令的实现,InterpreterRuntime::monitorenter
monitorexit 指令:InterpreterRuntime::monitorexit
java创建对象的方式
new 关键字的实现,参考: hotspot\src\share\vm\interpreter\interpreterRuntime.cpp InterpreterRuntime::_new
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 IRT_ENTRY(void , InterpreterRuntime::_new(JavaThread* thread, constantPoolOopDesc* pool, int index)) klassOop k_oop = pool->klass_at(index, CHECK); instanceKlassHandle klass (THREAD, k_oop) ; klass->check_valid_for_instantiation(true , CHECK); klass->initialize(CHECK); oop obj = klass->allocate_instance(CHECK); thread->set_vm_result(obj); IRT_END
调用 klass->allocate_instance(CHECK); 来创建对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 JVM_ENTRY(jobject, JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjectArray args0)) JVMWrapper("JVM_NewInstanceFromConstructor" ); oop constructor_mirror = JNIHandles::resolve(c); objArrayHandle args (THREAD, objArrayOop(JNIHandles::resolve(args0))) ; oop result = Reflection::invoke_constructor(constructor_mirror, args, CHECK_NULL); jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) { JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } return res; JVM_END oop Reflection::invoke_constructor(oop constructor_mirror, objArrayHandle args, TRAPS) { oop mirror = java_lang_reflect_Constructor::clazz(constructor_mirror); int slot = java_lang_reflect_Constructor::slot(constructor_mirror); bool override = java_lang_reflect_Constructor::override(constructor_mirror) != 0 ; objArrayHandle ptypes (THREAD, objArrayOop(java_lang_reflect_Constructor::parameter_types(constructor_mirror))) ; instanceKlassHandle klass (THREAD, java_lang_Class::as_klassOop(mirror)) ; methodOop m = klass->method_with_idnum(slot); if (m == NULL ) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke" ); } methodHandle method (THREAD, m) ; assert(method->name() == vmSymbols::object_initializer_name(), "invalid constructor" ); klass->initialize(CHECK_NULL); klass->check_valid_for_instantiation(false , CHECK_NULL); Handle receiver = klass->allocate_instance_handle(CHECK_NULL); invoke(klass, method, receiver, override, ptypes, T_VOID, args, false , CHECK_NULL); return receiver(); }
调用 klass->allocate_instance_handle 创建新对象实例。 allocate_instance_handle 内部使用 allocate_instance 来创建对象。
所以,对于一个 java 对象,最终是由其所对应的类型的 Klass 的 allocate_instance 方法来完成内存分配的。
$. 参考
Dangerous Code: How to be Unsafe with Java Classes & Objects in Memory
Getting Started with HotSpot and OpenJDK
源码分析:Java对象的内存分配
HotSpotVM 对象机制实现浅析