JVM內存池

JVM內存池

根據jconsole工具提供,內存池大致可分爲


  •  年輕代(New area)
       survivor space
       eden space
     老年代(old Gen 或者 tenuered area)
  • 非堆
     元數據(Metaspace)
      compressed Class space
      其它元數據
     代碼緩存塊(codeCache)

結合《內存概述與JAVA進程內存》,進一步得出
在這裏插入圖片描述在這裏插入圖片描述
注:其它內存都可以算到c heap中

元空間和類指針壓縮空間的區別

  1. 類指針壓縮空間只包含類的元數據,比如InstanceKlass, ArrayKlass
    僅當打開了UseCompressedClassPointers選項才生效
    爲了提高性能,Java中的虛方法表也存放到這裏
    這裏到底存放哪些元數據的類型,目前仍在減少
  2. 元空間包含類的其它比較大的元數據,比如方法,字節碼,常量池等。
    InstanceKlass, ArrayKlass只是一個框架對象,保存關係,大多都通過指針向堆或元空間關聯

初始化

ContinueInNewThread0()《openJdk的啓動流程》中的方法->
  pthread_attr_setstacksize() #設置線程棧大小

Threads::create_vm()《openJdk的啓動流程》中的方法->
  init_globals()–>
  CodeCache::codeCache_init() #初始化code cache
  Universe::universe_init() #初始化堆 -->
   Metaspace::global_initialize() #初始化元數據空間

CodeCache

用於緩存不同類型的生成的彙編代碼,如熱點方法編譯後的代碼,各種運行時的調用入口Stub等,所有的彙編代碼在CodeCache中都是以CodeBlob及其子類的形式存在的。通常CodeBlob會對應一個CodeBuffer,負責生成彙編代碼的生成器會通過CodeBuffer將彙編代碼寫入到CodeBlob中。位於hotspot/src/share/vm/code/codeCache.hpp。

class CodeCache : AllStatic {
    //CodeHeap就是實際管理彙編代碼內存分配的實現
    //CodeCache只是CodeHeap的一層包裝而已,核心實現都在CodeHeap中
    //位於hotspot/src/share/vm/memory/heap.hpp。
    static CodeHeap * _heap;
    
	void codeCache_init() {
  		CodeCache::initialize();
	}
	
	void CodeCache::initialize() {
	//校驗參數配置是否正確,CodeCacheSegmentSize表示CodeCache對應內存的最小值,CodeEntryAlignment表示分配給一段彙編代碼的最小內存空間
	  assert(CodeCacheSegmentSize >= (uintx)CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points");
	#ifdef COMPILER2
	//OptoLoopAlignment表示給內部循環代碼分配的最低內存大小
	  assert(CodeCacheSegmentSize >= (uintx)OptoLoopAlignment,  "CodeCacheSegmentSize must be large enough to align inner loops");
	#endif
	  assert(CodeCacheSegmentSize >= sizeof(jdouble),    "CodeCacheSegmentSize must be large enough to align constants");
	  
	  //按照系統的內存頁大小對CodeCache的參數取整
	  //CodeCacheExpansionSize表示CodeCache擴展一次內存空間對應的內存大小,x86下默認值是2304k
	  CodeCacheExpansionSize = round_to(CodeCacheExpansionSize, os::vm_page_size());
	  //InitialCodeCacheSize表示CodeCache的初始大小,x86 啓用C2編譯下,默認值是2304k
	  InitialCodeCacheSize = round_to(InitialCodeCacheSize, os::vm_page_size());
	  //ReservedCodeCacheSize表示CodeCache的最大內存大小,x86 啓用C2編譯下,默認值是48M
	  ReservedCodeCacheSize = round_to(ReservedCodeCacheSize, os::vm_page_size());
	  //CodeHeap根據參數申請內存空間
	  if (!_heap->reserve(ReservedCodeCacheSize, InitialCodeCacheSize, CodeCacheSegmentSize)) {
	    vm_exit_during_initialization("Could not reserve enough space for code cache");
	  }
	  //將CodeHeap放入一個MemoryPool中管理起來
	  MemoryService::add_code_heap_memory_pool(_heap);
	 
	  //初始化用於刷新CPU指令緩存的Icache,即生成一段用於刷新指令緩存的彙編代碼,此時因爲heap屬性已初始化完成,所以可以從CodeCache中分配Blob了
	  icache_init();
	 
	  //通知操作系統我們的CodeCache的內存區域,主要是win64使用
	  os::register_code_area(_heap->low_boundary(), _heap->high_boundary());
	}
}

Universe

用來保存JVM中重要的系統類及其實例,如基礎類型數組的Klass,用來提供對分配Java對象的CollectedHeap的訪問入口,提供多種對象分配的方法.位於hotspot src/shared/vm/memory/universe.hpp。

class Universe: AllStatic {

	//負責Java對象分配的CollectedHeap引用
	//不堆的垃圾收集器對應其不同的子類
  	static CollectedHeap* _collectedHeap;
  
