今天来看看HotSpotVM的Java堆初始化。

Universe

Java堆的初始化主要由Universe模块来完毕,来看下Universe模块初始化的代码,universe_init

jint universe_init() {
  assert(!Universe::_fully_initialized, "called after initialize_vtables");
  guarantee(1 << LogHeapWordSize == sizeof(HeapWord),
         "LogHeapWordSize is incorrect.");
  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);
  GC_locker::lock();  // do not allow gc during bootstrapping
  JavaClasses::compute_hard_coded_offsets();

  // Get map info from shared archive file.
  if (DumpSharedSpaces)
    UseSharedSpaces = false;

  FileMapInfo* mapinfo = NULL;
  if (UseSharedSpaces) {
    mapinfo = NEW_C_HEAP_OBJ(FileMapInfo, mtInternal);
    memset(mapinfo, 0, sizeof(FileMapInfo));

    // Open the shared archive file, read and validate the header. If
    // initialization files, shared spaces [UseSharedSpaces] are
    // disabled and the file is closed.

    if (mapinfo->initialize()) {
      FileMapInfo::set_current_info(mapinfo);
    } else {
      assert(!mapinfo->is_open() && !UseSharedSpaces,
             "archive file not closed or shared spaces not disabled.");
    }
  }

  ////////////////////////////////////////
  // initialize_heap()
  ////////////////////////////////////////
  jint status = Universe::initialize_heap();
  if (status != JNI_OK) {
    return status;
  }

  // We have a heap so create the methodOop caches before
  // CompactingPermGenGen::initialize_oops() tries to populate them.
  Universe::_finalizer_register_cache = new LatestMethodOopCache();
  Universe::_loader_addClass_cache    = new LatestMethodOopCache();
  Universe::_pd_implies_cache         = new LatestMethodOopCache();
  Universe::_reflect_invoke_cache     = new ActiveMethodOopsCache();

  if (UseSharedSpaces) {

    // Read the data structures supporting the shared spaces (shared
    // system dictionary, symbol table, etc.).  After that, access to
    // the file (other than the mapped regions) is no longer needed, and
    // the file is closed. Closing the file does not affect the
    // currently mapped regions.

    CompactingPermGenGen::initialize_oops();
    mapinfo->close();

  } else {
    SymbolTable::create_table();
    StringTable::create_table();
    ClassLoader::create_package_info_table();
  }

  return JNI_OK;
}

主要就是initialize_heap方法

