内存堆管理器GenCollectedHeap的初始化

     前文在介绍Java对象内存分配的上层接口CollectedHeap时就提过, GenCollectedHeap是一种基于内存分代管理的内存堆管理器实现. 它一方面负责java对象的内存分配, 另一方面还得负责垃圾对象的回收, 而GC策略CollectorPolicy则是它的核心组件.这里,内存堆管理器GenCollectedHeap配置的GC策略实现是GenCollectorPolicy的子类, 目前主要的实现包括: MarkSweepPolicy/ASConcurrentMarkSweepPolicy/ConcurrentMarkSweepPolicy. 当然, 本文只会详细的介绍GenCollectedHeap的初始化, 至于它是如何给Java对象分配内存, 又是如何回收垃圾对象的, 笔者将会在后面的博文中娓娓道来.内存堆管理器GenCollectedHeap的初始化主要包括垃圾回收线程的创建,内存堆内存的申请,以及各内存代管理器的创建和内存分配.



内存堆管理器GenCollectedHeap的初始化代码如下:

/**
 * 初始化内存堆
 */
jint GenCollectedHeap::initialize() {

  printf("%s[%d] [tid: %lu]: 开始初始化内存堆...\n", __FILE__, __LINE__, pthread_self());

  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 alignment = Generation::GenGrain;

  //各内存代管理器的生成器
  _gen_specs = gen_policy()->generations();
  //永久代管理器的生成器
  PermanentGenerationSpec *perm_gen_spec = collector_policy()->permanent_generation();

  //对齐各内存代的初始大小及最大大小
  for (i = 0; i < _n_gens; i++) {
    _gen_specs[i]->align(alignment);
  }
  perm_gen_spec->align(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(0);

  //为内存堆申请空间
  heap_address = allocate(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();
      //当前JVM放弃jar共享
      mapinfo->fail_continue("Unable to reserve shared region.");
      allocate(alignment, perm_gen_spec, &total_reserved, &n_covered_regions, &heap_rs);
    }
  }

  if (!heap_rs.is_reserved()) {//没有申请到足够的内存空间,则终止JVM
    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++) {
	printf("%s[%d] [tid: %lu]: 开始从内存堆中为内存代[%d]划分%lu bytes的总内存空间...\n", __FILE__, __LINE__, pthread_self(), i, _gen_specs[i]->max_size());
    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());
    heap_rs = heap_rs.last_part(_gen_specs[i]->max_size());
  }
  printf("%s[%d] [tid: %lu]: 开始从内存堆中为永久代划分%lu bytes的总内存空间...\n", __FILE__, __LINE__, pthread_self(), PermSize);
  _perm_gen = perm_gen_spec->init(heap_rs, PermSize, rem_set());

  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()) { //CMS Gc策略
    bool success = create_cms_collector();
    if (!success) return JNI_ENOMEM;
  }
#endif // SERIALGC

  return JNI_OK;
}

1.GC线程的创建

     当我们用JVM的工具jstack来查询某一个java进程的所有线程状态的时候, jstack除了dump出Java级线程(java程序创建的)外, 还会打印出JVM级线程,如垃圾回收线程. 当以默认的方式启动JVM时,它的垃圾回收线程.就是:

"GC task thread#0 (ParallelGC)" prio=10 tid=0x000000004bcea000 nid=0x38f2 runnable 

"GC task thread#1 (ParallelGC)" prio=10 tid=0x000000004bceb800 nid=0x38f3 runnable 

"GC task thread#2 (ParallelGC)" prio=10 tid=0x000000004bced800 nid=0x38f4 runnable 

"GC task thread#3 (ParallelGC)" prio=10 tid=0x000000004bcef800 nid=0x38f5 runnable 

"GC task thread#4 (ParallelGC)" prio=10 tid=0x000000004bcf1000 nid=0x38f6 runnable 

"GC task thread#5 (ParallelGC)" prio=10 tid=0x000000004bcf3000 nid=0x38f7 runnable 

"GC task thread#6 (ParallelGC)" prio=10 tid=0x000000004bcf5000 nid=0x38f8 runnable 

"GC task thread#7 (ParallelGC)" prio=10 tid=0x000000004bcf6800 nid=0x38f9 runnable 

"GC task thread#8 (ParallelGC)" prio=10 tid=0x000000004bcf8800 nid=0x38fa runnable 

"GC task thread#9 (ParallelGC)" prio=10 tid=0x000000004bcfa800 nid=0x38fb runnable 

"GC task thread#10 (ParallelGC)" prio=10 tid=0x000000004bcfc000 nid=0x38fc runnable 