	jint universe_init() {
	  assert(!Universe::_fully_initialized, "called after initialize_vtables");
	  //校驗參數的合法
	  guarantee(1 << LogHeapWordSize == sizeof(HeapWord),
	         "LogHeapWordSize is incorrect.");
	  //sizeof(oop)和sizeof(HeapWord)實際都是一個指針的大小
	  guarantee(sizeof(oop) >= sizeof(HeapWord), "HeapWord larger than oop?");
	  guarantee(sizeof(oop) % sizeof(HeapWord) == 0,
	            "oop size is not not a multiple of HeapWord size");
	  TraceTime timer("Genesis", TraceStartupTime);
	  //計算部分重要的系統類的關鍵屬性在oop中的偏移量,方便快速根據內存偏移讀取屬性值
	  JavaClasses::compute_hard_coded_offsets();
	  //初始化collectedHeap和TLABA
	  //1.通過選用不同的垃圾收集器創建不同的_collectedHeap子類,並保存引用
	  //2.在子類中,通過讀取Arguments及globals解析出的vm參數,反調Universe::reserve_heap
	  //3.根據不同的垃圾收集器,將堆分配用不同的類型,年輕代,老年代等
	  jint status = Universe::initialize_heap();
	  if (status != JNI_OK) {
	    return status;
	  }
	  //初始化負責元空間內存管理的Metaspace
	  Metaspace::global_initialize();
	 
	  // 初始化ClassLoaderData的_the_null_class_loader_data屬性
	  ClassLoaderData::init_null_class_loader_data();
	 
	  // 初始化屬性
	  Universe::_finalizer_register_cache = new LatestMethodCache();
	  Universe::_loader_addClass_cache    = new LatestMethodCache();
	  Universe::_pd_implies_cache         = new LatestMethodCache();
	  Universe::_throw_illegal_access_error_cache = new LatestMethodCache();
	 
	  //UseSharedSpaces默認爲true,表示爲元數據使用共享空間
	  if (UseSharedSpaces) {
	    //初始化共享空間
	    MetaspaceShared::initialize_shared_spaces();
	    StringTable::create_table();
	  } else {
	    //不使用共享空間時,分別初始化各個組件,SymbolTable表示符號表,StringTable表示字符串表
	    SymbolTable::create_table();
	    StringTable::create_table();
	    ClassLoader::create_package_info_table();
	 
	    if (DumpSharedSpaces) {
	      MetaspaceShared::prepare_for_dumping();
	    }
	  }
	  if (strlen(VerifySubSet) > 0) {
	    Universe::initialize_verify_flags();
	  }
	 
	  return JNI_OK;
	}
	 
	ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {
	  //校驗參數
	  assert(alignment <= Arguments::conservative_max_heap_alignment(),
	      err_msg("actual alignment " SIZE_FORMAT " must be within maximum heap alignment " SIZE_FORMAT,
	          alignment, Arguments::conservative_max_heap_alignment()));
	  //heap_size取整
	  size_t total_reserved = align_size_up(heap_size, alignment);
	  //使用指針壓縮時堆空間不能超過OopEncodingHeapMax,是計算出來的,32G
	  assert(!UseCompressedOops || (total_reserved <= (OopEncodingHeapMax - os::vm_page_size())),
	      "heap size is too big for compressed oops");
	  
	  //是否使用大內存頁,UseLargePages默認是false
	  bool use_large_pages = UseLargePages && is_size_aligned(alignment, os::large_page_size());
	  assert(!UseLargePages
	      || UseParallelGC
	      || use_large_pages, "Wrong alignment to use large pages");
	   //計算Java堆的基地址,堆內存起始地址
	  char* addr = Universe::preferred_heap_base(total_reserved, alignment, Universe::UnscaledNarrowOop);
	  //在執行構造方法的時候會向操作系統申請一段連續的內存空間
	  ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages, addr);
	 
	  if (UseCompressedOops) {
	     //如果申請失敗,即該地址已經被分配了,則重試重新申請,每次重試時使用的NARROW_OOP_MODE不同
	    if (addr != NULL && !total_rs.is_reserved()) {
	      addr = Universe::preferred_heap_base(total_reserved, alignment, Universe::ZeroBasedNarrowOop);
	 
	      ReservedHeapSpace total_rs0(total_reserved, alignment,
	          use_large_pages, addr);
	 
	      if (addr != NULL && !total_rs0.is_reserved()) {
	        //繼續重試
	        addr = Universe::preferred_heap_base(total_reserved, alignment, Universe::HeapBasedNarrowOop);
	        assert(addr == NULL, "");
	 
	        ReservedHeapSpace total_rs1(total_reserved, alignment,
	            use_large_pages, addr);
	        total_rs = total_rs1;
	      } else {
	        total_rs = total_rs0;
	      }
	    }
	  }
	 
	  //重試依然失敗,拋出異常
	  if (!total_rs.is_reserved()) {
	    vm_exit_during_initialization(err_msg("Could not reserve enough space for " SIZE_FORMAT "KB object heap", total_reserved/K));
	    return total_rs;
	  }
	 
	  if (UseCompressedOops) {
	    //設置壓縮指針的基地址
	    address base = (address)(total_rs.base() - os::vm_page_size());
	    Universe::set_narrow_oop_base(base);
	  }
	  return total_rs;
	}
}

Metaspace

Metaspace表示用來給Klass等元數據分配內存的一個內存空間,稱爲元空間,每個ClassLoader實例包括啓動類加載器都會創建一個對應的Metaspace實例,每個Metaspace實例都有一個SpaceManager實例,通過SpaceManager完成內存分配與管理,位於hotspot/src/share/vm/memory/metaspace.hpp

class Metaspace : public CHeapObj<mtClass> {
	 