jint Universe::initialize_heap() {

  ////////////////////////////////////////
  // -XX:+UseParallelGC
  ////////////////////////////////////////
  if (UseParallelGC) {
#ifndef SERIALGC
    Universe::_collectedHeap = new ParallelScavengeHeap();
#else  // SERIALGC
    fatal("UseParallelGC not supported in java kernel vm.");
#endif // SERIALGC

  ////////////////////////////////////////
  // -XX:+UseG1GC
  ////////////////////////////////////////
  } else if (UseG1GC) {
#ifndef SERIALGC
    G1CollectorPolicy* g1p = new G1CollectorPolicy();
    G1CollectedHeap* g1h = new G1CollectedHeap(g1p);
    Universe::_collectedHeap = g1h;
#else  // SERIALGC
    fatal("UseG1GC not supported in java kernel vm.");
#endif // SERIALGC

  } else {
    GenCollectorPolicy *gc_policy;

    ////////////////////////////////////////
    // -XX:+UseSerialGC
    ////////////////////////////////////////
    if (UseSerialGC) {
      gc_policy = new MarkSweepPolicy();

    ////////////////////////////////////////
    // -XX:+UseConcMarkSweepGC
    ////////////////////////////////////////
    } else if (UseConcMarkSweepGC) {
#ifndef SERIALGC
      if (UseAdaptiveSizePolicy) {
        gc_policy = new ASConcurrentMarkSweepPolicy();
      } else {
        gc_policy = new ConcurrentMarkSweepPolicy();
      }
#else   // SERIALGC
    fatal("UseConcMarkSweepGC not supported in java kernel vm.");
#endif // SERIALGC
    } else { // default old generation
      gc_policy = new MarkSweepPolicy();
    }

    Universe::_collectedHeap = new GenCollectedHeap(gc_policy);
  }

  ////////////////////////////////////////
  // 堆的初始化
  ////////////////////////////////////////
  jint status = Universe::heap()->initialize();
  if (status != JNI_OK) {
    return status;
  }

#ifdef _LP64
  if (UseCompressedOops) {
    // Subtract a page because something can get allocated at heap base.
    // This also makes implicit null checking work, because the
    // memory+1 page below heap_base needs to cause a signal.
    // See needs_explicit_null_check.
    // Only set the heap base for compressed oops because it indicates
    // compressed oops for pstack code.
    bool verbose = PrintCompressedOopsMode || (PrintMiscellaneous && Verbose);
    if (verbose) {
      tty->cr();
      tty->print("heap address: " PTR_FORMAT ", size: " SIZE_FORMAT " MB",
                 Universe::heap()->base(), Universe::heap()->reserved_region().byte_size()/M);
    }
    if ((uint64_t)Universe::heap()->reserved_region().end() > OopEncodingHeapMax) {
      // Can't reserve heap below 32Gb.
      Universe::set_narrow_oop_base(Universe::heap()->base() - os::vm_page_size());
      Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes);
      if (verbose) {
        tty->print(", %s: "PTR_FORMAT,
            narrow_oop_mode_to_string(HeapBasedNarrowOop),
            Universe::narrow_oop_base());
      }
    } else {
      Universe::set_narrow_oop_base(0);
      if (verbose) {
        tty->print(", %s", narrow_oop_mode_to_string(ZeroBasedNarrowOop));
      }
#ifdef _WIN64
      if (!Universe::narrow_oop_use_implicit_null_checks()) {
        // Don't need guard page for implicit checks in indexed addressing
        // mode with zero based Compressed Oops.
        Universe::set_narrow_oop_use_implicit_null_checks(true);
      }
#endif //  _WIN64
      if((uint64_t)Universe::heap()->reserved_region().end() > NarrowOopHeapMax) {
        // Can't reserve heap below 4Gb.
        Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes);
      } else {
        Universe::set_narrow_oop_shift(0);
        if (verbose) {
          tty->print(", %s", narrow_oop_mode_to_string(UnscaledNarrowOop));
        }
      }
    }
    if (verbose) {
      tty->cr();
      tty->cr();
    }
  }
  assert(Universe::narrow_oop_base() == (Universe::heap()->base() - os::vm_page_size()) ||
         Universe::narrow_oop_base() == NULL, "invalid value");
  assert(Universe::narrow_oop_shift() == LogMinObjAlignmentInBytes ||
         Universe::narrow_oop_shift() == 0, "invalid value");
#endif

  // We will never reach the CATCH below since Exceptions::_throw will cause
  // the VM to exit if an exception is thrown during initialization

  ////////////////////////////////////////
  // -XX:+UseTLAB
  ////////////////////////////////////////
  if (UseTLAB) {
    assert(Universe::heap()->supports_tlab_allocation(),
           "Should support thread-local allocation buffers");
    ThreadLocalAllocBuffer::startup_initialization();
  }
  return JNI_OK;
}

主要就是依据各个GC相关的option使用不同类型的CollectedHeap,也就是ParallelScavengeHeapG1CollectedHeapGenCollectedHeap。不同类型的CollectedHeap有不同的CollectorPolicy

// CollectedHeap
//   SharedHeap
//     GenCollectedHeap
//     G1CollectedHeap
//   ParallelScavengeHeap

然后初始化所选的CollectedHeapTLAB

以下我们来看看GenCollectedHeap的初始化。

先看下使用CMS时採用的CollectorPolicy,也就是ConcurrentMarkSweepPolicy

ConcurrentMarkSweepPolicy

看下构造函数

ConcurrentMarkSweepPolicy::ConcurrentMarkSweepPolicy() {
  initialize_all();
}

initialize_all

  virtual void initialize_all() {
    initialize_flags();
    initialize_size_info();
    initialize_generations();
  }

ConcurrentMarkSweepPolicy继承自TwoGenerationCollectorPolicyTwoGenerationCollectorPolicy继承自GenCollectorPolicyGenCollectorPolicy继承自CollectorPolicy

TwoGenerationCollectorPolicy中有这么一段凝视,