"GC task thread#11 (ParallelGC)" prio=10 tid=0x000000004bcfe000 nid=0x38fd runnable 

"GC task thread#12 (ParallelGC)" prio=10 tid=0x000000004bd00000 nid=0x38fe runnable
    垃圾回收线程就是用来做GC用的,它由SharedHeap来创建,之前说过GenCollectedHeap是SharedHeap的子类. SharedHeap创建GC线程的流程如下:

SharedHeap::SharedHeap(CollectorPolicy* policy_) :
  CollectedHeap(),
  _collector_policy(policy_),
  _perm_gen(NULL), _rem_set(NULL),
  _strong_roots_parity(0),
  _process_strong_tasks(new SubTasksDone(SH_PS_NumElements)),
  _workers(NULL)
{
  if (_process_strong_tasks == NULL || !_process_strong_tasks->valid()) {
    vm_exit_during_initialization("Failed necessary allocation.");
  }

  _sh = this;  // ch is static, should be set only once.

  if ((UseParNewGC || (UseConcMarkSweepGC && CMSParallelRemarkEnabled) || UseG1GC)
		  && ParallelGCThreads > 0) {

	printf("%s[%d] [tid: %lu]: 试图创建Gc线程管理调度器(Gc线程数量=%lu)...\n", __FILE__, __LINE__, pthread_self(), ParallelGCThreads);

    _workers = new FlexibleWorkGang("Parallel GC Threads", ParallelGCThreads,
                            /* are_GC_task_threads */true,
                            /* are_ConcurrentGC_threads */false);

    if (_workers == NULL) {
      printf("%s[%d] [tid: %lu]: 创建Gc线程管理调度器失败!", __FILE__, __LINE__, pthread_self());
      vm_exit_during_initialization("Failed necessary allocation.");
    } else {
      printf("%s[%d] [tid: %lu]: 试图初始化Gc线程管理调度器...\n", __FILE__, __LINE__, pthread_self());
      _workers->initialize_workers();
    }
  }
}
   GC线程的工作原理将会在介绍GC时进行详细的阐述, 而GC线程的数量主要是由配置参数 ParallelGCThreads决定的, 它的默认值是:

product(uintx, ParallelGCThreads, 0,  "Number of parallel threads parallel gc will use")
虽然 ParallelGCThreads的默认值为0,  不过JVM会根据当前的GC策略和系统cpu的数量来进行计算调整的, 其计算规则参见源代码:

unsigned int Abstract_VM_Version::nof_parallel_worker_threads(
                                                      unsigned int num,
                                                      unsigned int den,
                                                      unsigned int switch_pt) {
  if (FLAG_IS_DEFAULT(ParallelGCThreads)) {
    assert(ParallelGCThreads == 0, "Default ParallelGCThreads is not 0");
    // For very large machines, there are diminishing returns
    // for large numbers of worker threads.  Instead of
    // hogging the whole system, use a fraction of the workers for every
    // processor after the first 8.  For example, on a 72 cpu machine
    // and a chosen fraction of 5/8
    // use 8 + (72 - 8) * (5/8) == 48 worker threads.
    unsigned int ncpus = (unsigned int) os::active_processor_count();
    return (ncpus <= switch_pt) ?
           ncpus :
          (switch_pt + ((ncpus - switch_pt) * num) / den);
  } else {
    return ParallelGCThreads;
  }
}

unsigned int Abstract_VM_Version::calc_parallel_worker_threads() {
  return nof_parallel_worker_threads(5, 8, 8);
}


// Does not set the _initialized flag since it is
// a global flag.
unsigned int Abstract_VM_Version::parallel_worker_threads() {
  if (!_parallel_worker_threads_initialized) {
    if (FLAG_IS_DEFAULT(ParallelGCThreads)) {
      _parallel_worker_threads = VM_Version::calc_parallel_worker_threads();
    } else {
      _parallel_worker_threads = ParallelGCThreads;
    }
    _parallel_worker_threads_initialized = true;
  }

  return _parallel_worker_threads;
}

