--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Mon Oct 13 16:05:46 2014 +0200
@@ -131,7 +131,10 @@
storage->set_mapping_changed_listener(&_listener);
}
-void CMBitMapMappingChangedListener::on_commit(uint start_region, size_t num_regions) {
+void CMBitMapMappingChangedListener::on_commit(uint start_region, size_t num_regions, bool zero_filled) {
+ if (zero_filled) {
+ return;
+ }
// We need to clear the bitmap on commit, removing any existing information.
MemRegion mr(G1CollectedHeap::heap()->bottom_addr_for_region(start_region), num_regions * HeapRegion::GrainWords);
_bm->clearRange(mr);
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Mon Oct 13 16:05:46 2014 +0200
@@ -127,7 +127,7 @@
void set_bitmap(CMBitMap* bm) { _bm = bm; }
- virtual void on_commit(uint start_idx, size_t num_regions);
+ virtual void on_commit(uint start_idx, size_t num_regions, bool zero_filled);
};
class CMBitMap : public CMBitMapRO {
--- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp Mon Oct 13 16:05:46 2014 +0200
@@ -109,7 +109,7 @@
class G1BlockOffsetSharedArrayMappingChangedListener : public G1MappingChangedListener {
public:
- virtual void on_commit(uint start_idx, size_t num_regions) {
+ virtual void on_commit(uint start_idx, size_t num_regions, bool zero_filled) {
// Nothing to do. The BOT is hard-wired to be part of the HeapRegion, and we cannot
// retrieve it here since this would cause firing of several asserts. The code
// executed after commit of a region already needs to do some re-initialization of
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.cpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.cpp Mon Oct 13 16:05:46 2014 +0200
@@ -33,7 +33,10 @@
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
-void G1CardCountsMappingChangedListener::on_commit(uint start_idx, size_t num_regions) {
+void G1CardCountsMappingChangedListener::on_commit(uint start_idx, size_t num_regions, bool zero_filled) {
+ if (zero_filled) {
+ return;
+ }
MemRegion mr(G1CollectedHeap::heap()->bottom_addr_for_region(start_idx), num_regions * HeapRegion::GrainWords);
_counts->clear_range(mr);
}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.hpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.hpp Mon Oct 13 16:05:46 2014 +0200
@@ -42,7 +42,7 @@
public:
void set_cardcounts(G1CardCounts* counts) { _counts = counts; }
- virtual void on_commit(uint start_idx, size_t num_regions);
+ virtual void on_commit(uint start_idx, size_t num_regions, bool zero_filled);
};
// Table to track the number of times a card has been refined. Once
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Mon Oct 13 16:05:46 2014 +0200
@@ -389,7 +389,9 @@
OtherRegionsTable::invalidate(start_idx, num_regions);
}
-void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_regions) {
+void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_regions, bool zero_filled) {
+ // The from card cache is not the memory that is actually committed. So we cannot
+ // take advantage of the zero_filled parameter.
reset_from_card_cache(start_idx, num_regions);
}
@@ -3610,7 +3612,7 @@
cl.candidate_humongous());
_has_humongous_reclaim_candidates = cl.candidate_humongous() > 0;
- if (_has_humongous_reclaim_candidates) {
+ if (_has_humongous_reclaim_candidates || G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
clear_humongous_is_live_table();
}
}
@@ -6272,9 +6274,10 @@
g1h->humongous_region_is_always_live(region_idx)) {
if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
- gclog_or_tty->print_cr("Live humongous %d region %d with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
+ gclog_or_tty->print_cr("Live humongous %d region %d size "SIZE_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
r->is_humongous(),
region_idx,
+ obj->size()*HeapWordSize,
r->rem_set()->occupied(),
r->rem_set()->strong_code_roots_list_length(),
next_bitmap->isMarked(r->bottom()),
@@ -6291,8 +6294,9 @@
r->bottom()));
if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) {
- gclog_or_tty->print_cr("Reclaim humongous region %d start "PTR_FORMAT" region %d length "UINT32_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
+ gclog_or_tty->print_cr("Reclaim humongous region %d size "SIZE_FORMAT" start "PTR_FORMAT" region %d length "UINT32_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d",
r->is_humongous(),
+ obj->size()*HeapWordSize,
r->bottom(),
region_idx,
r->region_num(),
@@ -6331,7 +6335,8 @@
void G1CollectedHeap::eagerly_reclaim_humongous_regions() {
assert_at_safepoint(true);
- if (!G1ReclaimDeadHumongousObjectsAtYoungGC || !_has_humongous_reclaim_candidates) {
+ if (!G1ReclaimDeadHumongousObjectsAtYoungGC ||
+ (!_has_humongous_reclaim_candidates && !G1TraceReclaimDeadHumongousObjectsAtYoungGC)) {
g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms(0.0, 0);
return;
}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Mon Oct 13 16:05:46 2014 +0200
@@ -172,7 +172,7 @@
private:
void reset_from_card_cache(uint start_idx, size_t num_regions);
public:
- virtual void on_commit(uint start_idx, size_t num_regions);
+ virtual void on_commit(uint start_idx, size_t num_regions, bool zero_filled);
};
class G1CollectedHeap : public SharedHeap {
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.cpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.cpp Mon Oct 13 16:05:46 2014 +0200
@@ -69,7 +69,7 @@
virtual void commit_regions(uintptr_t start_idx, size_t num_regions) {
_storage.commit(start_idx * _pages_per_region, num_regions * _pages_per_region);
_commit_map.set_range(start_idx, start_idx + num_regions);
- fire_on_commit(start_idx, num_regions);
+ fire_on_commit(start_idx, num_regions, true);
}
virtual void uncommit_regions(uintptr_t start_idx, size_t num_regions) {
@@ -115,12 +115,14 @@
assert(!_commit_map.at(i), err_msg("Trying to commit storage at region "INTPTR_FORMAT" that is already committed", i));
uintptr_t idx = region_idx_to_page_idx(i);
uint old_refcount = _refcounts.get_by_index(idx);
+ bool zero_filled = false;
if (old_refcount == 0) {
_storage.commit(idx, 1);
+ zero_filled = true;
}
_refcounts.set_by_index(idx, old_refcount + 1);
_commit_map.set_bit(i);
- fire_on_commit(i, 1);
+ fire_on_commit(i, 1, zero_filled);
}
}
@@ -139,9 +141,9 @@
}
};
-void G1RegionToSpaceMapper::fire_on_commit(uint start_idx, size_t num_regions) {
+void G1RegionToSpaceMapper::fire_on_commit(uint start_idx, size_t num_regions, bool zero_filled) {
if (_listener != NULL) {
- _listener->on_commit(start_idx, num_regions);
+ _listener->on_commit(start_idx, num_regions, zero_filled);
}
}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.hpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.hpp Mon Oct 13 16:05:46 2014 +0200
@@ -33,7 +33,9 @@
public:
// Fired after commit of the memory, i.e. the memory this listener is registered
// for can be accessed.
- virtual void on_commit(uint start_idx, size_t num_regions) = 0;
+ // Zero_filled indicates that the memory can be considered as filled with zero bytes
+ // when called.
+ virtual void on_commit(uint start_idx, size_t num_regions, bool zero_filled) = 0;
};
// Maps region based commit/uncommit requests to the underlying page sized virtual
@@ -51,7 +53,7 @@
G1RegionToSpaceMapper(ReservedSpace rs, size_t commit_granularity, size_t region_granularity, MemoryType type);
- void fire_on_commit(uint start_idx, size_t num_regions);
+ void fire_on_commit(uint start_idx, size_t num_regions, bool zero_filled);
public:
MemRegion reserved() { return _storage.reserved(); }
--- a/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp Mon Oct 13 16:05:46 2014 +0200
@@ -125,7 +125,8 @@
}
#endif
-void G1SATBCardTableLoggingModRefBSChangedListener::on_commit(uint start_idx, size_t num_regions) {
+void G1SATBCardTableLoggingModRefBSChangedListener::on_commit(uint start_idx, size_t num_regions, bool zero_filled) {
+ // Default value for a clean card on the card table is -1. So we cannot take advantage of the zero_filled parameter.
MemRegion mr(G1CollectedHeap::heap()->bottom_addr_for_region(start_idx), num_regions * HeapRegion::GrainWords);
_card_table->clear(mr);
}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp Mon Oct 13 16:05:46 2014 +0200
@@ -136,7 +136,7 @@
void set_card_table(G1SATBCardTableLoggingModRefBS* card_table) { _card_table = card_table; }
- virtual void on_commit(uint start_idx, size_t num_regions);
+ virtual void on_commit(uint start_idx, size_t num_regions, bool zero_filled);
};
// Adds card-table logging to the post-barrier.
--- a/hotspot/src/share/vm/opto/graphKit.cpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/opto/graphKit.cpp Mon Oct 13 16:05:46 2014 +0200
@@ -3826,6 +3826,110 @@
// Final sync IdealKit and GraphKit.
final_sync(ideal);
}
+/*
+ * Determine if the G1 pre-barrier can be removed. The pre-barrier is
+ * required by SATB to make sure all objects live at the start of the
+ * marking are kept alive, all reference updates need to any previous
+ * reference stored before writing.
+ *
+ * If the previous value is NULL there is no need to save the old value.
+ * References that are NULL are filtered during runtime by the barrier
+ * code to avoid unnecessary queuing.
+ *
+ * However in the case of newly allocated objects it might be possible to
+ * prove that the reference about to be overwritten is NULL during compile
+ * time and avoid adding the barrier code completely.
+ *
+ * The compiler needs to determine that the object in which a field is about
+ * to be written is newly allocated, and that no prior store to the same field
+ * has happened since the allocation.
+ *
+ * Returns true if the pre-barrier can be removed
+ */
+bool GraphKit::g1_can_remove_pre_barrier(PhaseTransform* phase, Node* adr,
+ BasicType bt, uint adr_idx) {
+ intptr_t offset = 0;
+ Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset);
+ AllocateNode* alloc = AllocateNode::Ideal_allocation(base, phase);
+
+ if (offset == Type::OffsetBot) {
+ return false; // cannot unalias unless there are precise offsets
+ }
+
+ if (alloc == NULL) {
+ return false; // No allocation found
+ }
+
+ intptr_t size_in_bytes = type2aelembytes(bt);
+
+ Node* mem = memory(adr_idx); // start searching here...
+
+ for (int cnt = 0; cnt < 50; cnt++) {
+
+ if (mem->is_Store()) {
+
+ Node* st_adr = mem->in(MemNode::Address);
+ intptr_t st_offset = 0;
+ Node* st_base = AddPNode::Ideal_base_and_offset(st_adr, phase, st_offset);
+
+ if (st_base == NULL) {
+ break; // inscrutable pointer
+ }
+
+ // Break we have found a store with same base and offset as ours so break
+ if (st_base == base && st_offset == offset) {
+ break;
+ }
+
+ if (st_offset != offset && st_offset != Type::OffsetBot) {
+ const int MAX_STORE = BytesPerLong;
+ if (st_offset >= offset + size_in_bytes ||
+ st_offset <= offset - MAX_STORE ||
+ st_offset <= offset - mem->as_Store()->memory_size()) {
+ // Success: The offsets are provably independent.
+ // (You may ask, why not just test st_offset != offset and be done?
+ // The answer is that stores of different sizes can co-exist
+ // in the same sequence of RawMem effects. We sometimes initialize
+ // a whole 'tile' of array elements with a single jint or jlong.)
+ mem = mem->in(MemNode::Memory);
+ continue; // advance through independent store memory
+ }
+ }
+
+ if (st_base != base
+ && MemNode::detect_ptr_independence(base, alloc, st_base,
+ AllocateNode::Ideal_allocation(st_base, phase),
+ phase)) {
+ // Success: The bases are provably independent.
+ mem = mem->in(MemNode::Memory);
+ continue; // advance through independent store memory
+ }
+ } else if (mem->is_Proj() && mem->in(0)->is_Initialize()) {
+
+ InitializeNode* st_init = mem->in(0)->as_Initialize();
+ AllocateNode* st_alloc = st_init->allocation();
+
+ // Make sure that we are looking at the same allocation site.
+ // The alloc variable is guaranteed to not be null here from earlier check.
+ if (alloc == st_alloc) {
+ // Check that the initialization is storing NULL so that no previous store
+ // has been moved up and directly write a reference
+ Node* captured_store = st_init->find_captured_store(offset,
+ type2aelembytes(T_OBJECT),
+ phase);
+ if (captured_store == NULL || captured_store == st_init->zero_memory()) {
+ return true;
+ }
+ }
+ }
+
+ // Unless there is an explicit 'continue', we must bail out here,
+ // because 'mem' is an inscrutable memory state (e.g., a call).
+ break;
+ }
+
+ return false;
+}
// G1 pre/post barriers
void GraphKit::g1_write_barrier_pre(bool do_load,
@@ -3846,6 +3950,12 @@
assert(adr != NULL, "where are loading from?");
assert(pre_val == NULL, "loaded already?");
assert(val_type != NULL, "need a type");
+
+ if (use_ReduceInitialCardMarks()
+ && g1_can_remove_pre_barrier(&_gvn, adr, bt, alias_idx)) {
+ return;
+ }
+
} else {
// In this case both val_type and alias_idx are unused.
assert(pre_val != NULL, "must be loaded already");
@@ -3927,6 +4037,65 @@
final_sync(ideal);
}
+/*
+ * G1 similar to any GC with a Young Generation requires a way to keep track of
+ * references from Old Generation to Young Generation to make sure all live
+ * objects are found. G1 also requires to keep track of object references
+ * between different regions to enable evacuation of old regions, which is done
+ * as part of mixed collections. References are tracked in remembered sets and
+ * is continuously updated as reference are written to with the help of the
+ * post-barrier.
+ *
+ * To reduce the number of updates to the remembered set the post-barrier
+ * filters updates to fields in objects located in the Young Generation,
+ * the same region as the reference, when the NULL is being written or
+ * if the card is already marked as dirty by an earlier write.
+ *
+ * Under certain circumstances it is possible to avoid generating the
+ * post-barrier completely if it is possible during compile time to prove
+ * the object is newly allocated and that no safepoint exists between the
+ * allocation and the store.
+ *
+ * In the case of slow allocation the allocation code must handle the barrier
+ * as part of the allocation in the case the allocated object is not located
+ * in the nursery, this would happen for humongous objects. This is similar to
+ * how CMS is required to handle this case, see the comments for the method
+ * CollectedHeap::new_store_pre_barrier and OptoRuntime::new_store_pre_barrier.
+ * A deferred card mark is required for these objects and handled in the above
+ * mentioned methods.
+ *
+ * Returns true if the post barrier can be removed
+ */
+bool GraphKit::g1_can_remove_post_barrier(PhaseTransform* phase, Node* store,
+ Node* adr) {
+ intptr_t offset = 0;
+ Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset);
+ AllocateNode* alloc = AllocateNode::Ideal_allocation(base, phase);
+
+ if (offset == Type::OffsetBot) {
+ return false; // cannot unalias unless there are precise offsets
+ }
+
+ if (alloc == NULL) {
+ return false; // No allocation found
+ }
+
+ // Start search from Store node
+ Node* mem = store->in(MemNode::Control);
+ if (mem->is_Proj() && mem->in(0)->is_Initialize()) {
+
+ InitializeNode* st_init = mem->in(0)->as_Initialize();
+ AllocateNode* st_alloc = st_init->allocation();
+
+ // Make sure we are looking at the same allocation
+ if (alloc == st_alloc) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
//
// Update the card table and add card address to the queue
//
@@ -3979,6 +4148,20 @@
return;
}
+ if (use_ReduceInitialCardMarks() && obj == just_allocated_object(control())) {
+ // We can skip marks on a freshly-allocated object in Eden.
+ // Keep this code in sync with new_store_pre_barrier() in runtime.cpp.
+ // That routine informs GC to take appropriate compensating steps,
+ // upon a slow-path allocation, so as to make this card-mark
+ // elision safe.
+ return;
+ }
+
+ if (use_ReduceInitialCardMarks()
+ && g1_can_remove_post_barrier(&_gvn, oop_store, adr)) {
+ return;
+ }
+
if (!use_precise) {
// All card marks for a (non-array) instance are in one place:
adr = obj;
--- a/hotspot/src/share/vm/opto/graphKit.hpp Fri Oct 10 02:24:35 2014 -0700
+++ b/hotspot/src/share/vm/opto/graphKit.hpp Mon Oct 13 16:05:46 2014 +0200
@@ -771,6 +771,10 @@
Node* index, Node* index_adr,
Node* buffer, const TypeFunc* tf);
+ bool g1_can_remove_pre_barrier(PhaseTransform* phase, Node* adr, BasicType bt, uint adr_idx);
+
+ bool g1_can_remove_post_barrier(PhaseTransform* phase, Node* store, Node* adr);
+
public:
// Helper function to round double arguments before a call
void round_double_arguments(ciMethod* dest_method);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/TestG1TraceReclaimDeadHumongousObjectsAtYoungGC.java Mon Oct 13 16:05:46 2014 +0200
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test TestG1TraceReclaimDeadHumongousObjectsAtYoungGC
+ * @bug 8058801
+ * @summary Ensure that the output for a G1TraceReclaimDeadHumongousObjectsAtYoungGC
+ * includes the expected necessary messages.
+ * @key gc
+ * @library /testlibrary
+ */
+
+import com.oracle.java.testlibrary.ProcessTools;
+import com.oracle.java.testlibrary.OutputAnalyzer;
+import java.util.LinkedList;
+
+public class TestG1TraceReclaimDeadHumongousObjectsAtYoungGC {
+ public static void main(String[] args) throws Exception {
+ testGCLogs();
+ testHumongousObjectGCLogs();
+ }
+
+ private static void testGCLogs() throws Exception {
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
+ "-Xms128M",
+ "-Xmx128M",
+ "-Xmn16M",
+ "-XX:G1HeapRegionSize=1M",
+ "-XX:+PrintGC",
+ "-XX:+UnlockExperimentalVMOptions",
+ "-XX:G1LogLevel=finest",
+ "-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC",
+ GCTest.class.getName());
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ // As G1ReclaimDeadHumongousObjectsAtYoungGC is set(default), below logs should be displayed.
+ // And GCTest doesn't have humongous objects, so values should be zero.
+ output.shouldContain("[Humongous Reclaim");
+ output.shouldContain("[Humongous Total: 0]");
+ output.shouldContain("[Humongous Candidate: 0]");
+ output.shouldContain("[Humongous Reclaimed: 0]");
+
+ output.shouldHaveExitValue(0);
+ }
+
+ private static void testHumongousObjectGCLogs() throws Exception {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
+ "-Xms128M",
+ "-Xmx128M",
+ "-Xmn16M",
+ "-XX:G1HeapRegionSize=1M",
+ "-XX:+PrintGC",
+ "-XX:+UnlockExperimentalVMOptions",
+ "-XX:G1LogLevel=finest",
+ "-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC",
+ GCWithHumongousObjectTest.class.getName());
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ // As G1ReclaimDeadHumongousObjectsAtYoungGC is set(default), below logs should be displayed.
+ output.shouldContain("[Humongous Reclaim");
+ output.shouldContain("[Humongous Total");
+ output.shouldContain("[Humongous Candidate");
+ output.shouldContain("[Humongous Reclaimed");
+
+ // As G1TraceReclaimDeadHumongousObjectsAtYoungGC is set and GCWithHumongousObjectTest has humongous objects,
+ // these logs should be displayed.
+ output.shouldContain("Live humongous");
+ output.shouldContain("Reclaim humongous region");
+ output.shouldHaveExitValue(0);
+ }
+
+ static class GCTest {
+ private static byte[] garbage;
+
+ public static void main(String [] args) {
+ System.out.println("Creating garbage");
+ // create 128MB of garbage. This should result in at least one GC
+ for (int i = 0; i < 1024; i++) {
+ garbage = new byte[128 * 1024];
+ }
+ System.out.println("Done");
+ }
+ }
+
+ static class GCWithHumongousObjectTest {
+
+ public static final int M = 1024*1024;
+ public static LinkedList<Object> garbageList = new LinkedList<Object>();
+ // A large object referenced by a static.
+ static int[] filler = new int[10 * M];
+
+ public static void genGarbage() {
+ for (int i = 0; i < 32*1024; i++) {
+ garbageList.add(new int[100]);
+ }
+ garbageList.clear();
+ }
+
+ public static void main(String[] args) {
+
+ int[] large = new int[M];
+ Object ref = large;
+
+ System.out.println("Creating garbage");
+ for (int i = 0; i < 100; i++) {
+ // A large object that will be reclaimed eagerly.
+ large = new int[6*M];
+ genGarbage();
+ // Make sure that the compiler cannot completely remove
+ // the allocation of the large object until here.
+ System.out.println(large);
+ }
+
+ // Keep the reference to the first object alive.
+ System.out.println(ref);
+ System.out.println("Done");
+ }
+ }
+}