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源码解析
逃逸分析/标量替换的一个演示

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