堆内存分配

本文解析了GenCollectedHeap和GenCollectorPolicy类中的内存分配过程,重点讨论了新生代和老年代的内存分配策略,以及如何根据策略进行eden、tlab和大对象的内存分配,包括GC计数、内存扩展和超时处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆内存分配

genCollectedHeap.cpp:334

HeapWord* GenCollectedHeap::mem_allocate(size_t size,
	 bool* gc_overhead_limit_was_exceeded) {
  return collector_policy()->mem_allocate_work(size,
	   false /* is_tlab */,
	   gc_overhead_limit_was_exceeded);  // 根据策略来分配
}

GenCollectorPolicy::mem_allocate_work

HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size,
                                        bool is_tlab,
                                        bool* gc_overhead_limit_was_exceeded) {
  GenCollectedHeap *gch = GenCollectedHeap::heap();

  debug_only(gch->check_for_valid_allocation_state());
  assert(gch->no_gc_in_progress(), "Allocation during gc not allowed");

  // In general gc_overhead_limit_was_exceeded should be false so
  // set it so here and reset it to true only if the gc time
  // limit is being exceeded as checked below.
  *gc_overhead_limit_was_exceeded = false; // 

  HeapWord* result = NULL;

  // Loop until the allocation is satisified,
  // or unsatisfied after GC.  -------------start
  for (int try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) {
    HandleMark hm; // discard any handles allocated in each iteration

    // First allocation attempt is lock-free.
    Generation *gen0 = gch->get_gen(0); // 新生代
    assert(gen0->supports_inline_contig_alloc(),
      "Otherwise, must do alloc within heap lock");  // 这一步只会分配小对象
    if (gen0->should_allocate(size, is_tlab)) { // 新生代是否允许分配 DefNewGeneration::should_allocate:262  空间未满  && 字节数>0  && ( is_tlab=true || PretenureSizeThreshold非0 || 小对象)
      result = gen0->par_allocate(size, is_tlab); // DefNewGeneration::par_allocate;1074   eden()->par_allocate是关键,最后将由par_allocate_impl来实现
      if (result != NULL) { // 首先判断是否可以在Young区分配空间,如果可以,那么就在Young区域分配,如果分配成功了,那么就可以结束这次内存分配之旅了
        assert(gch->is_in_reserved(result), "result not in heap");
        return result;  // 先在eden分配
      }
    }
    unsigned int gc_count_before;  // read inside the Heap_lock locked region
    {
      MutexLocker ml(Heap_lock);
      if (PrintGC && Verbose) {
        gclog_or_tty->print_cr("TwoGenerationCollectorPolicy::mem_allocate_work:"
                      " attempting locked slow path allocation");
      }
      // Note that only large objects get a shot at being
      // allocated in later generations.  只有大对象可以被分配在老年代。一般情况下都是false, 所以first_only=true
      bool first_only = ! should_try_older_generation_allocation(size); // 这3种返回true:  新生代空间不够 || gc locker 被占用(jni 临界区)|| 最近发生过一次担保失败或者可能发生担保失败
      // 如果first_only = false 要进入老年代
      result = gch->attempt_allocation(size, is_tlab, first_only); // 在每个代尝试分配,first_only=true时只会在年轻代分配
      if (result != NULL) {
        assert(gch->is_in_reserved(result), "result not in heap");
        return result; // 大对象有可能在这里分配成功  这里对象分配不成功 下面要gc了
      }
      // Gc操作已被触发但还无法被执行,一般不会出现这种情况,只有在jni中jni_GetStringCritical等方法被调用时出现is_active_and_needs_gc=TRUE,主要是为了避免GC导致对象地址改变
      if (GC_locker::is_active_and_needs_gc()) {
        if (is_tlab) {
          return NULL;  // Caller will retry allocating individual object
        }
        if (!gch->is_maximal_no_gc()) { // 因为不能进行GC回收,所以只能尝试通过扩堆
          // Try and expand heap to satisfy request
          result = expand_heap_and_allocate(size, is_tlab);
          // result could be null if we are out of space
          if (result != NULL) {
            return result;
          }
        }

        if (gclocker_stalled_count > GCLockerRetryAllocationCount) { // 默认GCLockerRetryAllocationCount=2
          return NULL; // we didn't get to do a GC and we didn't get any memory
        }

        // If this thread is not in a jni critical section, we stall
        // the requestor until the critical section has cleared and
        // GC allowed. When the critical section clears, a GC is
        // initiated by the last thread exiting the critical section; so
        // we retry the allocation sequence from the beginning of the loop,
        // rather than causing more, now probably unnecessary, GC attempts.
        JavaThread* jthr = JavaThread::current();
        if (!jthr->in_critical()) {
          MutexUnlocker mul(Heap_lock);
          // Wait for JNI critical section to be exited
          GC_locker::stall_until_clear();
          gclocker_stalled_count += 1;
          continue;
        } else {
          if (CheckJNICalls) {
            fatal("Possible deadlock due to allocating while"
                  " in jni critical section");
          }
          return NULL;
        }
      }

      // Read the gc count while the heap lock is held.
      gc_count_before = Universe::heap()->total_collections();
    }
    // VM操作进行一次由分配失败触发的GC
    VM_GenCollectForAllocation op(size, is_tlab, gc_count_before);
    VMThread::execute(&op);
    if (op.prologue_succeeded()) {  //一次GC操作已完成
      result = op.result();
      if (op.gc_locked()) { // 当前线程没有成功触发GC(可能刚被其它线程触发了),则继续重试分配
         assert(result == NULL, "must be NULL if gc_locked() is true");
         continue;  // retry and/or stall as necessary
      }

      // Allocation has failed and a collection
      // has been done.  If the gc time limit was exceeded the
      // this time, return NULL so that an out-of-memory
      // will be thrown.  Clear gc_overhead_limit_exceeded
      // so that the overhead exceeded does not persist.
        // 分配失败且已经完成GC了,则判断是否超时等信息
      const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();
      const bool softrefs_clear = all_soft_refs_clear();

      if (limit_exceeded && softrefs_clear) {
        *gc_overhead_limit_was_exceeded = true;
        size_policy()->set_gc_overhead_limit_exceeded(false);
        if (op.result() != NULL) {
          CollectedHeap::fill_with_object(op.result(), size);
        }
        return NULL;
      }
      assert(result == NULL || gch->is_in_reserved(result),
             "result not in heap");
      return result;
    }

    // Give a warning if we seem to be looping forever.
    if ((QueuedAllocationWarningCount > 0) &&
        (try_count % QueuedAllocationWarningCount == 0)) {
          warning("TwoGenerationCollectorPolicy::mem_allocate_work retries %d times \n\t"
                  " size=" SIZE_FORMAT " %s", try_count, size, is_tlab ? "(TLAB)" : "");
    }
  }// -------------end
}