// All of hotspot's current collectors are subtypes of this
// class. Currently, these collectors all use the same gen[0],
// but have different gen[1] types. If we add another subtype
// of CollectorPolicy, this class should be broken out into
// its own file.

上面所说的gen[0]就是YoungGen。而gen[1]OldGen。也就是说当前HotSpot所採用的GC算法中,YoungGen都是一样的。

initialize_all

如今来看看initialize_all方法都做了啥。首先是initialize_flags。调用的层次是这种,

//3. TwoGenerationCollectorPolicy::initialize_flags
//2.   =>GenCollectorPolicy::initialize_flags
//1.     =>CollectorPolicy::initialize_flags

initialize_flags方法用来调整命令行标记所指定的GC相关參数的大小,各自是:

  1. PermGen的大小(PermSizeMaxPermSize)
  2. YoungGen的大小(NewSizeMaxNewSize)
  3. OldGen的大小(OldSize)和MaxHeapSize(-Xmx)

然后是initialize_size_info方法。

//3. TwoGenerationCollectorPolicy::initialize_size_info
//2.   =>GenCollectorPolicy::initialize_size_info
//1.     =>CollectorPolicy::initialize_size_info

initialize_size_info方法依据调整好的标记设置变量大小,各自是:

  1. _initial_heap_byte_size_min_heap_byte_size_max_heap_byte_size
  2. _initial_gen0_size_min_gen0_size_max_gen0_size
  3. _initial_gen1_size_min_gen1_size_max_gen1_size

最后是initialize_generations方法

void ConcurrentMarkSweepPolicy::initialize_generations() {
  initialize_perm_generation(PermGen::ConcurrentMarkSweep);
  _generations = new GenerationSpecPtr[number_of_generations()];
  if (_generations == NULL)
    vm_exit_during_initialization("Unable to allocate gen spec");

  if (ParNewGeneration::in_use()) {
    if (UseAdaptiveSizePolicy) {
      _generations[0] = new GenerationSpec(Generation::ASParNew,
                                           _initial_gen0_size, _max_gen0_size);
    } else {
      _generations[0] = new GenerationSpec(Generation::ParNew,
                                           _initial_gen0_size, _max_gen0_size);
    }
  } else {
    _generations[0] = new GenerationSpec(Generation::DefNew,
                                         _initial_gen0_size, _max_gen0_size);
  }
  if (UseAdaptiveSizePolicy) {
    _generations[1] = new GenerationSpec(Generation::ASConcurrentMarkSweep,
                            _initial_gen1_size, _max_gen1_size);
  } else {
    _generations[1] = new GenerationSpec(Generation::ConcurrentMarkSweep,
                            _initial_gen1_size, _max_gen1_size);
  }

  if (_generations[0] == NULL || _generations[1] == NULL) {
    vm_exit_during_initialization("Unable to allocate gen spec");
  }
}

GenCollectedHeap

接下来能够看下GenCollectedHeap的初始化了。GenCollectedHeap::initialize

