JVM篇-结合源码分析JVM堆内存&元空间的申请分配
发布日期:2021-05-07 20:42:18 浏览次数:27 分类:精选文章

本文共 25446 字,大约阅读时间需要 84 分钟。

一、堆内存的结构

1、结构图

在这里插入图片描述

​ 我们可以看到给的这张图。可以看到其堆可以有两种分配类型:一种就是JVM申请提交的内存(committed)以及目前还没有使用到的内存(Virtual),另一种就是按照分代的概念:年轻代(Young Generation)老年代(Tenured Generation)。

​ 同时根据这个结构图我们可以知道在代与代之间还有空出来暂时没有使用到的内存。

2、年轻代&老年代分配

​ 我们可以看到在年轻代是有两个概念的划分的:Eden、Survivor,然后Survivor是又有分两个区。整个内存之所以会有这些代的概念划分其主要是从对象的创建以及对象的回收(GC)来说的,因为大部分对象只能活一代,如创建一个对象,调用其的一个方法,然后这个对象就能回收了。只有少部分对象才会存活很久,这种就按Eden -> Survivor -> Tenured的顺序晋升(也不一定会是这种顺序,然后例如如果Survivor的空间不够了,也是可能直接分配到Tenured中)。然后在回收的时候就可以按照这些代不同的特性来使用不同的回收算法来回收。例如Eden区就是使用一代的对象,这里就需要经常进行垃圾回收(以及不会再使用的对象),然后如果要使用更多代,就会将其晋升到Survivor空间。为什么Survivor会划分为两个呢?这是因为这里更适合使用使用复制算法。这些我们到垃圾回收算法篇再来梳理。

二、一些基本类&数据结构的定义(基于openjdk9)

​ 阅读的很多书都是从一些概念上进行描叙,下面我们就从JVM具体的实现代码来大体梳理下堆空间的申请划分过程。实现我们来看下JVM定义的一些基本类

1、CHeapObj

class CHeapObj { public:  void* operator new(size_t size) throw();  void  operator delete(void* p);  void* new_array(size_t size);};

​ 这个就是堆对象的基类,可以看到其主要是定义了newdelete的重载方法。

2、CollectedHeap