	void Metaspace::global_initialize() {
	  MetaspaceGC::initialize();
	 
	  // Initialize the alignment for shared spaces.
	  int max_alignment = os::vm_allocation_granularity();
	  size_t cds_total = 0;
	 
	  MetaspaceShared::set_max_alignment(max_alignment);
	  //DumpSharedSpaces默認爲false
	  if (DumpSharedSpaces) {
	#if INCLUDE_CDS
	    MetaspaceShared::estimate_regions_size();
	 
	    SharedReadOnlySize  = align_size_up(SharedReadOnlySize,  max_alignment);
	    SharedReadWriteSize = align_size_up(SharedReadWriteSize, max_alignment);
	    SharedMiscDataSize  = align_size_up(SharedMiscDataSize,  max_alignment);
	    SharedMiscCodeSize  = align_size_up(SharedMiscCodeSize,  max_alignment);
	 
	    // the min_misc_code_size estimate is based on MetaspaceShared::generate_vtable_methods()
	    uintx min_misc_code_size = align_size_up(
	      (MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size) *
	        (sizeof(void*) + MetaspaceShared::vtbl_method_size) + MetaspaceShared::vtbl_common_code_size,
	          max_alignment);
	 
	    if (SharedMiscCodeSize < min_misc_code_size) {
	      report_out_of_shared_space(SharedMiscCode);
	    }
	 
	    cds_total = FileMapInfo::shared_spaces_size();
	    cds_total = align_size_up(cds_total, _reserve_alignment);
	    _space_list = new VirtualSpaceList(cds_total/wordSize);
	    _chunk_manager_metadata = new ChunkManager(SpecializedChunk, SmallChunk, MediumChunk);
	 
	    if (!_space_list->initialization_succeeded()) {
	      vm_exit_during_initialization("Unable to dump shared archive.", NULL);
	    }
	 
	#ifdef _LP64
	    if (cds_total + compressed_class_space_size() > UnscaledClassSpaceMax) {
	      vm_exit_during_initialization("Unable to dump shared archive.",
	          err_msg("Size of archive (" SIZE_FORMAT ") + compressed class space ("
	                  SIZE_FORMAT ") == total (" SIZE_FORMAT ") is larger than compressed "
	                  "klass limit: " SIZE_FORMAT, cds_total, compressed_class_space_size(),
	                  cds_total + compressed_class_space_size(), UnscaledClassSpaceMax));
	    }
	 
	    assert(UseCompressedOops && UseCompressedClassPointers,
	      "UseCompressedOops and UseCompressedClassPointers must be set");
	    Universe::set_narrow_klass_base((address)_space_list->current_virtual_space()->bottom());
	    if (TraceMetavirtualspaceAllocation && Verbose) {
	      gclog_or_tty->print_cr("Setting_narrow_klass_base to Address: " PTR_FORMAT,
	                             _space_list->current_virtual_space()->bottom());
	    }
	 
	    Universe::set_narrow_klass_shift(0);
	#endif // _LP64
	#endif // INCLUDE_CDS
	  } else {
	#if INCLUDE_CDS
	    // If using shared space, open the file that contains the shared space
	    // and map in the memory before initializing the rest of metaspace (so
	    // the addresses don't conflict)
	    address cds_address = NULL;
	    //UseSharedSpaces默認是false
	    if (UseSharedSpaces) {
	      FileMapInfo* mapinfo = new FileMapInfo();
	      if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) {
	        cds_total = FileMapInfo::shared_spaces_size();
	        cds_address = (address)mapinfo->region_base(0);
	      } else {
	        assert(!mapinfo->is_open() && !UseSharedSpaces,
	               "archive file not closed or shared spaces not disabled.");
	      }
	    }
	#endif // INCLUDE_CDS
	#ifdef _LP64
	     //以堆內存的終止地址作爲起始地址申請內存,避免堆內存與Metaspace的內存地址衝突
	    char* base = (char*)align_ptr_up(Universe::heap()->reserved_region().end(), _reserve_alignment);
	    //申請元數據空間
	    allocate_metaspace_compressed_klass_ptrs(base, 0);
	#endif // _LP64
	 
	    //計算_first_chunk_word_size和_first_class_chunk_word_size
	    _first_chunk_word_size = InitialBootClassLoaderMetaspaceSize / BytesPerWord;
	    _first_chunk_word_size = align_word_size_up(_first_chunk_word_size);
	    _first_class_chunk_word_size = MIN2((size_t)MediumChunk*6,
	                                       (CompressedClassSpaceSize/BytesPerWord)*2);
	    _first_class_chunk_word_size = align_word_size_up(_first_class_chunk_word_size);
	 
	    //VIRTUALSPACEMULTIPLIER的取值是2,初始化_space_list和_chunk_manager_metadata
	    size_t word_size = VIRTUALSPACEMULTIPLIER * _first_chunk_word_size;
	    word_size = align_size_up(word_size, Metaspace::reserve_alignment_words());
	    _space_list = new VirtualSpaceList(word_size);
	    _chunk_manager_metadata = new ChunkManager(SpecializedChunk, SmallChunk, MediumChunk);
	    
	    //_space_list初始化失敗
	    if (!_space_list->initialization_succeeded()) {
	      vm_exit_during_initialization("Unable to setup metadata virtual space list.", NULL);
	    }
	  }
	 