jint GenCollectedHeap::initialize() {
  CollectedHeap::pre_initialize();

  int i;
  _n_gens = gen_policy()->number_of_generations();

  // While there are no constraints in the GC code that HeapWordSize
  // be any particular value, there are multiple other areas in the
  // system which believe this to be true (e.g. oop->object_size in some
  // cases incorrectly returns the size in wordSize units rather than
  // HeapWordSize).
  guarantee(HeapWordSize == wordSize, "HeapWordSize must equal wordSize");

  // The heap must be at least as aligned as generations.
  size_t gen_alignment = Generation::GenGrain;

  _gen_specs = gen_policy()->generations();
  PermanentGenerationSpec *perm_gen_spec =
                                collector_policy()->permanent_generation();

  size_t heap_alignment = collector_policy()->max_alignment();

  // Make sure the sizes are all aligned.
  for (i = 0; i < _n_gens; i++) {
    _gen_specs[i]->align(gen_alignment);
  }
  perm_gen_spec->align(heap_alignment);

  // If we are dumping the heap, then allocate a wasted block of address
  // space in order to push the heap to a lower address.  This extra
  // address range allows for other (or larger) libraries to be loaded
  // without them occupying the space required for the shared spaces.

  if (DumpSharedSpaces) {
    uintx reserved = 0;
    uintx block_size = 64*1024*1024;
    while (reserved < SharedDummyBlockSize) {
      char* dummy = os::reserve_memory(block_size);
      reserved += block_size;
    }
  }

  ////////////////////////////////////////
  // 開始内存分配
  ////////////////////////////////////////
  // Allocate space for the heap.

  char* heap_address;
  size_t total_reserved = 0;
  int n_covered_regions = 0;
  ReservedSpace heap_rs;

  heap_address = allocate(heap_alignment, perm_gen_spec, &total_reserved,
                          &n_covered_regions, &heap_rs);

  if (UseSharedSpaces) {
    if (!heap_rs.is_reserved() || heap_address != heap_rs.base()) {
      if (heap_rs.is_reserved()) {
        heap_rs.release();
      }
      FileMapInfo* mapinfo = FileMapInfo::current_info();
      mapinfo->fail_continue("Unable to reserve shared region.");
      allocate(heap_alignment, perm_gen_spec, &total_reserved, &n_covered_regions,
               &heap_rs);
    }
  }

  if (!heap_rs.is_reserved()) {
    vm_shutdown_during_initialization(
      "Could not reserve enough space for object heap");
    return JNI_ENOMEM;
  }

  _reserved = MemRegion((HeapWord*)heap_rs.base(),
                        (HeapWord*)(heap_rs.base() + heap_rs.size()));

  // It is important to do this in a way such that concurrent readers can't
  // temporarily think somethings in the heap.  (Seen this happen in asserts.)
  _reserved.set_word_size(0);
  _reserved.set_start((HeapWord*)heap_rs.base());
  size_t actual_heap_size = heap_rs.size() - perm_gen_spec->misc_data_size()
                                           - perm_gen_spec->misc_code_size();
  _reserved.set_end((HeapWord*)(heap_rs.base() + actual_heap_size));

  _rem_set = collector_policy()->create_rem_set(_reserved, n_covered_regions);
  set_barrier_set(rem_set()->bs());

  _gch = this;

  for (i = 0; i < _n_gens; i++) {
    ReservedSpace this_rs = heap_rs.first_part(_gen_specs[i]->max_size(),
                                              UseSharedSpaces, UseSharedSpaces);
    _gens[i] = _gen_specs[i]->init(this_rs, i, rem_set());
    // tag generations in JavaHeap
    MemTracker::record_virtual_memory_type((address)this_rs.base(), mtJavaHeap);
    heap_rs = heap_rs.last_part(_gen_specs[i]->max_size());
  }
  _perm_gen = perm_gen_spec->init(heap_rs, PermSize, rem_set());
  // tag PermGen
  MemTracker::record_virtual_memory_type((address)heap_rs.base(), mtJavaHeap);

  clear_incremental_collection_failed();

#ifndef SERIALGC
  // If we are running CMS, create the collector responsible
  // for collecting the CMS generations.
  if (collector_policy()->is_concurrent_mark_sweep_policy()) {
    bool success = create_cms_collector();
    if (!success) return JNI_ENOMEM;
  }
#endif // SERIALGC

  return JNI_OK;
}

真正的内存分配动作由allocate方法完毕,