2.内存堆的内存申请

    内存堆管理器GenCollectedHeap在向操作系统申请整个堆内存的时候涉及到两个JVM参数DumpSharedSpacesUseSharedSpaces, 这两个参数不会同时有效.这里先简单介绍一下DumpSharedSpacesUseSharedSpaces这两个参数所代表的功能.

    当JVM启动时若配置-XX:+DumpSharedSpaces, 则它会去jdk的安装目录下寻找一个名为classlist的文件 (linux下它一般在*/jre/lib/classlist), 这个文件其实就是一个类列表, 它的每一行表示的是一个类的全路径名,JVM会加载每一个类(装载/解析/链接),然后把它们对应的类型描述信息dump到一个名为classes.jsa的文件中((linux下它一般在*/jre/lib/amd64/{client|server}/classes.jsa下),然后JVM退出

    当JVM启动时若配置-XX:+UseSharedSpaces,则它会通过内存映射文件的方式把classes.jsa文件的内存加载到自己的JVM进程空间中. classes.jsa对应的这一部分内存空间地址一般在永久代内存地址空间的后面. JVM这么做的目的就是让这个JVM的所有实例共享classlist中所有类的类型描述信息以达到节约物理内存的目标

/**
 * 给内存堆分配内存空间(新生代+旧生代+永久代)
 */
char* GenCollectedHeap::allocate(size_t alignment,
                                 PermanentGenerationSpec* perm_gen_spec,
                                 size_t* _total_reserved,
                                 int* _n_covered_regions,
                                 ReservedSpace* heap_rs){
  const char overflow_msg[] = "The size of the object heap + VM data exceeds the maximum representable size";

  printf("%s[%d] [tid: %lu]: 开始为内存堆申请内存空间...\n", __FILE__, __LINE__, pthread_self());

  // 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();

  //通过新生代及旧生代来计算内存堆的总大小及总分区数
  for (int i = 0; i < _n_gens; i++) {
    total_reserved += _gen_specs[i]->max_size();
    if (total_reserved < _gen_specs[i]->max_size()) {
      vm_exit_during_initialization(overflow_msg);
    }
    n_covered_regions += _gen_specs[i]->n_covered_regions();
  }

  //确保内存堆的大小是内存页的整数倍
  assert(total_reserved % pageSize == 0,
         err_msg("Gen size; total_reserved=" SIZE_FORMAT ", pageSize="
        		 SIZE_FORMAT, total_reserved, pageSize));

  //通过永久代来计算内存堆的总大小及总分区数
  total_reserved += perm_gen_spec->max_size();
  assert(total_reserved % pageSize == 0,
         err_msg("Perm size; total_reserved=" SIZE_FORMAT ", pageSize="
                 SIZE_FORMAT ", perm gen max=" SIZE_FORMAT, total_reserved,
                 pageSize, perm_gen_spec->max_size()));

  if (total_reserved < perm_gen_spec->max_size()) {
    vm_exit_during_initialization(overflow_msg);
  }
  n_covered_regions += perm_gen_spec->n_covered_regions();

  // 内存堆还包括数据区和代码区
  size_t s = perm_gen_spec->misc_data_size() + perm_gen_spec->misc_code_size();
  total_reserved += s;
  if (total_reserved < s) {
    vm_exit_during_initialization(overflow_msg);
  }

  if (UseLargePages) {	//使用大内存页
    assert(total_reserved != 0, "total_reserved cannot be 0");
    //将内存堆的总大小向上调整为内存页大小的整数倍
    total_reserved = round_to(total_reserved, os::large_page_size());
    if (total_reserved < os::large_page_size()) {
      vm_exit_during_initialization(overflow_msg);
    }
  }

  // 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) {	//JVM共享jar包

    // 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, Universe::UnscaledNarrowOop);
      *_total_reserved = total_reserved;
      *_n_covered_regions = n_covered_regions;

      printf("%s[%d] [tid: %lu]: 开始为内存堆向操作系统申请大小为%lu bytes的总内存空间...\n", __FILE__, __LINE__, pthread_self(), total_reserved);
      *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, Universe::ZeroBasedNarrowOop);
        printf("%s[%d] [tid: %lu]: 开始为内存堆向操作系统申请大小为%lu bytes的内存空间...\n", __FILE__, __LINE__, pthread_self(), total_reserved);
        *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, Universe::HeapBasedNarrowOop);
          assert(heap_address == NULL, "");

          printf("%s[%d] [tid: %lu]: 开始为内存堆向操作系统申请大小为%lu bytes的内存空间...\n", __FILE__, __LINE__, pthread_self(), total_reserved);
          *heap_rs = ReservedHeapSpace(total_reserved, alignment, UseLargePages, heap_address);
        }
      }

      return heap_address;
    }
  }

  *_total_reserved = total_reserved;
  *_n_covered_regions = n_covered_regions;

  printf("%s[%d] [tid: %lu]: 开始为内存堆向操作系统申请大小为%lu bytes的内存空间...\n", __FILE__, __LINE__, pthread_self(), total_reserved);
  *heap_rs = ReservedHeapSpace(total_reserved, alignment, UseLargePages, heap_address);

  return heap_address;
}
 源代码中的ReservedHeapSpace实际上就是一种操作内存块的数据结构,它向操作系统申请内存的工作原理如下:

ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment,
                                     bool large, char* requested_address) :
  ReservedSpace(size, alignment, large,
                requested_address,
                (UseCompressedOops && (Universe::narrow_oop_base() != NULL) &&
                 Universe::narrow_oop_use_implicit_null_checks()) ?
                  lcm(os::vm_page_size(), alignment) : 0) {
  // Only reserved space for the java heap should have a noaccess_prefix
  // if using compressed oops.
  protect_noaccess_prefix(size);
}

