##java大对象数组,计算260000长度引用类型数组大概占用1M左右空间
import java.util.ArrayList;
import java.util.List;public class OutOfMemoryTest {public static void main(String[] args) {List<MiBigObject> miBigObjects = new ArrayList<>(260000);int length=0;while(true) {MiBigObject miBigObject = new MiBigObject();miBigObjects.add(miBigObject);System.out.println(length++);}}}public class MiBigObject {
}
##C++分配大对象数组内存GDB堆栈
#0 G1CollectedHeap::attempt_allocation_humongous (this=0x7ffff00453d0, word_size=130002) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/gc/g1/g1CollectedHeap.cpp:826
#1 0x00007ffff61d65ee in G1CollectedHeap::mem_allocate (this=0x7ffff00453d0, word_size=130002, gc_overhead_limit_was_exceeded=0x7ffff7bfe570)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/gc/g1/g1CollectedHeap.cpp:372
#2 0x00007ffff6779bf8 in MemAllocator::allocate_outside_tlab (this=0x7ffff7bfe5d0, allocation=...)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/gc/shared/memAllocator.cpp:258
#3 0x00007ffff677a023 in MemAllocator::mem_allocate (this=0x7ffff7bfe5d0, allocation=...) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/gc/shared/memAllocator.cpp:358
#4 0x00007ffff677a068 in MemAllocator::allocate (this=0x7ffff7bfe5d0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/gc/shared/memAllocator.cpp:365
#5 0x00007ffff6019669 in CollectedHeap::array_allocate (this=0x7ffff00453d0, klass=0x100058458, size=130002, length=260000, do_zero=true, __the_thread__=0x7ffff0028920)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/gc/shared/collectedHeap.inline.hpp:41
#6 0x00007ffff6344a7c in InstanceKlass::allocate_objArray (this=0x100041040, n=1, length=260000, __the_thread__=0x7ffff0028920)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/oops/instanceKlass.cpp:1349
#7 0x00007ffff685c14e in oopFactory::new_objArray (klass=0x100041040, length=260000, __the_thread__=0x7ffff0028920)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/memory/oopFactory.cpp:122
#8 0x00007ffff6368710 in InterpreterRuntime::anewarray (current=0x7ffff0028920, pool=0x7fffd99038f0, index=7, size=260000)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/interpreter/interpreterRuntime.cpp:266
#9 0x00007fffe1023e51 in ?? ()
#10 0x00007fffe1023dc2 in ?? ()
#11 0x00000000ffa167e0 in ?? ()
#12 0x00007ffff7bfe730 in ?? ()
#13 0x00007fffd9908ed2 in ?? ()
#14 0x00007ffff7bfe790 in ?? ()
#15 0x00007fffd9aced80 in ?? ()
#16 0x0000000000000000 in ?? ()
##C++源码
HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {ResourceMark rm; // For retrieving the thread names in log messages.// The structure of this method has a lot of similarities to// attempt_allocation_slow(). The reason these two were not merged// into a single one is that such a method would require several "if// allocation is not humongous do this, otherwise do that"// conditional paths which would obscure its flow. In fact, an early// version of this code did use a unified method which was harder to// follow and, as a result, it had subtle bugs that were hard to// track down. So keeping these two methods separate allows each to// be more readable. It will be good to keep these two in sync as// much as possible.assert_heap_not_locked_and_not_at_safepoint();assert(is_humongous(word_size), "attempt_allocation_humongous() ""should only be called for humongous allocations");// Humongous objects can exhaust the heap quickly, so we should check if we// need to start a marking cycle at each humongous object allocation. We do// the check before we do the actual allocation. The reason for doing it// before the allocation is that we avoid having to keep track of the newly// allocated memory while we do a GC.if (policy()->need_to_start_conc_mark("concurrent humongous allocation",word_size)) {collect(GCCause::_g1_humongous_allocation);}// We will loop until a) we manage to successfully perform the// allocation or b) we successfully schedule a collection which// fails to perform the allocation. b) is the only case when we'll// return NULL.HeapWord* result = NULL;for (uint try_count = 1, gclocker_retry_count = 0; /* we'll return */; try_count += 1) {bool should_try_gc;bool preventive_collection_required = false;uint gc_count_before;{MutexLocker x(Heap_lock);size_t size_in_regions = humongous_obj_size_in_regions(word_size);preventive_collection_required = policy()->preventive_collection_required((uint)size_in_regions);if (!preventive_collection_required) {// Given that humongous objects are not allocated in young// regions, we'll first try to do the allocation without doing a// collection hoping that there's enough space in the heap.result = humongous_obj_allocate(word_size);if (result != NULL) {policy()->old_gen_alloc_tracker()->add_allocated_humongous_bytes_since_last_gc(size_in_regions * HeapRegion::GrainBytes);return result;}}// Only try a GC if the GCLocker does not signal the need for a GC. Wait until// the GCLocker initiated GC has been performed and then retry. This includes// the case when the GC Locker is not active but has not been performed.should_try_gc = !GCLocker::needs_gc();// Read the GC count while still holding the Heap_lock.gc_count_before = total_collections();}if (should_try_gc) {GCCause::Cause gc_cause = preventive_collection_required ? GCCause::_g1_preventive_collection: GCCause::_g1_humongous_allocation;bool succeeded;result = do_collection_pause(word_size, gc_count_before, &succeeded, gc_cause);if (result != NULL) {assert(succeeded, "only way to get back a non-NULL result");log_trace(gc, alloc)("%s: Successfully scheduled collection returning " PTR_FORMAT,Thread::current()->name(), p2i(result));size_t size_in_regions = humongous_obj_size_in_regions(word_size);policy()->old_gen_alloc_tracker()->record_collection_pause_humongous_allocation(size_in_regions * HeapRegion::GrainBytes);return result;}if (succeeded) {// We successfully scheduled a collection which failed to allocate. No// point in trying to allocate further. We'll just return NULL.log_trace(gc, alloc)("%s: Successfully scheduled collection failing to allocate "SIZE_FORMAT " words", Thread::current()->name(), word_size);return NULL;}log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating " SIZE_FORMAT "",Thread::current()->name(), word_size);} else {// Failed to schedule a collection.if (gclocker_retry_count > GCLockerRetryAllocationCount) {log_warning(gc, alloc)("%s: Retried waiting for GCLocker too often allocating "SIZE_FORMAT " words", Thread::current()->name(), word_size);return NULL;}log_trace(gc, alloc)("%s: Stall until clear", Thread::current()->name());// The GCLocker is either active or the GCLocker initiated// GC has not yet been performed. Stall until it is and// then retry the allocation.GCLocker::stall_until_clear();gclocker_retry_count += 1;}// We can reach here if we were unsuccessful in scheduling a// collection (because another thread beat us to it) or if we were// stalled due to the GC locker. In either can we should retry the// allocation attempt in case another thread successfully// performed a collection and reclaimed enough space.// Humongous object allocation always needs a lock, so we wait for the retry// in the next iteration of the loop, unlike for the regular iteration case.// Give a warning if we seem to be looping forever.if ((QueuedAllocationWarningCount > 0) &&(try_count % QueuedAllocationWarningCount == 0)) {log_warning(gc, alloc)("%s: Retried allocation %u times for " SIZE_FORMAT " words",Thread::current()->name(), try_count, word_size);}}ShouldNotReachHere();return NULL;
}