	  _tracer = new MetaspaceTracer();
	}

	void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, address cds_base) {
	  //校驗參數
	  assert(using_class_space(), "called improperly");
	  assert(UseCompressedClassPointers, "Only use with CompressedKlassPtrs");
	  assert(compressed_class_space_size() < KlassEncodingMetaspaceMax,
	         "Metaspace size is too big");
	  assert_is_ptr_aligned(requested_addr, _reserve_alignment);
	  assert_is_ptr_aligned(cds_base, _reserve_alignment);
	  assert_is_size_aligned(compressed_class_space_size(), _reserve_alignment);
	 
	  //嘗試在指定起始地址處申請一段連續的內存空間
	  bool large_pages = false;
	  //如果開啓了指針壓縮,則CompressedClassSpace分配在MaxMetaspaceSize裏頭,即MaxMetaspaceSize=Compressed Class Space Size + Metaspace area (excluding the Compressed Class Space) Size
	  ReservedSpace metaspace_rs = ReservedSpace(compressed_class_space_size(),
	                                             _reserve_alignment,
	                                             large_pages,
	                                             requested_addr, 0);
	  if (!metaspace_rs.is_reserved()) {
	#if INCLUDE_CDS
	    if (UseSharedSpaces) {
	      size_t increment = align_size_up(1*G, _reserve_alignment);
	      char *addr = requested_addr;
	      while (!metaspace_rs.is_reserved() && (addr + increment > addr) &&
	             can_use_cds_with_metaspace_addr(addr + increment, cds_base)) {
	        addr = addr + increment;
	        metaspace_rs = ReservedSpace(compressed_class_space_size(),
	                                     _reserve_alignment, large_pages, addr, 0);
	      }
	    }
	#endif
	    // 忽略起始地址,嘗試重新申請,分配失敗拋出異常
	    if (!metaspace_rs.is_reserved()) {
	      metaspace_rs = ReservedSpace(compressed_class_space_size(),
	                                   _reserve_alignment, large_pages);
	      if (!metaspace_rs.is_reserved()) {
	        vm_exit_during_initialization(err_msg("Could not allocate metaspace: %d bytes",
	                                              compressed_class_space_size()));
	      }
	    }
	  }
	 
	  // If we got here then the metaspace got allocated.
	  MemTracker::record_virtual_memory_type((address)metaspace_rs.base(), mtClass);
	 
	#if INCLUDE_CDS
	  if (UseSharedSpaces && !can_use_cds_with_metaspace_addr(metaspace_rs.base(), cds_base)) {
	    FileMapInfo::stop_sharing_and_unmap(
	        "Could not allocate metaspace at a compatible address");
	  }
	#endif
	  //重置narrow_klass_base和narrow_klass_shift
	  set_narrow_klass_base_and_shift((address)metaspace_rs.base(),
	                                  UseSharedSpaces ? (address)cds_base : 0);
	  initialize_class_space(metaspace_rs);
	  
	  //打印日誌
	  if (PrintCompressedOopsMode || (PrintMiscellaneous && Verbose)) {
	      print_compressed_class_space(gclog_or_tty, requested_addr);
	  }
	}
	
	MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
	                              bool read_only, MetaspaceObj::Type type, TRAPS) {
	  if (HAS_PENDING_EXCEPTION) {
	    //不能有未處理異常
	    assert(false, "Should not allocate with exception pending");
	    return NULL;  // caller does a CHECK_NULL too
	  }
	  //loader_data不能爲空
	  assert(loader_data != NULL, "Should never pass around a NULL loader_data. "
	        "ClassLoaderData::the_null_class_loader_data() should have been used.");
	 
	  // DumpSharedSpaces默認爲false
	  if (DumpSharedSpaces) {
	    assert(type > MetaspaceObj::UnknownType && type < MetaspaceObj::_number_of_types, "sanity");
	    Metaspace* space = read_only ? loader_data->ro_metaspace() : loader_data->rw_metaspace();
	    MetaWord* result = space->allocate(word_size, NonClassType);
	    if (result == NULL) {
	      report_out_of_shared_space(read_only ? SharedReadOnly : SharedReadWrite);
	    }
	    if (PrintSharedSpaces) {
	      space->record_allocation(result, type, space->vsm()->get_raw_word_size(word_size));
	    }
	 
	    // Zero initialize.
	    Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);
	 
	    return result;
	  }
	 
	  MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;
	 
	  //獲取ClassLoaderData的_metaspace,然後分配內存
	  MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype);
	 
	  if (result == NULL) {
	    //報告分配事變
	    tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype);
	    if (is_init_completed()) {
	      //啓動完成通過GC釋放部分內存,然後嘗試重新分配
	      result = Universe::heap()->collector_policy()->satisfy_failed_metadata_allocation(
	          loader_data, word_size, mdtype);
	    }
	  }
	 
	  if (result == NULL) {
	    //報告分配失敗,會對外拋出異常
	    report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL);
	  }
	 
	  //將分配的內存初始化成0
	  Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);
	 
	  return result;
	}
}

對象創建

在《Hotspot 類文件加載、鏈接和初始化》,我們把類模板實例好了,只剩用new關鍵字創建對象了。關於new指令《hotspot解釋器和JIT》,他最終會觸發