class CollectedHeap : public CHeapObj
{ ......... public: enum Name { GenCollectedHeap, ParallelScavengeHeap, G1CollectedHeap }; ............

​ 这个就是内存分配的操作类CollectedHeap,可以看到其有三个子类GenCollectedHeapParallelScavengeHeapG1CollectedHeap,分别对应不同的垃圾回收器。

3、GenCollectedHeap

class GenCollectedHeap : public CollectedHeap {  friend class GenCollectorPolicy;  friend class Generation;  friend class DefNewGeneration;  friend class TenuredGeneration;  friend class ConcurrentMarkSweepGeneration;  friend class CMSCollector;  friend class GenMarkSweep;	..........  enum GenerationType {    YoungGen,    OldGen  };private:  Generation* _young_gen;  Generation* _old_gen;  // The singleton CardTable Remembered Set.  CardTableRS* _rem_set;  // The generational collector policy.  GenCollectorPolicy* _gen_policy;    ..............

​ 可以看到其定义了两个代类型YoungGen年轻代OldGen老年代。

4、CollectorPolicy

class CollectorPolicy : public CHeapObj
{ protected: virtual void initialize_alignments() = 0; virtual void initialize_flags(); virtual void initialize_size_info(); DEBUG_ONLY(virtual void assert_flags();) DEBUG_ONLY(virtual void assert_size_info();) size_t _initial_heap_byte_size; size_t _max_heap_byte_size; size_t _min_heap_byte_size; size_t _space_alignment; size_t _heap_alignment; ...... public: virtual void initialize_all() { initialize_alignments(); initialize_flags(); initialize_size_info(); } .......

​ 这里就是第一收集器的策略,例如有GenCollectorPolicy、MarkSweepPolicy(标记清除),可以看到这里有堆的最大、最小值,空间对齐等。然后主要有一个initialize_all()方法是初始化创建的时候调用的。

5、GenCollectorPolicy

class GenCollectorPolicy : public CollectorPolicy {  friend class TestGenCollectorPolicy;  friend class VMStructs; protected:  size_t _min_young_size;  size_t _initial_young_size;  size_t _max_young_size;  size_t _min_old_size;  size_t _initial_old_size;  size_t _max_old_size;	.....  GenerationSpec* _young_gen_spec;  GenerationSpec* _old_gen_spec;
class GenerationSpec : public CHeapObj
{ friend class VMStructs;private: Generation::Name _name; size_t _init_size; size_t _max_size;

​ 可以看到这里有定义两个代的规范:_young_gen_spec_old_gen_spec

6、MarkSweepPolicy

class MarkSweepPolicy : public GenCollectorPolicy { protected:  void initialize_alignments();  void initialize_generations(); public:  MarkSweepPolicy() {}  MarkSweepPolicy* as_mark_sweep_policy() { return this; }  void initialize_gc_policy_counters();};

​ 标记清除

7、ReservedSpace

class ReservedSpace VALUE_OBJ_CLASS_SPEC {  friend class VMStructs; protected:  char*  _base;  size_t _size;  size_t _noaccess_prefix;  size_t _alignment;  bool   _special; private:  bool   _executable;  // ReservedSpace  ReservedSpace(char* base, size_t size, size_t alignment, bool special,                bool executable); protected:  void initialize(size_t size, size_t alignment, bool large,                  char* requested_address,                  bool executable);  	.............  // Accessors  char*  base()            const { return _base;      }  size_t size()            const { return _size;      }  size_t alignment()       const { return _alignment; }  bool   special()         const { return _special;   }

​ 这个类很重要,这个类就是用来管理分配的具体空间的,可以看到这里有一个_base,与_size配合,_base就是分配到的内存的开始位置,然后_size就是大小,以前配合就能表示分派的内存了。

8、ReservedSpace的子类

// Class encapsulating behavior specific of memory space reserved for Java heap.class ReservedHeapSpace : public ReservedSpace { private:  void try_reserve_heap(size_t size, size_t alignment, bool large,                        char *requested_address);  ..........};

​ 这个ReservedHeapSpace就是表示的堆空间。

// Class encapsulating behavior specific memory space for Codeclass ReservedCodeSpace : public ReservedSpace { public:  // Constructor  ReservedCodeSpace(size_t r_size, size_t rs_align, bool large);};

​ 这个ReservedCodeSpace就是用来描叙分配的代码空间

// VirtualSpace is data structure for committing a previously reserved address range in smaller chunks.class VirtualSpace VALUE_OBJ_CLASS_SPEC {  friend class VMStructs; private:  // Reserved area  char* _low_boundary;  char* _high_boundary;  // Committed area  char* _low;  char* _high;

​ 这个VirtualSpace就是对应前面表示堆空间图中的预留空间的,可以看到其由_low表示低位、_high表示高位的地址。

三、JVM源码对堆的申请分配代码大体流程

在这里插入图片描述

1、universe.cpp - universe_init()

​ 其的初始化起点是在universe.cpp文件的universe_init()方法

jint universe_init() {	.......  jint status = Universe::initialize_heap();  if (status != JNI_OK) {    return status;  }  Metaspace::global_initialize();	..........  if (UseSharedSpaces) {    ...........    MetaspaceShared::initialize_shared_spaces();    StringTable::create_table();  } else {    .......    if (DumpSharedSpaces) {      MetaspaceShared::prepare_for_dumping();    }  }    ..........  return JNI_OK;}

​ 这里的对于堆的初始化就是Universe::initialize_heap()方法。然后这里还有JVM两个参数的判断UseSharedSpacesDumpSharedSpaces。这个SharedSpaces目前还不知道是干嘛的。

2、universe.cpp - initialize_heap()

1)、initialize_heap()

jint Universe::initialize_heap() {     jint status = JNI_ERR;	......  if (_collectedHeap == NULL) {       _collectedHeap = create_heap();  }  status = _collectedHeap->initialize();  if (status != JNI_OK) {       return status;  }  ThreadLocalAllocBuffer::set_max_size(Universe::heap()->max_tlab_size());	......  return JNI_OK;}
// The particular choice of collected heap.  static CollectedHeap* _collectedHeap;  ............static CollectedHeap* heap() { return _collectedHeap; }

​ 这里就是通过CollectedHeap(_collectedHeap)来进行初始化分配操作,目前我源码debug其默认使用的是GenCollectedHeap

2)、create_heap()

CollectedHeap* Universe::create_heap() {   ........  if (UseParallelGC) {    return Universe::create_heap_with_policy
(); } else if (UseG1GC) { return Universe::create_heap_with_policy
(); } else if (UseConcMarkSweepGC) { return Universe::create_heap_with_policy
();#endif } else if (UseSerialGC) { return Universe::create_heap_with_policy
(); } ShouldNotReachHere(); return NULL;}

​ 可以看到这里根据不同的垃圾回收算法使用的是不同的CollectedHeap以及CollectorPolicy

在这里插入图片描述

​ 可以看到我目前debug默认的是UseSerialGC算法

template 
CollectedHeap* Universe::create_heap_with_policy() { Policy* policy = new Policy(); policy->initialize_all(); return new Heap(policy);}

​ 同时在这里有调用initialize_all()方法,create_heap_with_policy()被定义为了一个template模板方法。

3)、初始化赋值

这里初始化赋值初始化调用

GenCollectorPolicy::GenCollectorPolicy() :    _min_young_size(0),    _initial_young_size(0),    _max_young_size(0),    _min_old_size(0),    _initial_old_size(0),    _max_old_size(0),    _gen_alignment(0),    _young_gen_spec(NULL),    _old_gen_spec(NULL){}
virtual void initialize_all() {    CollectorPolicy::initialize_all();    initialize_generations();}
public:  virtual void initialize_all() {    initialize_alignments();    initialize_flags();    initialize_size_info();  }  .........void GenCollectorPolicy::initialize_size_info() {  CollectorPolicy::initialize_size_info();  _initial_young_size = NewSize;  _max_young_size = MaxNewSize;  _initial_old_size = OldSize;

​ 上面就是GenCollectorPolicy的一些初始化赋值,然后关键就是initialize_generations()方法的调用以及实现

void MarkSweepPolicy::initialize_generations() {  _young_gen_spec = new GenerationSpec(Generation::DefNew, _initial_young_size, _max_young_size, _gen_alignment);  _old_gen_spec   = new GenerationSpec(Generation::MarkSweepCompact, _initial_old_size, _max_old_size, _gen_alignment);}
public: // The set of possible generation kinds. enum Name {   DefNew,   ParNew,   MarkSweepCompact,   ConcurrentMarkSweep,   Other };

​ 在这里我们就完成了_young_gen_spec_old_gen_spec的初始化创建,可以看到当前策略年轻代使用的是DefNew、老年代使用的是MarkSweepCompact(标记清除压缩)。

3、 genCollectedHeap.cpp - initialize()

jint GenCollectedHeap::initialize() {  .............  // Allocate space for the heap.  char* heap_address;  ReservedSpace heap_rs;  size_t heap_alignment = collector_policy()->heap_alignment();  heap_address = allocate(heap_alignment, &heap_rs);	........  initialize_reserved_region((HeapWord*)heap_rs.base(), (HeapWord*)(heap_rs.base() + heap_rs.size()));  _rem_set = collector_policy()->create_rem_set(reserved_region());  set_barrier_set(rem_set()->bs());  ReservedSpace young_rs = heap_rs.first_part(gen_policy()->young_gen_spec()->max_size(), false, false);  _young_gen = gen_policy()->young_gen_spec()->init(young_rs, rem_set());  heap_rs = heap_rs.last_part(gen_policy()->young_gen_spec()->max_size());  ReservedSpace old_rs = heap_rs.first_part(gen_policy()->old_gen_spec()->max_size(), false, false);  _old_gen = gen_policy()->old_gen_spec()->init(old_rs, rem_set());  clear_incremental_collection_failed();	...........  return JNI_OK;}

​ 这个方法就是进行内存申请&内存划代分配的方法了。

1)、allocate(heap_alignment, &heap_rs)

char* GenCollectedHeap::allocate(size_t alignment,                                 ReservedSpace* heap_rs){  // Now figure out the total size.  const size_t pageSize = UseLargePages ? os::large_page_size() : os::vm_page_size();  assert(alignment % pageSize == 0, "Must be");  GenerationSpec* young_spec = gen_policy()->young_gen_spec();  GenerationSpec* old_spec = gen_policy()->old_gen_spec();  // Check for overflow.  size_t total_reserved = young_spec->max_size() + old_spec->max_size(); ......  assert(total_reserved % alignment == 0,         "Gen size; total_reserved=" SIZE_FORMAT ", alignment="         SIZE_FORMAT, total_reserved, alignment);  *heap_rs = Universe::reserve_heap(total_reserved, alignment);	......  return heap_rs->base();}

​ 可以看到这里首先是是获取两个代标准对象young_specold_spec。然后由其的最大值之和得到一个申请的总空间total_reserved。然后通过reserve_heap方法来正式申请内存空间。然后通过heap_rs->base()来返回这块空间的起始位置_base

2)、Universe::reserve_heap(total_reserved, alignment)

ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {......  size_t total_reserved = align_size_up(heap_size, alignment);......  // Now create the space.  ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages);  if (total_rs.is_reserved()) {    assert((total_reserved == total_rs.size()) && ((uintptr_t)total_rs.base() % alignment == 0),           "must be exactly of required size and alignment");    // We are good.    if (UseCompressedOops) {      // Universe::initialize_heap() will reset this to NULL if unscaled      // or zero-based narrow oops are actually used.      // Else heap start and base MUST differ, so that NULL can be encoded nonambigous.      Universe::set_narrow_oop_base((address)total_rs.compressed_oop_base());    }    return total_rs;  }......  return ReservedHeapSpace(0, 0, false);}

​ 这里就是通过ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages);来创建这个空间。

ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large) : ReservedSpace() {  if (size == 0) {    return;  }    .........  if (UseCompressedOops) {    initialize_compressed_heap(size, alignment, large);    if (_size > size) {      // We allocated heap with noaccess prefix.      // It can happen we get a zerobased/unscaled heap with noaccess prefix,      // if we had to try at arbitrary address.      establish_noaccess_prefix();    }  } else {    initialize(size, alignment, large, NULL, false);  }.....  if (base() > 0) {    MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap);  }}

可以看到这里还有一个参数判断UseCompressedOops。这个当前默认是true,但我们简单处理,这个修改这个值来跳到initialize(size, alignment, large, NULL, false)

在这里插入图片描述

void ReservedSpace::initialize(size_t size, size_t alignment, bool large,                               char* requested_address, ......  _base = NULL;  _size = 0;  _special = false;  _executable = executable;  _alignment = 0;  _noaccess_prefix = 0;  if (size == 0) {    return;  }  // If OS doesn't support demand paging for large page memory, we need  // to use reserve_memory_special() to reserve and pin the entire region.  bool special = large && !os::can_commit_large_page_memory();  char* base = NULL;	.............  if (base == NULL) {    // Optimistically assume that the OSes returns an aligned base pointer.    // When reserving a large address range, most OSes seem to align to at    // least 64K.    // If the memory was requested at a particular address, use    // os::attempt_reserve_memory_at() to avoid over mapping something    // important.  If available space is not detected, return NULL.    if (requested_address != 0) {      base = os::attempt_reserve_memory_at(size, requested_address);      if (failed_to_reserve_as_requested(base, requested_address, size, false)) {        // OS ignored requested address. Try different address.        base = NULL;      }    } else {      base = os::reserve_memory(size, NULL, alignment);    }    if (base == NULL) return;	.........  // Done  _base = base;  _size = size;  _alignment = alignment;}

​ 这里由于我们当前没有requested_address(传入的是NULL)所以进入的是base = os::reserve_memory(size, NULL, alignment),通过其申请到内存然后通过_base = base赋值给_base

4、os.cpp - reserve_memory(size, NULL, alignment)

char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint) {  char* result = pd_reserve_memory(bytes, addr, alignment_hint);  if (result != NULL) {    MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);  }  return result;}

这个pd_reserve_memory方法是与平台相关的,当前是linux。

在这里插入图片描述

char* os::pd_reserve_memory(size_t bytes, char* requested_addr,                            size_t alignment_hint) {  return anon_mmap(requested_addr, bytes, (requested_addr != NULL));}
static char* anon_mmap(char* requested_addr, size_t bytes, bool fixed) {  char * addr;  int flags;  flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS;  if (fixed) {    assert((uintptr_t)requested_addr % os::Linux::page_size() == 0, "unaligned address");    flags |= MAP_FIXED;  }  // Map reserved/uncommitted pages PROT_NONE so we fail early if we  // touch an uncommitted page. Otherwise, the read/write might  // succeed if we have enough swap space to back the physical page.  addr = (char*)::mmap(requested_addr, bytes, PROT_NONE,                       flags, -1, 0);  return addr == MAP_FAILED ? NULL : addr;}

可以看到我们终于来到了申请内存的最终linux系统的方法mmap了,这里就是通过mmap(requested_addr, bytes, PROT_NONE,flags, -1, 0)方法申请到了我们的方法。

5、分代逻辑&年轻代的细分

1)、分代逻辑

​ 通过上面的逻辑,已经申请到了内存我们再回到前面逻辑,来看下代的划分逻辑

ReservedSpace young_rs = heap_rs.first_part(gen_policy()->young_gen_spec()->max_size(), false, false);_young_gen = gen_policy()->young_gen_spec()->init(young_rs, rem_set());heap_rs = heap_rs.last_part(gen_policy()->young_gen_spec()->max_size());ReservedSpace old_rs = heap_rs.first_part(gen_policy()->old_gen_spec()->max_size(), false, false);_old_gen = gen_policy()->old_gen_spec()->init(old_rs, rem_set());

这里主要是两个方法来操作heap_rs.last_partheap_rs.last_part

ReservedSpace ReservedSpace::first_part(size_t partition_size, size_t alignment,                                        bool split, bool realloc) {  assert(partition_size <= size(), "partition failed");  if (split) {    os::split_reserved_memory(base(), size(), partition_size, realloc);  }  ReservedSpace result(base(), partition_size, alignment, special(),                       executable());  return result;}ReservedSpaceReservedSpace::last_part(size_t partition_size, size_t alignment) {  assert(partition_size <= size(), "partition failed");  ReservedSpace result(base() + partition_size, size() - partition_size,                       alignment, special(), executable());  return result;}
ReservedSpace::ReservedSpace(char* base, size_t size, size_t alignment,                             bool special, bool executable) {  assert((size % os::vm_allocation_granularity()) == 0,         "size not allocation aligned");  _base = base;  _size = size;  _alignment = alignment;  _noaccess_prefix = 0;  _special = special;  _executable = executable;}

​ 我们先看first_part,其的base赋值是直接获取base()地址赋值的,然后last_part,是重新设置了_base的位置,其的设置是base() + partition_size,对应代码heap_rs = heap_rs.last_part(gen_policy()->young_gen_spec()->max_size()),这里就是将_base移动了已经分配给了_young_gen的空间(gen_policy()->young_gen_spec()->max_size())。然后下次分配给_old_gen的时候再调用base()方法的时候就是另一个起点了。怎样就完成了两个区的划分了。

2)、年轻代的细分

年轻代的具体划分是承接上面的gen_policy()->young_gen_spec()->init(young_rs, rem_set())init()

Generation* GenerationSpec::init(ReservedSpace rs, CardTableRS* remset) {  switch (name()) {    case Generation::DefNew:      return new DefNewGeneration(rs, init_size());    case Generation::MarkSweepCompact:      return new TenuredGeneration(rs, init_size(), remset);#if INCLUDE_ALL_GCS    case Generation::ParNew:      return new ParNewGeneration(rs, init_size());    case Generation::ConcurrentMarkSweep: {      assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set");      if (remset == NULL) {        vm_exit_during_initialization("Rem set incompatibility.");      }      // Otherwise      // The constructor creates the CMSCollector if needed,      // else registers with an existing CMSCollector      ConcurrentMarkSweepGeneration* g = NULL;      g = new ConcurrentMarkSweepGeneration(rs, init_size(), remset);      g->initialize_performance_counters();      return g;    }#endif // INCLUDE_ALL_GCS    default:      guarantee(false, "unrecognized GenerationName");      return NULL;  }}

​ 这个就是我们前面定义的名称判断,我们前面年轻代定义的是DefNew,所以这里走的的是new DefNewGeneration(rs, init_size())

然后就是构造方法的初始创建了:

DefNewGeneration::DefNewGeneration(ReservedSpace rs,                                   size_t initial_size,                                   const char* policy)  : Generation(rs, initial_size),    _preserved_marks_set(false /* in_c_heap */),    _promo_failure_drain_in_progress(false),    _should_allocate_from_space(false){  MemRegion cmr((HeapWord*)_virtual_space.low(),                (HeapWord*)_virtual_space.high());  GenCollectedHeap* gch = GenCollectedHeap::heap();  gch->barrier_set()->resize_covered_region(cmr);  _eden_space = new ContiguousSpace();  _from_space = new ContiguousSpace();  _to_space   = new ContiguousSpace();.........  // Compute the maximum eden and survivor space sizes. These sizes  // are computed assuming the entire reserved space is committed.  // These values are exported as performance counters.  uintx alignment = gch->collector_policy()->space_alignment();  uintx size = _virtual_space.reserved_size();  _max_survivor_size = compute_survivor_size(size, alignment);  _max_eden_size = size - (2*_max_survivor_size);  // allocate the performance counters  GenCollectorPolicy* gcp = gch->gen_policy();  // Generation counters -- generation 0, 3 subspaces  _gen_counters = new GenerationCounters("new", 0, 3,      gcp->min_young_size(), gcp->max_young_size(), &_virtual_space);  _gc_counters = new CollectorCounters(policy, 0);  _eden_counters = new CSpaceCounters("eden", 0, _max_eden_size, _eden_space,                                      _gen_counters);  _from_counters = new CSpaceCounters("s0", 1, _max_survivor_size, _from_space,                                      _gen_counters);  _to_counters = new CSpaceCounters("s1", 2, _max_survivor_size, _to_space,                                    _gen_counters);  compute_space_boundaries(0, SpaceDecorator::Clear, SpaceDecorator::Mangle);  update_counters();  _old_gen = NULL;  _tenuring_threshold = MaxTenuringThreshold;  _pretenure_size_threshold_words = PretenureSizeThreshold >> LogHeapWordSize;  _gc_timer = new (ResourceObj::C_HEAP, mtGC) STWGCTimer();}

在这里插入图片描述

​ 我们可以看到这里又讲年轻代分为了3段即_eden_space_from_space_to_space,然后还有一个就是记数,用于代的递升,可以看到这里晋升到tenuring space是为15,还有_eden_counters(“eden”)、_from_counters(“s0”)、_to_counters(“s1”)。

以上就是我们从JVM的角度来具体分析的对堆的代的划分。

3)、预留的空间(VirtualSpace)

​ 承接上面,在通过new DefNewGeneration(rs, init_size())的构造方法调用的时候,其也进行了对应预留空间的处理,通过_virtual_space.initialize(rs, initial_size)

在这里插入图片描述

class Generation: public CHeapObj
{ friend class VMStructs; private: jlong _time_of_last_gc; // time when last gc on this generation happened (ms) MemRegion _prev_used_region; // for collectors that want to "remember" a value for // used region at some specific point during collection. protected: // Minimum and maximum addresses for memory reserved (not necessarily // committed) for generation. // Used by card marking code. Must not overlap with address ranges of // other generations. MemRegion _reserved; // Memory area reserved for generation VirtualSpace _virtual_space;
class DefNewGeneration: public Generation {

四、元空间的申请分配(Metaspace)

jint universe_init() {  ...........  jint status = Universe::initialize_heap();  if (status != JNI_OK) {    return status;  }  Metaspace::global_initialize();	......  return JNI_OK;}

前面我们主要是梳理了堆空间内存的申请与代的划分。下面我们来看下元空间的申请过程。

1、metaspace.cpp - global_initialize()

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);  if (DumpSharedSpaces) {     ............#endif // _LP64#endif // INCLUDE_CDS  } else {        ........#ifdef _LP64    if (!UseSharedSpaces && using_class_space()) {      char* base = (char*)align_ptr_up(Universe::heap()->reserved_region().end(), _reserve_alignment);      allocate_metaspace_compressed_klass_ptrs(base, 0);    }#endif // _LP64     .........  _tracer = new MetaspaceTracer();}
static CollectedHeap* heap() { return _collectedHeap; }

​ 这里的内存申请先是获取前面堆内存的最后地址(Universe::heap()->reserved_region().end()),来作为整个原空间的开始(_base)地址,然后通过allocate_metaspace_compressed_klass_ptrs(base, 0),进行空间的申请

2、metaspace.cpp - allocate_metaspace_compressed_klass_ptrs()

void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, address cds_base) {	......  // Don't use large pages for the class space.  bool large_pages = false;#ifndef AARCH64  ReservedSpace metaspace_rs = ReservedSpace(compressed_class_space_size(),                                             _reserve_alignment,                                             large_pages,                                             requested_addr);#else // AARCH64  ReservedSpace metaspace_rs;  // Our compressed klass pointers may fit nicely into the lower 32  // bits.  if ((uint64_t)requested_addr + compressed_class_space_size() < 4*G) {    metaspace_rs = ReservedSpace(compressed_class_space_size(),                                             _reserve_alignment,                                             large_pages,                                             requested_addr);  }	......  initialize_class_space(metaspace_rs);	........}
ReservedSpace::ReservedSpace(size_t size, size_t alignment,                             bool large,                             char* requested_address) {  initialize(size, alignment, large, requested_address, false);}

​ 这里就是ReservedSpace的构造方法调用initialize方法进行内存申请。

void ReservedSpace::initialize(size_t size, size_t alignment, bool large,                               char* requested_address,                               bool executable) { 	........  _base = NULL;  _size = 0;  _special = false;  _executable = executable;  _alignment = 0;  _noaccess_prefix = 0;  if (size == 0) {    return;  }	.......  if (base == NULL) {    if (requested_address != 0) {      base = os::attempt_reserve_memory_at(size, requested_address);      if (failed_to_reserve_as_requested(base, requested_address, size, false)) {        // OS ignored requested address. Try different address.        base = NULL;      }    } else {      base = os::reserve_memory(size, NULL, alignment);    }	.......  // Done  _base = base;  _size = size;  _alignment = alignment;}

可以看到现在requested_address不是像前面堆内存申请的时候传的NULL了,唐山市由于requested_address已经有值了,所以这里就是base = os::attempt_reserve_memory_at(size, requested_address)方法的调用。

char* os::attempt_reserve_memory_at(size_t bytes, char* addr) {  char* result = pd_attempt_reserve_memory_at(bytes, addr);  if (result != NULL) {    MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);  }  return result;}
char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) {  const int max_tries = 10;  char* base[max_tries];  size_t size[max_tries];  const size_t gap = 0x000000;	...........  char * addr = anon_mmap(requested_addr, bytes, false);  if (addr == requested_addr) {    return requested_addr;  }	..........  if (i < max_tries) {    return requested_addr;  } else {    return NULL;  }}
static char* anon_mmap(char* requested_addr, size_t bytes, bool fixed) {  char * addr;  int flags;		..........  // Map reserved/uncommitted pages PROT_NONE so we fail early if we  // touch an uncommitted page. Otherwise, the read/write might  // succeed if we have enough swap space to back the physical page.  addr = (char*)::mmap(requested_addr, bytes, PROT_NONE,                       flags, -1, 0);  return addr == MAP_FAILED ? NULL : addr;}

​ 然后再通过anon_mmap(requested_addr, bytes, false)去调用linux方法mmap申请内存。所以通过上面的整体过程,我们可以知道,在内存上面,堆内存与元空间内存在逻辑上面是连续首尾相接的。

以上就是堆内存与元空间内存申请的分析。

上一篇:JVM篇-oop-klass模型对类的描叙及类加载&实例化内存申请过程
下一篇:MySQL技术内幕InnoDB存储引擎阅读相关笔记-事务

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2025年03月27日 18时33分55秒