ReservedSpace::ReservedSpace(size_t size, size_t alignment,
                             bool large,
                             char* requested_address,
                             const size_t noaccess_prefix) {
  initialize(size+noaccess_prefix, alignment, large, requested_address,  noaccess_prefix, false);
}

/**
 * 向操作系统预定内存空间
 */
void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
                               char* requested_address,
                               const size_t noaccess_prefix,
                               bool executable) {
  const size_t granularity = os::vm_allocation_granularity();
  assert((size & (granularity - 1)) == 0, "size not aligned to os::vm_allocation_granularity()");
  assert((alignment & (granularity - 1)) == 0, "alignment not aligned to os::vm_allocation_granularity()");
  assert(alignment == 0 || is_power_of_2((intptr_t)alignment), "not a power of 2");

  alignment = MAX2(alignment, (size_t)os::vm_page_size());

  // Assert that if noaccess_prefix is used, it is the same as alignment.
  assert(noaccess_prefix == 0 ||
         noaccess_prefix == alignment, "noaccess prefix wrong");

  _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 (requested_address != 0) {
    requested_address -= noaccess_prefix; // adjust requested address
    assert(requested_address != NULL, "huge noaccess prefix?");
  }

  if (special) {

	//向操作系统申请指定大小的内存,并映射到用户指定的内存空间中
    base = os::reserve_memory_special(size, requested_address, executable);

    if (base != NULL) {
      if (failed_to_reserve_as_requested(base, requested_address, size, true)) {
        // OS ignored requested address. Try different address.
        return;
      }
      // Check alignment constraints
      assert((uintptr_t) base % alignment == 0, "Large pages returned a non-aligned address");
      _special = true;
    } else {
      // failed; try to reserve regular memory below
      if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
                            !FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
        if (PrintCompressedOopsMode) {
          tty->cr();
          tty->print_cr("Reserve regular memory without large pages.");
        }
      }
    }
  }

  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) {
      printf("%s[%d] [tid: %lu]: 开始尝试向操作系统申请大小为%lu bytes的内存空间,并映射到指定的内存起始位置...\n", __FILE__, __LINE__, pthread_self(), size);
      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 {
      printf("%s[%d] [tid: %lu]: 开始向操作系统申请大小为%lu bytes的内存空间...\n", __FILE__, __LINE__, pthread_self(), size);
      base = os::reserve_memory(size, NULL, alignment);
    }

    if (base == NULL) return;

    // Check alignment constraints
    if ((((size_t)base + noaccess_prefix) & (alignment - 1)) != 0) {
      // Base not aligned, retry
      if (!os::release_memory(base, size)) fatal("os::release_memory failed");
      // Reserve size large enough to do manual alignment and
      // increase size to a multiple of the desired alignment
      size = align_size_up(size, alignment);
      size_t extra_size = size + alignment;
      do {
        char* extra_base = os::reserve_memory(extra_size, NULL, alignment);
        if (extra_base == NULL) return;

        // Do manual alignement
        base = (char*) align_size_up((uintptr_t) extra_base, alignment);
        assert(base >= extra_base, "just checking");
        // Re-reserve the region at the aligned base address.
        os::release_memory(extra_base, extra_size);
        base = os::reserve_memory(size, base);
      } while (base == NULL);

      if (requested_address != 0 &&
          failed_to_reserve_as_requested(base, requested_address, size, false)) {
        // As a result of the alignment constraints, the allocated base differs
        // from the requested address. Return back to the caller who can
        // take remedial action (like try again without a requested address).
        assert(_base == NULL, "should be");
        return;
      }
    }
  }

  // Done
  _base = base;
  _size = size;
  _alignment = alignment;
  _noaccess_prefix = noaccess_prefix;

  // Assert that if noaccess_prefix is used, it is the same as alignment.
  assert(noaccess_prefix == 0 ||
         noaccess_prefix == _alignment, "noaccess prefix wrong");

  assert(markOopDesc::encode_pointer_as_mark(_base)->decode_pointer() == _base,
         "area must be distinguisable from marks for mark-sweep");
  assert(markOopDesc::encode_pointer_as_mark(&_base[size])->decode_pointer() == &_base[size],
         "area must be distinguisable from marks for mark-sweep");
}