//hotspot/src/share/vm/interpreter/bytecodeInterpretor.cpp
CASE(_new): {
        //獲取操作數棧中目標類的符號引用在常量池的索引
        u2 index = Bytes::get_Java_u2(pc+1);
        //獲取當前執行的方法的類的常量池,istate是當前字節碼解釋器BytecodeInterpreter實例的指針
        ConstantPool* constants = istate->method()->constants();
        //如果目標類已經解析
        if (!constants->tag_at(index).is_unresolved_klass()) {
          //校驗從常量池獲取的解析結果Klass指針是否是InstanceKlass指針,
          Klass* entry = constants->slot_at(index).get_klass();
          assert(entry->is_klass(), "Should be resolved klass");
          Klass* k_entry = (Klass*) entry;
          assert(k_entry->oop_is_instance(), "Should be InstanceKlass");
          InstanceKlass* ik = (InstanceKlass*) k_entry;
          //如果目標類已經完成初始化,並且可以使用快速分配的方式創建
          //如果滿足以下條件則不能使用快速分配的方式創建:
		  //1.目標類是抽象類
		  //2.目標類覆寫了Object的finalizer方法
		  //3.目標類大於FastAllocateSizeLimit參數的值,該參數默認是128k
		  //4.目標類是java/lang/Class,不能直接分配
          if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
            //獲取目標類的對象大小
            size_t obj_size = ik->size_helper();
            oop result = NULL;
            // If the TLAB isn't pre-zeroed then we'll have to do it
            //如果TLAB沒有預先初始化則必須在這裏完成初始化,need_zero表示是否需要初始化
            bool need_zero = !ZeroTLAB;
            //如果UseTLAB參數爲true,在TLAB中分配對象內存
            if (UseTLAB) {
              result = (oop) THREAD->tlab().allocate(obj_size);
            }
          
            //如果使用Profile則當TLAB分配失敗必須使用InterpreterRuntime::_new()方法分配內存,此時不能走
            //ifndef圈起來的一段邏輯
#ifndef CC_INTERP_PROFILE
            if (result == NULL) {
              need_zero = true;
              //嘗試在共享的eden區分配
            retry:
              //獲取當前未分配內存空間的起始地址
              HeapWord* compare_to = *Universe::heap()->top_addr();
              //起始地址加上目標類對象大小後,判斷是否超過eden區的終止地址
              HeapWord* new_top = compare_to + obj_size;
              if (new_top <= *Universe::heap()->end_addr()) {
                //如果沒有超過則通過原子CAS的方式嘗試分配,分配失敗就一直嘗試直到不能分配爲止
                //cmpxchg_ptr函數是比較top_addr的地址和compare_to的地址是否一樣,如果一樣則將new_top的地址寫入top_addr中並返回compare_to
                //如果不相等,即此時eden區分配了新對象,則返回top_addr新的地址,即返回結果不等於compare_to
                if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
                  goto retry;
                }
                result = (oop) compare_to;
              }
            }
#endif
            //如果分配成功
            if (result != NULL) {
              //如果需要完成對象初始化
              if (need_zero ) {
                HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
                obj_size -= sizeof(oopDesc) / oopSize;
                if (obj_size > 0 ) {
                  //將目標對象的內存置0
                  memset(to_zero, 0, obj_size * HeapWordSize);
                }
              }
              //設置對象頭
              if (UseBiasedLocking) {
               //如果使用偏向鎖
                result->set_mark(ik->prototype_header());
              } else {
                result->set_mark(markOopDesc::prototype());
              }
              //設置oop的相關屬性
              result->set_klass_gap(0);
              result->set_klass(k_entry);
              //執行內存屏障指令
              OrderAccess::storestore();
              //將目標對象放到操作數棧的頂部
              SET_STACK_OBJECT(result, 0);
              //更新PC指令計數器,即告訴解釋器此條new指令執行完畢,new指令總共3個字節,計數器加3
              UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
            }
          }
        }
        //調用InterpreterRuntime::_new執行慢速分配
        CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index),
                handle_exception);
        OrderAccess::storestore();
        //分配的對象保存在vm_result中,將對象放到操作數棧的頂部
        SET_STACK_OBJECT(THREAD->vm_result(), 0);
        //vm_result置空
        THREAD->set_vm_result(NULL);
        //更新PC指令計數器
        UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
      }
//運行時指令行接口
//hotspot/src/share/vm/interpreter/interpretorRuntime.cpp
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
  //獲取常量池中索引爲index的Klass指針
  Klass* k_oop = pool->klass_at(index, CHECK);
  instanceKlassHandle klass (THREAD, k_oop);
 
  //校驗Klass是否是抽象類,接口或者java.lang.Class,如果是則拋出異常
  klass->check_valid_for_instantiation(true, CHECK);
 
  //檢查Klass是否已經完成初始化,如果未完成則執行初始化
  //會對klass進行加載,鏈接初始化,
  klass->initialize(CHECK);
 
  //創建對象
  oop obj = klass->allocate_instance(CHECK);
  //將結果放到當前線程的_vm_result屬性中,該屬性專門用來傳遞解釋器執行方法調用的結果
  thread->set_vm_result(obj);
IRT_END

在這裏插入圖片描述

快速分配

如果在實例分配之前已經完成了類型的解析,那麼分配操作僅僅是在內存空間中劃分可用內存,因此能以較高效率實現內存分配,這就是快速分配

HotSpot通過線程局部分配緩存技術(Thread-Local AllocationBuffers,即TLABs)可以在線程私有區域實現空間的分配。
可以通過VM選項UseTLAB來開啓或關閉TLAB功能

根據分配空間是來自於線程私有區域還是共享的堆空間,快速分配可以分爲兩種空間選擇策略:
選擇TLAB:首先嚐試在TLAB中分配,因爲TLAB是線程私有區域,故不需要加鎖便能夠確保線程安全。在分配一個新的對象空間時,將首先嚐試在TLAB空間中分配對象空間,若分配空間的請求失敗,則再嘗試使用加鎖機制在Eden區分配新的TLAB,重新計算內存分部。如果還失敗。。
選擇Eden空間:則嘗試在共享的Eden區分配,Eden區是所有線程共享區域,需要保證線程安全,故採用原子操作進行分配。如果失敗,則一直重試

慢速分配

之所以成爲慢速分配,正是因爲在分配實例前需要對類進行解析,確保類及依賴類已得到正確的解析和初始化。

碰撞指針算法(bump-the-pointer)

通常情況下,系統中有大量連續的內存塊可來分配對象。如果提高效率

  1. 記錄上一次分配對象的位置
  2. 當有新對象要分配時,若檢查剩餘空間能夠滿足容納這個對象,就只需要一次移支指針的操作

線和局部分配緩存技術(TLAB)

爲了解決在Eden區分配內存時鎖競爭導致性能下降的問題。在Eden區域爲每個線程分配了私有的內存區域(Thread-local Allocation Buffers),簡稱TLABS。這裏的私有具體是指只能由該線程在這塊內存區域中分配對象,但是已經分配的對象其他線程也可以正常訪問.

