一、JVM中OOP-KLASS模型java
在JVM中,使用了OOP-KLASS模型來表示java對象,即:
1.jvm在加載class時,建立instanceKlass,表示其元數據,包括常量池、字段、方法等,存放在方法區;instanceKlass是jvm中的數據結構;
2.在new一個對象時,jvm建立instanceOopDesc,來表示這個對象,存放在堆區,其引用,存放在棧區;它用來表示對象的實例信息,看起來像個指針其實是藏在指針裏的對象;instanceOopDesc對應java中的對象實例;
3.HotSpot並不把instanceKlass暴露給Java,而會另外建立對應的instanceOopDesc來表示java.lang.Class對象,並將後者稱爲前者的「Java鏡像」,klass持有指向oop引用(_java_mirror即是該instanceKlass對Class對象的引用);
4.要注意,new操做返回的instanceOopDesc類型指針指向instanceKlass,而instanceKlass指向了對應的類型的Class實例的instanceOopDesc;有點繞,簡單說,就是Person實例——>Person的instanceKlass——>Person的Class。bootstrap
instanceOopDesc,只包含數據信息,它包含三部分:
1. 對象頭,也叫Mark Word,主要存儲對象運行時記錄信息,如hashcode, GC分代年齡,鎖狀態標誌,線程ID,時間戳等;
2. 元數據指針,即指向方法區的instanceKlass實例
3. 實例數據;
4. 另外,若是是數組對象,還多了一個數組長度數組
二、方法區變遷
JVM規範雖然說編譯後代碼在方法區,可是不作強制要求,具體要看JVM實現,hotspot將JIT編譯生成的代碼存放在native memory的CodeCache區域
jdk6
Klass元數據信息
每一個類的運行時常量池(字段、方法、類、接口等符號引用)、編譯後的代碼
靜態字段(不管是否有final)在instanceKlass末尾(位於PermGen內)
oop 其實就是Class對象實例
全局字符串常量池StringTable,本質上就是個Hashtable
符號引用(類型指針是SymbolKlass)數據結構
jdk7
Klass元數據信息
每一個類的運行時常量池(字段、方法、類、接口等符號引用)、編譯後的代碼
靜態字段從instanceKlass末尾移動到了java.lang.Class對象(oop)的末尾(位於普通Java heap內)
oop與全局字符串常量池移到java heap上
符號引用被移動到native heap中dom
jdk8
移除永久代:
Klass元數據信息、每一個類的運行時常量池、編譯後的代碼移到了另外一塊與堆不相連的本地內存————元空間(Metaspace)
參數控制-XX:MetaspaceSize與-XX:MaxMetaspaceSize。
關於openjdk移除永久代的相關信息:http://openjdk.java.net/jeps/122jvm
三、Class實例在堆中仍是方法區中?ide
JDK6oop
查看openjdk\hotspot\src\share\vm\classfile\classFileParser.cpp文件的instanceKlassHandle ClassFileParser::parseClassFile方法,在3522行使用oopFactory::new_instanceKlass來建立klassOop;post
klassOop至關於Java中的class,一個klassOop對象包含header、klass_field和Klassthis
instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name, Handle class_loader, Handle protection_domain, KlassHandle host_klass, GrowableArray<Handle>* cp_patches, symbolHandle& parsed_name, bool verify, TRAPS) { // 3522 // We can now create the basic klassOop for this klass klassOop ik = oopFactory::new_instanceKlass(vtable_size, itable_size, static_field_size, total_oop_map_count, rt, CHECK_(nullHandle)); instanceKlassHandle this_klass (THREAD, ik); // Create new handle outside HandleMark instanceKlassHandle this_klass (THREAD, preserve_this_klass); debug_only(this_klass->as_klassOop()->verify();) return this_klass; }
在openjdk\hotspot\src\share\vm\memory\oopFactory.cpp中找到這個方法,發現調用了instanceKlassKlass::allocate_instance_klass方法來建立klassOop
klassOop oopFactory::new_instanceKlass(Symbol* name, int vtable_len, int itable_len, int static_field_size, unsigned int nonstatic_oop_map_count, ReferenceType rt, TRAPS) { instanceKlassKlass* ikk = instanceKlassKlass::cast(Universe::instanceKlassKlassObj()); return ikk->allocate_instance_klass(name, vtable_len, itable_len, static_field_size, nonstatic_oop_map_count, rt, CHECK_NULL); }
在openjdk\hotspot\src\share\vm\oops\instanceKlassKlass.cpp中找到allocate_instance_klass方法,發現調用base_create_klass
klassOop instanceKlassKlass::allocate_instance_klass(Symbol* name, int vtable_len, int itable_len, int static_field_size, unsigned nonstatic_oop_map_count, ReferenceType rt, TRAPS) { const int nonstatic_oop_map_size = instanceKlass::nonstatic_oop_map_size(nonstatic_oop_map_count); int size = instanceKlass::object_size(align_object_offset(vtable_len) + align_object_offset(itable_len) + nonstatic_oop_map_size); // Allocation KlassHandle h_this_klass(THREAD, as_klassOop()); KlassHandle k; if (rt == REF_NONE) { if (name != vmSymbols::java_lang_Class()) { // regular klass instanceKlass o; k = base_create_klass(h_this_klass, size, o.vtbl_value(), CHECK_NULL); } else { // Class instanceMirrorKlass o; k = base_create_klass(h_this_klass, size, o.vtbl_value(), CHECK_NULL); } } else { // reference klass instanceRefKlass o; k = base_create_klass(h_this_klass, size, o.vtbl_value(), CHECK_NULL); } ..... k()->set_partially_loaded(); } return k(); }
在openjdk\hotspot\src\share\vm\oops\klass.cpp中base_create_klass方法內部直接調用base_create_klass_oop來建立klassOop
KlassHandle Klass::base_create_klass(KlassHandle& klass, int size, const Klass_vtbl& vtbl, TRAPS) { klassOop ek = base_create_klass_oop(klass, size, vtbl, THREAD); return KlassHandle(THREAD, ek); }
allocate_permanent方法默認在PermGen分配內存,instanceKlass對象保存在永久代區域
klassOop Klass::base_create_klass_oop(KlassHandle& klass, int size, const Klass_vtbl& vtbl, TRAPS) { size = align_object_size(size); // allocate and initialize vtable Klass* kl = (Klass*) vtbl.allocate_permanent(klass, size, CHECK_NULL);
// as_klassOop在klass.hpp文件中 klassOop k = kl->as_klassOop(); ......
return k; }
總結,從klassOop構建流程看出,JDK1.6中Class實例在方法區,並且和JDK7建立流程有了很大差別
JDK7
對於引用類型和數組類型對應的Class實例,openjdk\hotspot\src\share\vm\classfile\classFileParser.cpp的instanceKlassHandle ClassFileParser::parseClassFile方法的3615行經過java_lang_Class::create_mirror來建立
instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, Handle class_loader, Handle protection_domain, KlassHandle host_klass, GrowableArray<Handle>* cp_patches, TempNewSymbol& parsed_name, bool verify, TRAPS) { // 3615 // Allocate mirror and initialize static fields java_lang_Class::create_mirror(this_klass, CHECK_(nullHandle)); return this_klass; }
在openjdk\hotspot\src\share\vm\classfile\javaClasses.cpp中找到create_mirror方法,看以下發現InstanceMirrorKlass::allocate_instance建立了對象實例,並且實例不是數組時會初始化靜態字段
oop java_lang_Class::create_mirror(KlassHandle k, TRAPS) { assert(k->java_mirror() == NULL, "should only assign mirror once"); // Use this moment of initialization to cache modifier_flags also, // to support Class.getModifiers(). Instance classes recalculate // the cached flags after the class file is parsed, but before the // class is put into the system dictionary. int computed_modifiers = k->compute_modifier_flags(CHECK_0); k->set_modifier_flags(computed_modifiers); if (SystemDictionary::Class_klass_loaded() && (k->oop_is_instance() || k->oop_is_javaArray())) { // Allocate mirror (java.lang.Class instance) Handle mirror = instanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK_0); // Setup indirections mirror->obj_field_put(klass_offset, k()); k->set_java_mirror(mirror()); instanceMirrorKlass* mk = instanceMirrorKlass::cast(mirror->klass()); java_lang_Class::set_oop_size(mirror(), mk->instance_size(k)); java_lang_Class::set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror())); // It might also have a component mirror. This mirror must already exist. if (k->oop_is_javaArray()) { Handle comp_mirror; if (k->oop_is_typeArray()) { BasicType type = typeArrayKlass::cast(k->as_klassOop())->element_type(); comp_mirror = Universe::java_mirror(type); assert(comp_mirror.not_null(), "must have primitive mirror"); } else if (k->oop_is_objArray()) { klassOop element_klass = objArrayKlass::cast(k->as_klassOop())->element_klass(); if (element_klass != NULL && (Klass::cast(element_klass)->oop_is_instance() || Klass::cast(element_klass)->oop_is_javaArray())) { comp_mirror = Klass::cast(element_klass)->java_mirror(); assert(comp_mirror.not_null(), "must have element mirror"); } // else some object array internal to the VM, like systemObjArrayKlassObj } if (comp_mirror.not_null()) { // Two-way link between the array klass and its component mirror: arrayKlass::cast(k->as_klassOop())->set_component_mirror(comp_mirror()); set_array_klass(comp_mirror(), k->as_klassOop()); } } else if (k->oop_is_instance()) { // Initialize static fields instanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, CHECK_NULL); } return mirror(); } else { return NULL; } }
對於基本數據類型,是JVM內置的Class類型,openjdk\hotspot\src\share\vm\memory\universe.cpp中initialize_basic_type_mirrors方法即是初始化基本類型的類型實例的,經過調用java_lang_Class::create_basic_type_mirror
void Universe::initialize_basic_type_mirrors(TRAPS) { if (UseSharedSpaces) { assert(_int_mirror != NULL, "already loaded"); assert(_void_mirror == _mirrors[T_VOID], "consistently loaded"); } else { assert(_int_mirror==NULL, "basic type mirrors already initialized"); _int_mirror = java_lang_Class::create_basic_type_mirror("int", T_INT, CHECK); _float_mirror = java_lang_Class::create_basic_type_mirror("float", T_FLOAT, CHECK); _double_mirror = java_lang_Class::create_basic_type_mirror("double", T_DOUBLE, CHECK); _byte_mirror = java_lang_Class::create_basic_type_mirror("byte", T_BYTE, CHECK); _bool_mirror = java_lang_Class::create_basic_type_mirror("boolean",T_BOOLEAN, CHECK); _char_mirror = java_lang_Class::create_basic_type_mirror("char", T_CHAR, CHECK); _long_mirror = java_lang_Class::create_basic_type_mirror("long", T_LONG, CHECK); _short_mirror = java_lang_Class::create_basic_type_mirror("short", T_SHORT, CHECK); _void_mirror = java_lang_Class::create_basic_type_mirror("void", T_VOID, CHECK); _mirrors[T_INT] = _int_mirror; _mirrors[T_FLOAT] = _float_mirror; _mirrors[T_DOUBLE] = _double_mirror; _mirrors[T_BYTE] = _byte_mirror; _mirrors[T_BOOLEAN] = _bool_mirror; _mirrors[T_CHAR] = _char_mirror; _mirrors[T_LONG] = _long_mirror; _mirrors[T_SHORT] = _short_mirror; _mirrors[T_VOID] = _void_mirror; //_mirrors[T_OBJECT] = instanceKlass::cast(_object_klass)->java_mirror(); //_mirrors[T_ARRAY] = instanceKlass::cast(_object_klass)->java_mirror(); } }
openjdk\hotspot\src\share\vm\classfile\javaClasses.cpp中的create_basic_type_mirror方法
oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) { // This should be improved by adding a field at the Java level or by // introducing a new VM klass (see comment in ClassFileParser) oop java_class = instanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance((oop)NULL, CHECK_0); if (type != T_VOID) { klassOop aklass = Universe::typeArrayKlassObj(type); assert(aklass != NULL, "correct bootstrap"); set_array_klass(java_class, aklass); } instanceMirrorKlass* mk = instanceMirrorKlass::cast(SystemDictionary::Class_klass()); java_lang_Class::set_oop_size(java_class, mk->instance_size(oop(NULL))); java_lang_Class::set_static_oop_field_count(java_class, 0); return java_class; }
對比引用類型和基本數據類型的Class實例建立,發現都是經過instanceOop instanceMirrorKlass::allocate_instance方法;在openjdk\hotspot\src\share\vm\oops\instanceMirrorKlass.cpp中能夠找到;如下代碼就是核心了,經過對JavaObjectsInPerm
參數的判斷來決定Class實例存在方法區仍是在堆中。
instanceOop instanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) { // Query before forming handle. int size = instance_size(k); KlassHandle h_k(THREAD, as_klassOop()); instanceOop i; if (JavaObjectsInPerm) { i = (instanceOop) CollectedHeap::permanent_obj_allocate(h_k, size, CHECK_NULL); } else { assert(ScavengeRootsInCode > 0, "must be"); i = (instanceOop) CollectedHeap::obj_allocate(h_k, size, CHECK_NULL); } return i; }
接下來先看JavaObjectsInPerm爲true的狀況,CollectedHeap::permanent_obj_allocate方法,在openjdk\hotspot\src\share\vm\gc_interface\collectedHeap.inline.hpp中;
oop CollectedHeap::permanent_obj_allocate(KlassHandle klass, int size, TRAPS) { oop obj = permanent_obj_allocate_no_klass_install(klass, size, CHECK_NULL); post_allocation_install_obj_klass(klass, obj, size); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value((HeapWord*) obj, size)); return obj; } oop CollectedHeap::permanent_obj_allocate_no_klass_install(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_permanent_mem_allocate_init(size, CHECK_NULL); post_allocation_setup_no_klass_install(klass, obj, size); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; }
最終調用的是common_permanent_mem_allocate_init方法中的common_permanent_mem_allocate_noinit方法來建立實例
HeapWord* CollectedHeap::common_permanent_mem_allocate_init(size_t size, TRAPS) {
// 內存空間的分配
HeapWord* obj = common_permanent_mem_allocate_noinit(size, CHECK_NULL); // 對象的初始化
init_obj(obj, size); return obj; }
從common_permanent_mem_allocate_noinit方法的名字,和內存溢出的異常"PermGen space"能夠看出最後建立oop實例是在方法區中
// Need to investigate, do we really want to throw OOM exception here?
HeapWord* CollectedHeap::common_permanent_mem_allocate_noinit(size_t size, TRAPS) {
if (HAS_PENDING_EXCEPTION) { NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending")); return NULL; // caller does a CHECK_NULL too } #ifdef ASSERT if (CIFireOOMAt > 0 && THREAD->is_Compiler_thread() && ++_fire_out_of_memory_count >= CIFireOOMAt) { // For testing of OOM handling in the CI throw an OOM and see how // it does. Historically improper handling of these has resulted // in crashes which we really don't want to have in the CI. THROW_OOP_0(Universe::out_of_memory_error_perm_gen()); } #endif HeapWord* result = Universe::heap()->permanent_mem_allocate(size); if (result != NULL) { NOT_PRODUCT(Universe::heap()-> check_for_non_bad_heap_word_value(result, size)); assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); return result; } // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory("PermGen space"); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR, "PermGen space"); } THROW_OOP_0(Universe::out_of_memory_error_perm_gen()); }
再看JavaObjectsInPerm參數爲false的狀況,仍然是在collectedHeap.inline.hpp文件中,發現調用了common_mem_allocate_init,接着又調用了common_mem_allocate_noinit
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; }
HeapWord* CollectedHeap::common_mem_allocate_init(size_t size, bool is_noref, TRAPS) {
// 內存空間的分配 HeapWord* obj = common_mem_allocate_noinit(size, is_noref, CHECK_NULL);
// 對象的初始化 init_obj(obj, size); return obj; }
從common_mem_allocate_noinit方法的名字,和內存溢出的異常"Java heap space"能夠建立oop實例是在java heap中
HeapWord* CollectedHeap::common_mem_allocate_noinit(size_t size, bool is_noref, TRAPS) { // Clear unhandled oops for memory allocation. Memory allocation might // not take out a lock if from tlab, so clear here. CHECK_UNHANDLED_OOPS_ONLY(THREAD->clear_unhandled_oops();) if (HAS_PENDING_EXCEPTION) { NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending")); return NULL; // caller does a CHECK_0 too } // We may want to update this, is_noref objects might not be allocated in TLABs. HeapWord* result = NULL; if (UseTLAB) { result = CollectedHeap::allocate_from_tlab(THREAD, size); if (result != NULL) { assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); return result; } } bool gc_overhead_limit_was_exceeded = false; result = Universe::heap()->mem_allocate(size, is_noref, false, &gc_overhead_limit_was_exceeded); if (result != NULL) { NOT_PRODUCT(Universe::heap()-> check_for_non_bad_heap_word_value(result, size)); assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); THREAD->incr_allocated_bytes(size * HeapWordSize); return result; } if (!gc_overhead_limit_was_exceeded) { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory("Java heap space"); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, "Java heap space"); } THROW_OOP_0(Universe::out_of_memory_error_java_heap()); } else { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory("GC overhead limit exceeded"); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, "GC overhead limit exceeded"); } THROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit()); } }
最後查看openjdk\hotspot\src\share\vm\runtime\globals.hpp文件,發現JavaObjectsInPerm的值是定義爲false的
develop(bool, JavaObjectsInPerm, false, \ "controls whether Classes and interned Strings are allocated" \ "in perm. This purely intended to allow debugging issues" \ "in production.")
總結:JDK7建立Class實例存在堆中;由於JDK7中JavaObjectsInPerm參數值固定爲false。
JDK8
JDK8和JDK7建立Class實例的代碼大同小異,從openjdk\hotspot\src\share\vm\oops\instanceMirrorKlass.cpp中的instanceOop InstanceMirrorKlass::allocate_instance方法開始纔有了區別;
不在判斷JavaObjectsInPerm參數,由於jdk8已經去除了永久代,取而代之的值MetaSpace。這裏直接調用CollectedHeap::Class_obj_allocate
instanceOop InstanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) { // Query before forming handle. int size = instance_size(k); KlassHandle h_k(THREAD, this); instanceOop i = (instanceOop) CollectedHeap::Class_obj_allocate(h_k, size, k, CHECK_NULL); return i; }
openjdk\hotspot\src\share\vm\gc_interface\collectedHeap.cpp中找到CollectedHeap::Class_obj_allocate
oop CollectedHeap::Class_obj_allocate(KlassHandle klass, int size, KlassHandle real_klass, TRAPS) { HeapWord* obj; obj = common_mem_allocate_init(real_klass, size, CHECK_NULL); oop mirror = (oop)obj; java_lang_Class::set_oop_size(mirror, size); // Setup indirections if (!real_klass.is_null()) { java_lang_Class::set_klass(mirror, real_klass()); real_klass->set_java_mirror(mirror); } InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); assert(size == mk->instance_size(real_klass), "should have been set"); // notify jvmti and dtrace post_allocation_notify(klass, (oop)obj); return mirror; }
在openjdk\hotspot\src\share\vm\gc_interface\collectedHeap.inline.hpp中找到common_mem_allocate_init發現調用了common_mem_allocate_noinit
HeapWord* CollectedHeap::common_mem_allocate_init(KlassHandle klass, size_t size, TRAPS) { HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL); init_obj(obj, size); return obj; }
內存溢出的異常"Java heap space"能夠看出JDK8建立的Class實例最終在堆中
HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) { // Clear unhandled oops for memory allocation. Memory allocation might // not take out a lock if from tlab, so clear here. CHECK_UNHANDLED_OOPS_ONLY(THREAD->clear_unhandled_oops();) if (HAS_PENDING_EXCEPTION) { NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending")); return NULL; // caller does a CHECK_0 too } HeapWord* result = NULL; if (UseTLAB) { result = allocate_from_tlab(klass, THREAD, size); if (result != NULL) { assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); return result; } } bool gc_overhead_limit_was_exceeded = false; result = Universe::heap()->mem_allocate(size, &gc_overhead_limit_was_exceeded); if (result != NULL) { NOT_PRODUCT(Universe::heap()-> check_for_non_bad_heap_word_value(result, size)); assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); THREAD->incr_allocated_bytes(size * HeapWordSize); AllocTracer::send_allocation_outside_tlab_event(klass, size * HeapWordSize); return result; } if (!gc_overhead_limit_was_exceeded) { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory("Java heap space"); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, "Java heap space"); } THROW_OOP_0(Universe::out_of_memory_error_java_heap()); } else { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory("GC overhead limit exceeded"); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, "GC overhead limit exceeded"); } THROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit()); } }
總結:JDK8移除了永久代,轉而使用元空間來實現方法區,建立的Class實例在java heap中