DefNewGeneration::par_allocate

defNewGeneration.cpp:1074

HeapWord* DefNewGeneration::par_allocate(size_t word_size,
                                         bool is_tlab) {
  HeapWord* res = eden()->par_allocate(word_size); // todo eden分配
  if (CMSEdenChunksRecordAlways && _next_gen != NULL) {
    _next_gen->sample_eden_chunk();
  }
  return res;
}

EdenSpace

class EdenSpace : public ContiguousSpace { }

EdenSpace::par_allocate

// Lock-free.
HeapWord* EdenSpace::par_allocate(size_t size) {
  return par_allocate_impl(size, soft_end());
}

ContiguousSpace::par_allocate_impl

// This version is lock-free.  todo 统一调用ContiguousSpace (Eden也是调用这个)
inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size,
                                                    HeapWord* const end_value) {
  do {
    HeapWord* obj = top();
    if (pointer_delta(end_value, obj) >= size) { // 是否有足够的空间
      HeapWord* new_top = obj + size;
      HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj); // cas操作  分配内存
      // result can be one of two:
      //  the old top value: the exchange succeeded
      //  otherwise: the new value of the top is returned.
      if (result == obj) {
        assert(is_aligned(obj) && is_aligned(new_top), "checking alignment");
        return obj;
      }
    } else {
      return NULL;
    }
  } while (true);
}

GenCollectorPolicy::should_try_older_generation_allocation

collectorPolicy.cpp:948

bool GenCollectorPolicy::should_try_older_generation_allocation(
        size_t word_size) const {
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  size_t gen0_capacity = gch->get_gen(0)->capacity_before_gc();
  return    (word_size > heap_word_size(gen0_capacity)) // 新生代空间不够  要分配的大小 > 年轻代容量(eden+from总大小) eden()->capacity() + from()->capacity();
         || GC_locker::is_active_and_needs_gc()  // gc locker 被占用(jni 临界区)
         || gch->incremental_collection_failed(); // 最近发生过一次担保失败或者可能发生担保失败
}

GenCollectedHeap::attempt_allocation

//依次尝试从内存堆的各内存代中分配内存空间  first_only=true: 只在年青代尝试分配   first_only=false: 先在年青代尝试分配,分配失败则再在旧生代中尝试分配
HeapWord* GenCollectedHeap::attempt_allocation(size_t size,
                                               bool is_tlab,
                                               bool first_only) {
  HeapWord* res; // first_only=false进入老年代
  for (int i = 0; i < _n_gens; i++) { //0:新生代  1:老年代
    if (_gens[i]->should_allocate(size, is_tlab)) { // 如果是大对象 新生代肯定是false
      res = _gens[i]->allocate(size, is_tlab);
      if (res != NULL) return res;
      else if (first_only) break;
    }
  }
  // Otherwise...
  return NULL;
}

GenCollectedHeap::is_maximal_no_gc()

  for (int i = 0; i < _n_gens; i++) {
    if (!_gens[i]->is_maximal_no_gc()) {
      return false;
    }
  }
  return true;
}

expand_and_allocate

collectorPolicy.cpp:773

HeapWord* GenCollectorPolicy::expand_heap_and_allocate(size_t size,
                                                       bool   is_tlab) {
  GenCollectedHeap *gch = GenCollectedHeap::heap();
  HeapWord* result = NULL;
  for (int i = number_of_generations() - 1; i >= 0 && result == NULL; i--) {
    Generation *gen = gch->get_gen(i);
    if (gen->should_allocate(size, is_tlab)) {
      result = gen->expand_and_allocate(size, is_tlab);
    }
  }
  assert(result == NULL || gch->is_in_reserved(result), "result not in heap");
  return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值