//hotspot/src/share/vm/memory/threadLocalAllocBuffer.hpp
//每個線程中都會有這樣一個私有對象
class ThreadLocalAllocBuffer: public CHeapObj<mtThread> {
  //TLAB內存區域的起始地址
  HeapWord* _start;                              
  //最後一次分配後的地址,即該地址之前的內存區域都已經被分配了
  HeapWord* _top;
  //預分配的top
  HeapWord* _pf_top;                             
  //TLAB內存區域的結束地址
  HeapWord* _end;                               
  //期望的內存大小,包含保留區域,以字寬爲單位,即8字節
  size_t    _desired_size;                       
  //一個閾值,free()的返回值大於此值,則保留此TLAB,否則丟棄創建一個新的TLAB
  size_t    _refill_waste_limit; 
  //最近一次gc前已分配的內存大小
  size_t    _allocated_before_last_gc; 
  //TLAB的最大大小
  static size_t   _max_size;                     
  //在GC前的目標refill的次數
  static unsigned _target_refills; 
  //執行refill的次數,即重新分配TLAB的次數
  unsigned  _number_of_refills;
  //走快速分配refill浪費的內存大小
  unsigned  _fast_refill_waste;
  //走慢速分配refill浪費的內存大小
  unsigned  _slow_refill_waste;
  //因爲gc導致refill浪費的內存大小
  unsigned  _gc_waste;
  //走慢速分配的次數,通過TLAB分配是快速分配,走堆內存分配因爲必須加鎖是慢速分配
  unsigned  _slow_allocations;
  //用於自適應調整待分配的TLAB大小
  AdaptiveWeightedAverage _allocation_fraction;  
  
  //線程Thread初始化的時候會調用些方法
  void ThreadLocalAllocBuffer::initialize() {
	  //屬性初始化,爲空,全是null
	  initialize(NULL,                    // start
	             NULL,                    // top
	             NULL);                   // end
	  //計算TLAB期望的大小
	  set_desired_size(initial_desired_size());
	 
	  if (Universe::heap() != NULL) {
	    size_t capacity   = Universe::heap()->tlab_capacity(myThread()) / HeapWordSize;
	    double alloc_frac = desired_size() * target_refills() / (double) capacity;
	    //將當前準備分配的TLAB的大小作爲樣本採集
	    _allocation_fraction.sample(alloc_frac);
	  }
	 
	  //設置初始的refill_waste_limit
	  set_refill_waste_limit(initial_refill_waste_limit());
	  //初始化統計數據
	  initialize_statistics();
	}

	inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) {
	  invariants();
	  HeapWord* obj = top();
	  //通過指針地址計算剩餘的空間是否大於待分配的內存大小
	  //注意pointer_delta返回的內存大小以及這裏待分配的內存大小size的單位都是字寬,8字節
	  if (pointer_delta(end(), obj) >= size) {
	    //將obj的指針往前移動指定大小,將原top的地址返回
	    set_top(obj + size);
	 
	    invariants();
	    return obj;
	  }
	  return NULL;
	}
	 
	inline size_t pointer_delta(const HeapWord* left, const HeapWord* right) {
	  return pointer_delta(left, right, sizeof(HeapWord));
	}
	 
	inline size_t pointer_delta(const void* left,
	                            const void* right,
	                            size_t element_size) {
	  return (((uintptr_t) left) - ((uintptr_t) right)) / element_size;
	}
	 
	void ThreadLocalAllocBuffer::initialize(HeapWord* start,
	                                        HeapWord* top,
	                                        HeapWord* end) {
	  set_start(start);
	  set_top(top);
	  set_pf_top(top);
	  set_end(end);
	  //參數校驗
	  invariants();
	}
	
    //快速使用tlab分配失敗
    //計算,重新在eden裏面申請個tlab。
	HeapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass, Thread* thread, size_t size) {
	 
	  // Retain tlab and allocate object in shared space if
	  // the amount free in the tlab is too large to discard.
	  //如果剩餘的空間大於允許refill浪費的空間則繼續使用該TLAB,返回NULL後就將在Eden區分配內存,因爲必須加鎖,所以相對於
	  //走TLAB是慢速分配
	  if (thread->tlab().free() > thread->tlab().refill_waste_limit()) {
	    thread->tlab().record_slow_allocation(size);
	    return NULL;
	  }
	 
	  
	  //如果剩餘的空閒小於refill浪費的空間則丟棄當前線程的TLAB,重新分配一個新的
	  //爲了避免內存碎片化,新分配的TLAB會比之前分配的更小,compute_size就是計算待分配的TLAB的大小,如果返回0說明Eden區內存不足
	  size_t new_tlab_size = thread->tlab().compute_size(size);
	  
	  //這裏的clear並不是釋放當前TALB佔用的內存,而是將剩餘的允許浪費的空間用無意義的對象填充,讓Eden區的內存是連續的
	  //同時將top,end指針等置位NULL
	  thread->tlab().clear_before_allocation();
	 
	  if (new_tlab_size == 0) {
	    //Eden區堆內存不足了,返回NULL,可能會觸發Eden區的垃圾回收
	    return NULL;
	  }
	 
	  //分配一個新的TLAB,有可能分配失敗
	  //會在年輕代裏面重新開一個空間
	  HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
	  if (obj == NULL) {
	    return NULL;
	  }
	  //發佈事件通知
	  AllocTracer::send_allocation_in_new_tlab_event(klass, new_tlab_size * HeapWordSize, size * HeapWordSize);
	 
	  if (ZeroTLAB) {
	    //初始化
	    Copy::zero_to_words(obj, new_tlab_size);
	  }
	  //新分配的TLAB的屬性初始化 
	  thread->tlab().fill(obj, obj + size, new_tlab_size);
	  return obj;
	}
}
  1. 被始化ThreadLocalAllocBuffer

Threads::create_vm()《openJdk的啓動流程》中的方法->
  java_start
  JavaThread::run()
   ThreadLocalAllocBuffer::initialize()

設置的相關屬性都爲null
initialize(NULL, NULL, NULL);

  1. 當調用ThreadLocalAllocBuffer::allocate_from_tlab時