char* GenCollectedHeap::allocate(size_t alignment,
                                 PermanentGenerationSpec* perm_gen_spec,
                                 size_t* _total_reserved,
                                 int* _n_covered_regions,
                                 ReservedSpace* heap_rs){
  // Now figure out the total size.
  size_t total_reserved = 0;
  int n_covered_regions = 0;
  const size_t pageSize = UseLargePages ?

os::large_page_size() : os::vm_page_size(); assert(alignment % pageSize == 0, "Must be"); for (int i = 0; i < _n_gens; i++) { total_reserved = add_and_check_overflow(total_reserved, _gen_specs[i]->max_size()); n_covered_regions += _gen_specs[i]->n_covered_regions(); } assert(total_reserved % alignment == 0, err_msg("Gen size; total_reserved=" SIZE_FORMAT ", alignment=" SIZE_FORMAT, total_reserved, alignment)); total_reserved = add_and_check_overflow(total_reserved, perm_gen_spec->max_size()); assert(total_reserved % alignment == 0, err_msg("Perm size; total_reserved=" SIZE_FORMAT ", alignment=" SIZE_FORMAT ", perm gen max=" SIZE_FORMAT, total_reserved, alignment, perm_gen_spec->max_size())); n_covered_regions += perm_gen_spec->n_covered_regions(); // Add the size of the data area which shares the same reserved area // as the heap, but which is not actually part of the heap. size_t misc = perm_gen_spec->misc_data_size() + perm_gen_spec->misc_code_size(); total_reserved = add_and_check_overflow(total_reserved, misc); if (UseLargePages) { assert(misc == 0, "CDS does not support Large Pages"); assert(total_reserved != 0, "total_reserved cannot be 0"); assert(is_size_aligned(total_reserved, os::large_page_size()), "Must be"); total_reserved = round_up_and_check_overflow(total_reserved, os::large_page_size()); } // Calculate the address at which the heap must reside in order for // the shared data to be at the required address. char* heap_address; if (UseSharedSpaces) { // Calculate the address of the first word beyond the heap. FileMapInfo* mapinfo = FileMapInfo::current_info(); int lr = CompactingPermGenGen::n_regions - 1; size_t capacity = align_size_up(mapinfo->space_capacity(lr), alignment); heap_address = mapinfo->region_base(lr) + capacity; // Calculate the address of the first word of the heap. heap_address -= total_reserved; } else { heap_address = NULL; // any address will do. if (UseCompressedOops) { heap_address = Universe::preferred_heap_base(total_reserved, alignment, Universe::UnscaledNarrowOop); *_total_reserved = total_reserved; *_n_covered_regions = n_covered_regions; *heap_rs = ReservedHeapSpace(total_reserved, alignment, UseLargePages, heap_address); if (heap_address != NULL && !heap_rs->is_reserved()) { // Failed to reserve at specified address - the requested memory // region is taken already, for example, by 'java' launcher. // Try again to reserver heap higher. heap_address = Universe::preferred_heap_base(total_reserved, alignment, Universe::ZeroBasedNarrowOop); *heap_rs = ReservedHeapSpace(total_reserved, alignment, UseLargePages, heap_address); if (heap_address != NULL && !heap_rs->is_reserved()) { // Failed to reserve at specified address again - give up. heap_address = Universe::preferred_heap_base(total_reserved, alignment, Universe::HeapBasedNarrowOop); assert(heap_address == NULL, ""); *heap_rs = ReservedHeapSpace(total_reserved, alignment, UseLargePages, heap_address); } } return heap_address; } } *_total_reserved = total_reserved; *_n_covered_regions = n_covered_regions; *heap_rs = ReservedHeapSpace(total_reserved, alignment, UseLargePages, heap_address); return heap_address; }

主要是通过构造ReservedHeapSpace来完毕。

ReservedHeapSpace

ReservedHeapSpace继承自ReservedSpaceReservedSpace的构造函数会调用initialize方法,而initialize方法终于会调用os::reserve_memory来申请内存。

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, mtNone, CALLER_PC);
  }
  return result;
}

Linux下的pd_reserve_memory

char* os::pd_reserve_memory(size_t bytes, char* requested_addr,
                         size_t alignment_hint) {
  return anon_mmap(requested_addr, bytes, (requested_addr != NULL));
}
// If 'fixed' is true, anon_mmap() will attempt to reserve anonymous memory
// at 'requested_addr'. If there are existing memory mappings at the same
// location, however, they will be overwritten. If 'fixed' is false,
// 'requested_addr' is only treated as a hint, the return value may or
// may not start from the requested address. Unlike Linux mmap(), this
// function returns NULL to indicate failure.
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 uncommitted pages PROT_READ and PROT_WRITE, change access
  // to PROT_EXEC if executable when we commit the page.
  addr = (char*)::mmap(requested_addr, bytes, PROT_READ|PROT_WRITE,
                       flags, -1, 0);

  if (addr != MAP_FAILED) {
    // anon_mmap() should only get called during VM initialization,
    // don't need lock (actually we can skip locking even it can be called
    // from multiple threads, because _highest_vm_reserved_address is just a
    // hint about the upper limit of non-stack memory regions.)
    if ((address)addr + bytes > _highest_vm_reserved_address) {
      _highest_vm_reserved_address = (address)addr + bytes;
    }
  }

  return addr == MAP_FAILED ? NULL : addr;
}

所以最后底层事实上是通过调用anonymous的mmap来申请了内存。

參考资料