这里先不介绍操作系统是如何分配内存的细节,但众所周知,Java是一个号称跨平台的可移植性编程语言,JVM必定在每类操作系统上都有对应的实现,这可能还涉及到更底层的计算机体系架构.(下图是笔者对hotspot跨平台设计的初步勾勒)


3.内存代管理器的创建及内存分配

    内存堆管理器GenCollectedHeap从操作系统成功申请到了内存之后,就会把这些内存交给各内存代管理器去管理.GenCollectedHeap会根据GenCollectorPolicy创建的内存代管理器的生成器去创建对应的内存代管理器:

/**
 * 根据名字创建对应的内存代管理器(新生代+旧生代)
 */
Generation* GenerationSpec::init(ReservedSpace rs, int level,
                                 GenRemSet* remset) {
  switch (name()) {
    case Generation::DefNew:		//默认年青代内存管理器
      return new DefNewGeneration(rs, init_size(), level);

    case Generation::MarkSweepCompact:	//默认的旧生代内存管理器
      return new TenuredGeneration(rs, init_size(), level, remset);

#ifndef SERIALGC
    case Generation::ParNew:	//可并行GC的年青代内存管理器
      return new ParNewGeneration(rs, init_size(), level);

    case Generation::ASParNew:	//可调整大小及并行GC的年青代内存管理器
      return new ASParNewGeneration(rs, init_size(), init_size() /* min size */, level);

    case Generation::ConcurrentMarkSweep: {	//CMS的旧生代内存管理器
      assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set");
      CardTableRS* ctrs = remset->as_CardTableRS();
      if (ctrs == 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(), level, ctrs, UseCMSAdaptiveFreeLists,
                 (FreeBlockDictionary::DictionaryChoice)CMSDictionaryChoice);

      g->initialize_performance_counters();

      return g;
    }

    case Generation::ASConcurrentMarkSweep: {	//可调整大小及CMS的旧生代内存管理器
      assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set");
      CardTableRS* ctrs = remset->as_CardTableRS();
      if (ctrs == NULL) {
        vm_exit_during_initialization("Rem set incompatibility.");
      }
      // Otherwise
      // The constructor creates the CMSCollector if needed,
      // else registers with an existing CMSCollector

      ASConcurrentMarkSweepGeneration* g = NULL;
      g = new ASConcurrentMarkSweepGeneration(rs,
                 init_size(), level, ctrs, UseCMSAdaptiveFreeLists,
                 (FreeBlockDictionary::DictionaryChoice)CMSDictionaryChoice);

      g->initialize_performance_counters();

      return g;
    }
#endif // SERIALGC

    default:
      guarantee(false, "unrecognized GenerationName");
      return NULL;
  }
}

/**
 * 根据名字创建对应的内存代管理器(永久代)
 */
PermGen* PermanentGenerationSpec::init(ReservedSpace rs,
                                       size_t init_size,
                                       GenRemSet *remset) {

  // Break the reserved spaces into pieces for the permanent space
  // and the shared spaces.
  ReservedSpace perm_rs = rs.first_part(_max_size, UseSharedSpaces, UseSharedSpaces);
  ReservedSpace shared_rs = rs.last_part(_max_size);

  if (enable_shared_spaces()) {
    if (!perm_rs.is_reserved() ||
        perm_rs.base() + perm_rs.size() != shared_rs.base()) {
      FileMapInfo* mapinfo = FileMapInfo::current_info();
      mapinfo->fail_continue("Sharing disabled - unable to reserve address space.");
      shared_rs.release();
      disable_sharing();
    }
  }

  switch (name()) {
    case PermGen::MarkSweepCompact:
      return new CompactingPermGen(perm_rs, shared_rs, init_size, remset, this);

#ifndef SERIALGC
    case PermGen::MarkSweep:
      guarantee(false, "NYI");
      return NULL;

    case PermGen::ConcurrentMarkSweep: {
      assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set");
      CardTableRS* ctrs = remset->as_CardTableRS();
      if (ctrs == NULL) {
        vm_exit_during_initialization("RemSet/generation incompatibility.");
      }
      // XXXPERM
      return new CMSPermGen(perm_rs, init_size, ctrs,
                   (FreeBlockDictionary::DictionaryChoice)CMSDictionaryChoice);
    }
#endif // SERIALGC
    default:
      guarantee(false, "unrecognized GenerationName");
      return NULL;
  }
}
至于这些内存代管理器是如何工作的, 笔者将会在后面的博文中对这些内存代管理器的实现一一介绍.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值