HeapWord* CollectedHeap::allocate_from_tlab(KlassHandle klass, Thread* thread, size_t size) {
  assert(UseTLAB, "should use UseTLAB");
  //從tlab中分配指定大小的內存
  HeapWord* obj = thread->tlab().allocate(size);
  if (obj != NULL) {
    return obj;
  }
  //走慢速模式從tlab中分配內存
  return allocate_from_tlab_slow(klass, thread, size);
}

因爲初始值都爲null,所以直接allocate失敗,轉而走allocate_from_tlab_slow

  1. allocate_from_tlab_slow中如果發現tlab不夠,則申請

Universe::heap()->allocate_new_tlab(new_tlab_size);
ParallelScavengeHeap模式下最終調用年輕代申請
young_gen()->allocate(size)

  1. 再次申請對象就會進行快速分配allocate

//直接在已申請的tlab上划走空間
//將obj的指針往前移動指定大小,將原top的地址返回
set_top(obj + size);

棧上分配

在棧中分配的基本思路是這樣的:分析局部變量的作用域僅限於方法內部,則JVM直接在棧幀內分配對象空間,避免在堆中分配。

  1. 逃逸分析escape analysis。

分析程序中指針的動態作用域,看某個指針是否指向某個固定的對象並且沒有“逃逸”出某個函數/方法或者線程的範圍。如果沒有逃逸則可知該指針只在某個局部範圍內可見,外部(別的函數/方法或線程)無法看到它

  1. 標量替換,scalar replacement

Java中的原始類型無法再分解,可以看作標量(scalar);指向對象的引用也是標量;而對象本身則是聚合量(aggregate),可以包含任意個數的標量。如果把一個Java對象拆散,將其成員變量恢復爲分散的變量,這就叫做標量替換。拆散後的變量便可以被單獨分析與優化,可以各自分別在活動記錄(棧幀或寄存器)上分配空間;原本的對象就無需整體分配空間了。

這樣子做的目的是減少新生代的收集次數,間接的提高JVM性能。

注:

  1. Oracle/Sun的HotSpot VM從來沒在產品裏實現過棧上分配,而只實現過它的一種特殊形式——標量替換,這倆是不一樣的喔。棧上分配還是要分配完整的對象結構,只不過是在棧幀裏而不在GC堆裏分配;標量替換則不分配完整的對象,直接把對象的字段打散看作方法的局部變量,也就是說標量替換後就沒有對象頭了,也不需要把該對象的字段打包爲一個整體。
  2. 逃逸分析及標量替換的棧上分配,是在JIT編譯期優化的,解釋不存在

對象創建各組件空間的申請

對象數據實例申請
InstanceKlass::allocate_instance
 CollectedHeap::obj_allocate
  CollectedHeap::common_mem_allocate_init
   CollectedHeap->mem_allocate
根據不同的垃圾收集器,調用的類不一樣比如ParallelScavengeHeap
直接在年輕代裏面申請 young_gen()->allocate(size);

類信息數據內存申請
ClassFileParser::parseClassFile
 InstanceKlass::allocate_instance_klass
  new (loader_data, size, THREAD) InstanceKlass
   Klass::operator new()
   Metaspace::allocate
klass通過重寫operator new方式指定metaspace進行分配內存
在metaspace找到此類loader_data位置然後進行分配

類常量池內存申請
ClassFileParser::parseClassFile
 ConstantPool::allocate
  new (loader_data, size, false, ConstantPoolType, THREAD) ConstantPool
   MetaspaceObj::operator new()
   Metaspace::allocate
MetaspaceObj通過重寫operator new方式指定metaspace進行分配內存
在metaspace找到此類loader_data位置然後進行分配
另外描述符Symbol也是繼承MetaspaceObj,具體參考SymbolTable

字符串常量池內存申請,<<JVM常量池>>
ClassFileParser::parseClassFile
 java_lang_Class::create_mirror
對應class加載過程的準備階段
設置類的靜態 static聲明的,對象爲null,int 爲0
對於 final static String 去字符串常量池StringTable中去尋找
如果有則和 mirror關聯起來
如果沒有則 調用klass:allocate_instance申請空間

編譯方法的空間申請
<<hotspot解釋器和JIT>>中,c1,c2編譯生成nmethod
nmethod通過重寫operator new方式指定codeCache進行分配內存

c heap空間
比如StringTable和SymbolTable的框架結構都是直接new出來的
StringTable entry 是堆中申請
SymbolTable entry 是元數據塊申請

//hotspot/src/share/vm/oops/instanceKlass.cpp
InstanceKlass : Klass{
	instanceOop InstanceKlass::allocate_instance(TRAPS) {
	 //判斷目標類是否覆寫了Object類的finalize方法
	  bool has_finalizer_flag = has_finalizer(); 
	  //獲取目標類的對象大小
	  int size = size_helper();  
	  KlassHandle h_k(THREAD, this);
	  
	  instanceOop i;
	  //創建對象
	  i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
	  //如果覆寫了finalize方法並且RegisterFinalizersAtInit爲false,即不在JVM啓動時完成finalize方法的註冊
	  if (has_finalizer_flag && !RegisterFinalizersAtInit) {
	    //註冊finalize方法
	    i = register_finalizer(i, CHECK_NULL);
	  }
	  return i;
	}
	InstanceKlass* InstanceKlass::allocate_instance_klass(
	                                              ClassLoaderData* loader_data,
	                                              int vtable_len,
	                                              int itable_len,
	                                              int static_field_size,
	                                              int nonstatic_oop_map_size,
	                                              ReferenceType rt,
	                                              AccessFlags access_flags,
	                                              Symbol* name,
	                                              Klass* super_klass,
	                                              bool is_anonymous,
	                                              TRAPS) {
	
	  //計算出InstanceKlass對象要佔用的空間
	  int size = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size,
	                                 access_flags.is_interface(), is_anonymous);
	
	  // Allocation
	  InstanceKlass* ik;
	  if (rt == REF_NONE) {
	    if (name == vmSymbols::java_lang_Class()) {
	      ik = new (loader_data, size, THREAD) InstanceMirrorKlass(
	        vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt,
	        access_flags, is_anonymous);
	    } else if (name == vmSymbols::java_lang_ClassLoader() ||
	          (SystemDictionary::ClassLoader_klass_loaded() &&
	          super_klass != NULL &&
	          super_klass->is_subtype_of(SystemDictionary::ClassLoader_klass()))) {
	      ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(
	        vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt,
	        access_flags, is_anonymous);
	    } else {
	      // 普通類選項
	      //使用new生成對象實際上執行了三個操作:
          //1. 調用operator new分配內存 調用重寫Klass::operator new
          //2. 調用類的構造函數 
          //3. 返回指針
	      ik = new (loader_data, size, THREAD) InstanceKlass(
	        vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt,
	        access_flags, is_anonymous);
	    }
	  } else {
	    // reference klass
	    ik = new (loader_data, size, THREAD) InstanceRefKlass(
	        vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt,
	        access_flags, is_anonymous);
	  }
	
	  // Check for pending exception before adding to the loader data and incrementing
	  // class count.  Can get OOM here.
	  if (HAS_PENDING_EXCEPTION) {
	    return NULL;
	  }
	
	  // Add all classes to our internal class loader list here,
	  // including classes in the bootstrap (NULL) class loader.
	  loader_data->add_class(ik);
	
	  Atomic::inc(&_total_instanceKlass_count);
	  return ik;
	}
	
	void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
	  //元數據分配空間
	  return Metaspace::allocate(loader_data, word_size, /*read_only*/false,
	                             MetaspaceObj::ClassType, THREAD);
	}
}
//hotspot/src/share/vm/gc_interface/collectedHeap.hpp
CollectedHeap{
	oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
	  debug_only(check_for_valid_allocation_state());
	  //檢查Java堆是否正在gc
	  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(klass, size, CHECK_NULL);
	  //設置對象頭和klass屬性
	  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(KlassHandle klass, size_t size, TRAPS) {
	  HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
	  init_obj(obj, size);
	  return obj;
	}
	 
	HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) {
	 
	  //清理當前線程TLAB中未使用的opp
	  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;
	  //如果使用TLAB
	  if (UseTLAB) {
	    //從TLAB分配對象
	    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;
	  //從Java堆中分配對象內存
	  //根據不同的垃圾收集器,調用的類不一樣比如ParallelScavengeHeap
	  //HeapWord* result = young_gen()->allocate(size);
	  //直接在年輕帶裏面申請
	  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) {
	    //異常處理,Java heap space表示當前堆內存嚴重不足
	    report_java_out_of_memory("Java heap space");
	    //通知JVMTI
	    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 {
	    //同上,異常處理,GC overhead limit exceeded表示執行GC後仍不能有效回收內存導致內存不足
	    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());
	  }
	}
}
//hotspot/src/share/vm/classfile/collectedHeap.hpp
class java_lang_Class : AllStatic {

	void java_lang_Class::create_mirror(KlassHandle k, Handle class_loader,
	                                    Handle protection_domain, TRAPS) {
	  assert(k->java_mirror() == NULL, "should only assign mirror once");
	  
	  int computed_modifiers = k->compute_modifier_flags(CHECK);
	  k->set_modifier_flags(computed_modifiers);
	  // Class_klass是否已加載
	  if (SystemDictionary::Class_klass_loaded()) {
	    //根據Class_klass中的常量申請一個class對象
	    Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK);
	
	    // 把申請的class對象和Class_klass關聯起來
	    if (!k.is_null()) {
	      java_lang_Class::set_klass(mirror(), k());
	    }
	
	    InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass());
	    assert(oop_size(mirror()) == mk->instance_size(k), "should have been set");
        //設置Class_klass的靜態字段數	
	    java_lang_Class::set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror()));
	
	    // 如果是數組對象
	    if (k->oop_is_array()) {
	    	//省略
	    } else {//普通對象
	      assert(k->oop_is_instance(), "Must be");
	      //設置類的靜態 static聲明的,對象爲null,int 爲0
	      //對於 final static String 去字符串常量池StringTable中去尋找
	      //如果有則和 mirror關聯起來
	      //如果沒有則 調用klass:allocate_instance申請空間
	      initialize_mirror_fields(k, mirror, protection_domain, THREAD);
	      if (HAS_PENDING_EXCEPTION) {
	        //如果有異常設null
	        java_lang_Class::set_klass(mirror(), NULL);
	        return;
	      }
	    }
	
	    // set the classLoader field in the java_lang_Class instance
	    assert(class_loader() == k->class_loader(), "should be same");
	    set_class_loader(mirror(), class_loader());
	
	    // Setup indirection from klass->mirror last
	    // after any exceptions can happen during allocations.
	    if (!k.is_null()) {
	      k->set_java_mirror(mirror());
	    }
	  } else {
	    if (fixup_mirror_list() == NULL) {
	      GrowableArray<Klass*>* list =
	       new (ResourceObj::C_HEAP, mtClass) GrowableArray<Klass*>(40, true);
	      set_fixup_mirror_list(list);
	    }
	    fixup_mirror_list()->push(k());
	  }
	}
}

主要參考

《hotspot實戰》
《Java虛擬機規範8版》
openjdk hotspot源碼分析之string.intern方法原理
Hotspot 內存管理之Metaspace(三) 源碼解析
Hotspot 內存管理之CodeCache 源碼解析
Hotspot 內存管理之Universe 源碼解析
Hotspot Java對象創建和TLAB源碼解析
逃逸分析/標量替換的一個演示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章