--- old/src/hotspot/share/gc/g1/collectionSetChooser.cpp 2018-11-19 14:03:34.682689000 -0800 +++ new/src/hotspot/share/gc/g1/collectionSetChooser.cpp 2018-11-19 14:03:33.710506600 -0800 @@ -49,6 +49,14 @@ return -1; } + if (hr1->is_premature_old() && !hr2->is_premature_old()) { + return -1; + } + + if (hr2->is_premature_old() && !hr1->is_premature_old()) { + return 1; + } + double gc_eff1 = hr1->gc_efficiency(); double gc_eff2 = hr2->gc_efficiency(); if (gc_eff1 > gc_eff2) { @@ -283,7 +291,7 @@ bool CollectionSetChooser::should_add(HeapRegion* hr) const { return !hr->is_young() && !hr->is_pinned() && - region_occupancy_low_enough_for_evac(hr->live_bytes()) && + (hr->is_premature_old() || region_occupancy_low_enough_for_evac(hr->live_bytes())) && hr->rem_set()->is_complete(); } --- old/src/hotspot/share/gc/g1/g1Allocator.inline.hpp 2018-11-19 14:03:46.589158800 -0800 +++ new/src/hotspot/share/gc/g1/g1Allocator.inline.hpp 2018-11-19 14:03:45.605850100 -0800 @@ -97,7 +97,7 @@ } _archive_check_enabled = true; - size_t length = Universe::heap()->max_capacity(); + size_t length = G1CollectedHeap::heap()->max_reserved_capacity(); _closed_archive_region_map.initialize((HeapWord*)Universe::heap()->base(), (HeapWord*)Universe::heap()->base() + length, HeapRegion::GrainBytes); --- old/src/hotspot/share/gc/g1/g1Arguments.cpp 2018-11-19 14:03:58.053092000 -0800 +++ new/src/hotspot/share/gc/g1/g1Arguments.cpp 2018-11-19 14:03:57.093999100 -0800 @@ -147,5 +147,9 @@ } CollectedHeap* G1Arguments::create_heap() { + if(AllocateOldGenAt != NULL) { + return create_heap_with_policy(); + } return create_heap_with_policy(); + } --- old/src/hotspot/share/gc/g1/g1CardCounts.cpp 2018-11-19 14:04:09.769808400 -0800 +++ new/src/hotspot/share/gc/g1/g1CardCounts.cpp 2018-11-19 14:04:08.806007400 -0800 @@ -63,7 +63,7 @@ } void G1CardCounts::initialize(G1RegionToSpaceMapper* mapper) { - assert(_g1h->max_capacity() > 0, "initialization order"); + assert(_g1h->max_reserved_capacity() > 0, "initialization order"); assert(_g1h->capacity() == 0, "initialization order"); if (G1ConcRSHotCardLimit > 0) { --- old/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2018-11-19 14:04:21.286005100 -0800 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2018-11-19 14:04:20.330892000 -0800 @@ -166,7 +166,7 @@ "the only time we use this to allocate a humongous region is " "when we are allocating a single humongous region"); - HeapRegion* res = _hrm.allocate_free_region(is_old); + HeapRegion* res = _hrm->allocate_free_region(is_old); if (res == NULL && do_expand && _expand_heap_after_alloc_failure) { // Currently, only attempts to allocate GC alloc regions set @@ -183,7 +183,7 @@ // always expand the heap by an amount aligned to the heap // region size, the free list should in theory not be empty. // In either case allocate_free_region() will check for NULL. - res = _hrm.allocate_free_region(is_old); + res = _hrm->allocate_free_region(is_old); } else { _expand_heap_after_alloc_failure = false; } @@ -337,9 +337,9 @@ } else { // Policy: Try only empty regions (i.e. already committed first). Maybe we // are lucky enough to find some. - first = _hrm.find_contiguous_only_empty(obj_regions); + first = _hrm->find_contiguous_only_empty(obj_regions); if (first != G1_NO_HRM_INDEX) { - _hrm.allocate_free_regions_starting_at(first, obj_regions); + _hrm->allocate_free_regions_starting_at(first, obj_regions); } } @@ -347,14 +347,14 @@ // Policy: We could not find enough regions for the humongous object in the // free list. Look through the heap to find a mix of free and uncommitted regions. // If so, try expansion. - first = _hrm.find_contiguous_empty_or_unavailable(obj_regions); + first = _hrm->find_contiguous_empty_or_unavailable(obj_regions); if (first != G1_NO_HRM_INDEX) { // We found something. Make sure these regions are committed, i.e. expand // the heap. Alternatively we could do a defragmentation GC. log_debug(gc, ergo, heap)("Attempt heap expansion (humongous allocation request failed). Allocation request: " SIZE_FORMAT "B", word_size * HeapWordSize); - _hrm.expand_at(first, obj_regions, workers()); + _hrm->expand_at(first, obj_regions, workers()); g1_policy()->record_new_heap_size(num_regions()); #ifdef ASSERT @@ -365,7 +365,7 @@ assert(is_on_master_free_list(hr), "sanity"); } #endif - _hrm.allocate_free_regions_starting_at(first, obj_regions); + _hrm->allocate_free_regions_starting_at(first, obj_regions); } else { // Policy: Potentially trigger a defragmentation GC. } @@ -554,7 +554,7 @@ bool G1CollectedHeap::check_archive_addresses(MemRegion* ranges, size_t count) { assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); - MemRegion reserved = _hrm.reserved(); + MemRegion reserved = _hrm->reserved(); for (size_t i = 0; i < count; i++) { if (!reserved.contains(ranges[i].start()) || !reserved.contains(ranges[i].last())) { return false; @@ -571,7 +571,7 @@ assert(count != 0, "No MemRegions provided"); MutexLockerEx x(Heap_lock); - MemRegion reserved = _hrm.reserved(); + MemRegion reserved = _hrm->reserved(); HeapWord* prev_last_addr = NULL; HeapRegion* prev_last_region = NULL; @@ -605,7 +605,7 @@ // range ended, and adjust the start address so we don't try to allocate // the same region again. If the current range is entirely within that // region, skip it, just adjusting the recorded top. - HeapRegion* start_region = _hrm.addr_to_region(start_address); + HeapRegion* start_region = _hrm->addr_to_region(start_address); if ((prev_last_region != NULL) && (start_region == prev_last_region)) { start_address = start_region->end(); if (start_address > last_address) { @@ -615,12 +615,12 @@ } start_region->set_top(start_address); curr_range = MemRegion(start_address, last_address + 1); - start_region = _hrm.addr_to_region(start_address); + start_region = _hrm->addr_to_region(start_address); } // Perform the actual region allocation, exiting if it fails. // Then note how much new space we have allocated. - if (!_hrm.allocate_containing_regions(curr_range, &commits, workers())) { + if (!_hrm->allocate_containing_regions(curr_range, &commits, workers())) { return false; } increase_used(word_size * HeapWordSize); @@ -632,8 +632,8 @@ // Mark each G1 region touched by the range as archive, add it to // the old set, and set top. - HeapRegion* curr_region = _hrm.addr_to_region(start_address); - HeapRegion* last_region = _hrm.addr_to_region(last_address); + HeapRegion* curr_region = _hrm->addr_to_region(start_address); + HeapRegion* last_region = _hrm->addr_to_region(last_address); prev_last_region = last_region; while (curr_region != NULL) { @@ -650,7 +650,7 @@ HeapRegion* next_region; if (curr_region != last_region) { top = curr_region->end(); - next_region = _hrm.next_region_in_heap(curr_region); + next_region = _hrm->next_region_in_heap(curr_region); } else { top = last_address + 1; next_region = NULL; @@ -671,7 +671,7 @@ assert(!is_init_completed(), "Expect to be called at JVM init time"); assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); - MemRegion reserved = _hrm.reserved(); + MemRegion reserved = _hrm->reserved(); HeapWord *prev_last_addr = NULL; HeapRegion* prev_last_region = NULL; @@ -691,8 +691,8 @@ "Ranges not in ascending order: " PTR_FORMAT " <= " PTR_FORMAT , p2i(start_address), p2i(prev_last_addr)); - HeapRegion* start_region = _hrm.addr_to_region(start_address); - HeapRegion* last_region = _hrm.addr_to_region(last_address); + HeapRegion* start_region = _hrm->addr_to_region(start_address); + HeapRegion* last_region = _hrm->addr_to_region(last_address); HeapWord* bottom_address = start_region->bottom(); // Check for a range beginning in the same region in which the @@ -708,7 +708,7 @@ guarantee(curr_region->is_archive(), "Expected archive region at index %u", curr_region->hrm_index()); if (curr_region != last_region) { - curr_region = _hrm.next_region_in_heap(curr_region); + curr_region = _hrm->next_region_in_heap(curr_region); } else { curr_region = NULL; } @@ -757,7 +757,7 @@ assert(!is_init_completed(), "Expect to be called at JVM init time"); assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); - MemRegion reserved = _hrm.reserved(); + MemRegion reserved = _hrm->reserved(); HeapWord* prev_last_addr = NULL; HeapRegion* prev_last_region = NULL; size_t size_used = 0; @@ -779,8 +779,8 @@ size_used += ranges[i].byte_size(); prev_last_addr = last_address; - HeapRegion* start_region = _hrm.addr_to_region(start_address); - HeapRegion* last_region = _hrm.addr_to_region(last_address); + HeapRegion* start_region = _hrm->addr_to_region(start_address); + HeapRegion* last_region = _hrm->addr_to_region(last_address); // Check for ranges that start in the same G1 region in which the previous // range ended, and adjust the start address so we don't try to free @@ -791,7 +791,7 @@ if (start_address > last_address) { continue; } - start_region = _hrm.addr_to_region(start_address); + start_region = _hrm->addr_to_region(start_address); } prev_last_region = last_region; @@ -806,11 +806,11 @@ curr_region->set_free(); curr_region->set_top(curr_region->bottom()); if (curr_region != last_region) { - curr_region = _hrm.next_region_in_heap(curr_region); + curr_region = _hrm->next_region_in_heap(curr_region); } else { curr_region = NULL; } - _hrm.shrink_at(curr_index, 1); + _hrm->shrink_at(curr_index, 1); uncommitted_regions++; } @@ -1025,6 +1025,8 @@ abandon_collection_set(collection_set()); tear_down_region_sets(false /* free_list_only */); + + hrm()->prepare_for_full_collection_start(); } void G1CollectedHeap::verify_before_full_collection(bool explicit_gc) { @@ -1036,6 +1038,8 @@ } void G1CollectedHeap::prepare_heap_for_mutators() { + hrm()->prepare_for_full_collection_end(); + // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(); MetaspaceUtils::verify_metrics(); @@ -1072,7 +1076,7 @@ } void G1CollectedHeap::verify_after_full_collection() { - _hrm.verify_optional(); + _hrm->verify_optional(); _verifier->verify_region_sets_optional(); _verifier->verify_after_gc(G1HeapVerifier::G1VerifyFull); // Clear the previous marking bitmap, if needed for bitmap verification. @@ -1324,7 +1328,7 @@ if (expand(expand_bytes, _workers)) { - _hrm.verify_optional(); + _hrm->verify_optional(); _verifier->verify_region_sets_optional(); return attempt_allocation_at_safepoint(word_size, false /* expect_null_mutator_alloc_region */); @@ -1349,7 +1353,7 @@ uint regions_to_expand = (uint)(aligned_expand_bytes / HeapRegion::GrainBytes); assert(regions_to_expand > 0, "Must expand by at least one region"); - uint expanded_by = _hrm.expand_by(regions_to_expand, pretouch_workers); + uint expanded_by = _hrm->expand_by(regions_to_expand, pretouch_workers); if (expand_time_ms != NULL) { *expand_time_ms = (os::elapsedTime() - expand_heap_start_time_sec) * MILLIUNITS; } @@ -1364,7 +1368,7 @@ // The expansion of the virtual storage space was unsuccessful. // Let's see if it was because we ran out of swap. if (G1ExitOnExpansionFailure && - _hrm.available() >= regions_to_expand) { + _hrm->available() >= regions_to_expand) { // We had head room... vm_exit_out_of_memory(aligned_expand_bytes, OOM_MMAP_ERROR, "G1 heap expansion"); } @@ -1379,7 +1383,7 @@ HeapRegion::GrainBytes); uint num_regions_to_remove = (uint)(shrink_bytes / HeapRegion::GrainBytes); - uint num_regions_removed = _hrm.shrink_by(num_regions_to_remove); + uint num_regions_removed = _hrm->shrink_by(num_regions_to_remove); size_t shrunk_bytes = num_regions_removed * HeapRegion::GrainBytes; @@ -1407,7 +1411,7 @@ shrink_helper(shrink_bytes); rebuild_region_sets(true /* free_list_only */); - _hrm.verify_optional(); + _hrm->verify_optional(); _verifier->verify_region_sets_optional(); } @@ -1485,7 +1489,8 @@ _humongous_set("Humongous Region Set", new HumongousRegionSetChecker()), _bot(NULL), _listener(), - _hrm(), + _hrm(NULL), + _is_hetero_heap(AllocateOldGenAt != NULL), _allocator(NULL), _verifier(NULL), _summary_bytes_used(0), @@ -1618,7 +1623,7 @@ guarantee(HeapWordSize == wordSize, "HeapWordSize must equal wordSize"); size_t init_byte_size = collector_policy()->initial_heap_byte_size(); - size_t max_byte_size = collector_policy()->max_heap_byte_size(); + size_t max_byte_size = g1_collector_policy()->heap_reservation_size_bytes(); size_t heap_alignment = collector_policy()->heap_alignment(); // Ensure that the sizes are properly aligned. @@ -1682,12 +1687,17 @@ ReservedSpace g1_rs = heap_rs.first_part(max_byte_size); size_t page_size = UseLargePages ? os::large_page_size() : os::vm_page_size(); G1RegionToSpaceMapper* heap_storage = - G1RegionToSpaceMapper::create_mapper(g1_rs, + G1RegionToSpaceMapper::create_heap_mapper(g1_rs, g1_rs.size(), page_size, HeapRegion::GrainBytes, 1, mtJavaHeap); + if(heap_storage == NULL) { + vm_shutdown_during_initialization("Could not initialize G1 heap"); + return JNI_ERR; + } + os::trace_page_sizes("Heap", collector_policy()->min_heap_byte_size(), max_byte_size, @@ -1718,7 +1728,9 @@ G1RegionToSpaceMapper* next_bitmap_storage = create_aux_memory_mapper("Next Bitmap", bitmap_size, G1CMBitMap::heap_map_factor()); - _hrm.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage); + _hrm = HeapRegionManager::create_manager(this, collector_policy()); + + _hrm->initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage); _card_table->initialize(cardtable_storage); // Do later initialization work for concurrent refinement. _hot_card_cache->initialize(card_counts_storage); @@ -1733,20 +1745,20 @@ guarantee(g1_rs.base() >= (char*)G1CardTable::card_size, "Java heap must not start within the first card."); // Also create a G1 rem set. _g1_rem_set = new G1RemSet(this, _card_table, _hot_card_cache); - _g1_rem_set->initialize(max_capacity(), max_regions()); + _g1_rem_set->initialize(max_reserved_capacity(), max_regions()); size_t max_cards_per_region = ((size_t)1 << (sizeof(CardIdx_t)*BitsPerByte-1)) - 1; guarantee(HeapRegion::CardsPerRegion > 0, "make sure it's initialized"); guarantee(HeapRegion::CardsPerRegion < max_cards_per_region, "too many cards per region"); - FreeRegionList::set_unrealistically_long_length(max_regions() + 1); + FreeRegionList::set_unrealistically_long_length(max_expandable_regions() + 1); _bot = new G1BlockOffsetTable(reserved_region(), bot_storage); { - HeapWord* start = _hrm.reserved().start(); - HeapWord* end = _hrm.reserved().end(); + HeapWord* start = _hrm->reserved().start(); + HeapWord* end = _hrm->reserved().end(); size_t granularity = HeapRegion::GrainBytes; _in_cset_fast_test.initialize(start, end, granularity); @@ -1770,6 +1782,10 @@ // Perform any initialization actions delegated to the policy. g1_policy()->init(this, &_collection_set); + // Now we know the target length of young list. So adjust the heap to provision that many regions on dram. + if (is_hetero_heap()) { + static_cast(hrm())->adjust_dram_regions((uint)g1_policy()->young_list_target_length(), workers()); + } jint ecode = initialize_concurrent_refinement(); if (ecode != JNI_OK) { @@ -1789,7 +1805,7 @@ // Here we allocate the dummy HeapRegion that is required by the // G1AllocRegion class. - HeapRegion* dummy_region = _hrm.get_dummy_region(); + HeapRegion* dummy_region = _hrm->get_dummy_region(); // We'll re-use the same region whether the alloc region will // require BOT updates or not and, if it doesn't, then a non-young @@ -1909,16 +1925,20 @@ return _collector_policy; } +G1CollectorPolicy* G1CollectedHeap::g1_collector_policy() const { + return _collector_policy; +} + SoftRefPolicy* G1CollectedHeap::soft_ref_policy() { return &_soft_ref_policy; } size_t G1CollectedHeap::capacity() const { - return _hrm.length() * HeapRegion::GrainBytes; + return _hrm->length() * HeapRegion::GrainBytes; } size_t G1CollectedHeap::unused_committed_regions_in_bytes() const { - return _hrm.total_free_bytes(); + return _hrm->total_free_bytes(); } void G1CollectedHeap::iterate_hcc_closure(CardTableEntryClosure* cl, uint worker_i) { @@ -2133,7 +2153,7 @@ } bool G1CollectedHeap::is_in(const void* p) const { - if (_hrm.reserved().contains(p)) { + if (_hrm->reserved().contains(p)) { // Given that we know that p is in the reserved space, // heap_region_containing() should successfully // return the containing region. @@ -2147,7 +2167,7 @@ #ifdef ASSERT bool G1CollectedHeap::is_in_exact(const void* p) const { bool contains = reserved_region().contains(p); - bool available = _hrm.is_available(addr_to_region((HeapWord*)p)); + bool available = _hrm->is_available(addr_to_region((HeapWord*)p)); if (contains && available) { return true; } else { @@ -2178,18 +2198,18 @@ } void G1CollectedHeap::heap_region_iterate(HeapRegionClosure* cl) const { - _hrm.iterate(cl); + _hrm->iterate(cl); } void G1CollectedHeap::heap_region_par_iterate_from_worker_offset(HeapRegionClosure* cl, HeapRegionClaimer *hrclaimer, uint worker_id) const { - _hrm.par_iterate(cl, hrclaimer, hrclaimer->offset_for_worker(worker_id)); + _hrm->par_iterate(cl, hrclaimer, hrclaimer->offset_for_worker(worker_id)); } void G1CollectedHeap::heap_region_par_iterate_from_start(HeapRegionClosure* cl, HeapRegionClaimer *hrclaimer) const { - _hrm.par_iterate(cl, hrclaimer, 0); + _hrm->par_iterate(cl, hrclaimer, 0); } void G1CollectedHeap::collection_set_iterate(HeapRegionClosure* cl) { @@ -2238,7 +2258,11 @@ } size_t G1CollectedHeap::max_capacity() const { - return _hrm.reserved().byte_size(); + return _hrm->max_expandable_length() * HeapRegion::GrainBytes; +} + +size_t G1CollectedHeap::max_reserved_capacity() const { + return _hrm->max_length() * HeapRegion::GrainBytes; } jlong G1CollectedHeap::millis_since_last_gc() { @@ -2328,8 +2352,8 @@ st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", capacity()/K, used_unlocked()/K); st->print(" [" PTR_FORMAT ", " PTR_FORMAT ")", - p2i(_hrm.reserved().start()), - p2i(_hrm.reserved().end())); + p2i(_hrm->reserved().start()), + p2i(_hrm->reserved().end())); st->cr(); st->print(" region size " SIZE_FORMAT "K, ", HeapRegion::GrainBytes / K); uint young_regions = young_regions_count(); @@ -2504,6 +2528,10 @@ if (full) { // Update the number of full collections that have been completed. increment_old_marking_cycles_completed(false /* concurrent */); + // Now we know the target length of young list. So adjust the heap to provision that many regions on dram. + if (is_hetero_heap()) { + static_cast(hrm())->adjust_dram_regions((uint)g1_policy()->young_list_target_length(), workers()); + } } // We are at the end of the GC. Total collections has already been increased. @@ -3115,7 +3143,7 @@ // output from the concurrent mark thread interfering with this // logging output either. - _hrm.verify_optional(); + _hrm->verify_optional(); _verifier->verify_region_sets_optional(); TASKQUEUE_STATS_ONLY(print_taskqueue_stats()); @@ -3792,7 +3820,7 @@ bool locked) { assert(!hr->is_free(), "the region should not be free"); assert(!hr->is_empty(), "the region should not be empty"); - assert(_hrm.is_available(hr->hrm_index()), "region should be committed"); + assert(_hrm->is_available(hr->hrm_index()), "region should be committed"); assert(free_list != NULL, "pre-condition"); if (G1VerifyBitmaps) { @@ -3833,7 +3861,7 @@ assert(list != NULL, "list can't be null"); if (!list->is_empty()) { MutexLockerEx x(FreeList_lock, Mutex::_no_safepoint_check_flag); - _hrm.insert_list_into_free_list(list); + _hrm->insert_list_into_free_list(list); } } @@ -3913,7 +3941,15 @@ _bytes_allocated_in_old_since_last_gc += HeapRegion::GrainBytes; } // The region is now considered to be old. - r->set_old(); + if(g1h->is_hetero_heap()) { + if(!r->is_old()) { + // The region was young before, set it as pre-matured old so that next mixed gc can move + // its contents to old region which is on nv-dimm + r->set_premature_old(); + } + } else { + r->set_old(); + } // Do some allocation statistics accounting. Regions that failed evacuation // are always made old, so there is no need to update anything in the young // gen statistics, but we need to update old gen statistics. @@ -4366,7 +4402,7 @@ // this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. } - _hrm.remove_all_free_regions(); + _hrm->remove_all_free_regions(); } void G1CollectedHeap::increase_used(size_t bytes) { @@ -4441,7 +4477,7 @@ _survivor.clear(); } - RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_hrm); + RebuildRegionSetsClosure cl(free_list_only, &_old_set, _hrm); heap_region_iterate(&cl); if (!free_list_only) { @@ -4549,14 +4585,14 @@ HeapRegion* G1CollectedHeap::alloc_highest_free_region() { bool expanded = false; - uint index = _hrm.find_highest_free(&expanded); + uint index = _hrm->find_highest_free(&expanded); if (index != G1_NO_HRM_INDEX) { if (expanded) { log_debug(gc, ergo, heap)("Attempt heap expansion (requested address range outside heap bounds). region size: " SIZE_FORMAT "B", HeapRegion::GrainWords * HeapWordSize); } - _hrm.allocate_free_regions_starting_at(index, 1); + _hrm->allocate_free_regions_starting_at(index, 1); return region_at(index); } return NULL; --- old/src/hotspot/share/gc/g1/g1CollectedHeap.hpp 2018-11-19 14:04:33.096038400 -0800 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.hpp 2018-11-19 14:04:32.123917600 -0800 @@ -43,6 +43,7 @@ #include "gc/g1/g1SurvivorRegions.hpp" #include "gc/g1/g1YCTypes.hpp" #include "gc/g1/heapRegionManager.hpp" +#include "gc/g1/heterogeneousHeapRegionManager.hpp" #include "gc/g1/heapRegionSet.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/collectedHeap.hpp" @@ -194,7 +195,10 @@ G1RegionMappingChangedListener _listener; // The sequence of all heap regions in the heap. - HeapRegionManager _hrm; + HeapRegionManager* _hrm; + + // is the heap on heterogenous memory? + bool _is_hetero_heap; // Manages all allocations with regions except humongous object allocations. G1Allocator* _allocator; @@ -952,10 +956,15 @@ // The current policy object for the collector. G1Policy* g1_policy() const { return _g1_policy; } + HeapRegionManager* hrm() const { return _hrm; } + + bool is_hetero_heap() const { return _is_hetero_heap; } + const G1CollectionSet* collection_set() const { return &_collection_set; } G1CollectionSet* collection_set() { return &_collection_set; } virtual CollectorPolicy* collector_policy() const; + virtual G1CollectorPolicy* g1_collector_policy() const; virtual SoftRefPolicy* soft_ref_policy(); @@ -1004,7 +1013,7 @@ // But G1CollectedHeap doesn't yet support this. virtual bool is_maximal_no_gc() const { - return _hrm.available() == 0; + return _hrm->available() == 0; } // Returns whether there are any regions left in the heap for allocation. @@ -1013,16 +1022,19 @@ } // The current number of regions in the heap. - uint num_regions() const { return _hrm.length(); } + uint num_regions() const { return _hrm->length(); } // The max number of regions in the heap. - uint max_regions() const { return _hrm.max_length(); } + uint max_regions() const { return _hrm->max_length(); } + + // Max number of regions that can be comitted. + uint max_expandable_regions() const { return _hrm->max_expandable_length(); } // The number of regions that are completely free. - uint num_free_regions() const { return _hrm.num_free_regions(); } + uint num_free_regions() const { return _hrm->num_free_regions(); } MemoryUsage get_auxiliary_data_memory_usage() const { - return _hrm.get_auxiliary_data_memory_usage(); + return _hrm->get_auxiliary_data_memory_usage(); } // The number of regions that are not completely free. @@ -1030,7 +1042,7 @@ #ifdef ASSERT bool is_on_master_free_list(HeapRegion* hr) { - return _hrm.is_free(hr); + return _hrm->is_free(hr); } #endif // ASSERT @@ -1087,13 +1099,13 @@ // Return "TRUE" iff the given object address is in the reserved // region of g1. bool is_in_g1_reserved(const void* p) const { - return _hrm.reserved().contains(p); + return _hrm->reserved().contains(p); } // Returns a MemRegion that corresponds to the space that has been // reserved for the heap MemRegion g1_reserved() const { - return _hrm.reserved(); + return _hrm->reserved(); } virtual bool is_in_closed_subset(const void* p) const; @@ -1219,6 +1231,9 @@ // Print the maximum heap capacity. virtual size_t max_capacity() const; + // Return the size of reserved memory. Returns different value than max_capacity() when AllocateOldGenAt is used. + virtual size_t max_reserved_capacity() const; + virtual jlong millis_since_last_gc(); --- old/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp 2018-11-19 14:04:45.562751000 -0800 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp 2018-11-19 14:04:44.523413000 -0800 @@ -57,13 +57,13 @@ // Inline functions for G1CollectedHeap // Return the region with the given index. It assumes the index is valid. -inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm.at(index); } +inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm->at(index); } // Return the region with the given index, or NULL if unmapped. It assumes the index is valid. -inline HeapRegion* G1CollectedHeap::region_at_or_null(uint index) const { return _hrm.at_or_null(index); } +inline HeapRegion* G1CollectedHeap::region_at_or_null(uint index) const { return _hrm->at_or_null(index); } inline HeapRegion* G1CollectedHeap::next_region_in_humongous(HeapRegion* hr) const { - return _hrm.next_region_in_humongous(hr); + return _hrm->next_region_in_humongous(hr); } inline uint G1CollectedHeap::addr_to_region(HeapWord* addr) const { @@ -74,7 +74,7 @@ } inline HeapWord* G1CollectedHeap::bottom_addr_for_region(uint index) const { - return _hrm.reserved().start() + index * HeapRegion::GrainWords; + return _hrm->reserved().start() + index * HeapRegion::GrainWords; } template @@ -83,7 +83,7 @@ assert(is_in_g1_reserved((const void*) addr), "Address " PTR_FORMAT " is outside of the heap ranging from [" PTR_FORMAT " to " PTR_FORMAT ")", p2i((void*)addr), p2i(g1_reserved().start()), p2i(g1_reserved().end())); - return _hrm.addr_to_region((HeapWord*) addr); + return _hrm->addr_to_region((HeapWord*) addr); } template @@ -266,12 +266,12 @@ } inline void G1CollectedHeap::set_humongous_reclaim_candidate(uint region, bool value) { - assert(_hrm.at(region)->is_starts_humongous(), "Must start a humongous object"); + assert(_hrm->at(region)->is_starts_humongous(), "Must start a humongous object"); _humongous_reclaim_candidates.set_candidate(region, value); } inline bool G1CollectedHeap::is_humongous_reclaim_candidate(uint region) { - assert(_hrm.at(region)->is_starts_humongous(), "Must start a humongous object"); + assert(_hrm->at(region)->is_starts_humongous(), "Must start a humongous object"); return _humongous_reclaim_candidates.is_candidate(region); } --- old/src/hotspot/share/gc/g1/g1CollectorPolicy.cpp 2018-11-19 14:04:58.734489800 -0800 +++ new/src/hotspot/share/gc/g1/g1CollectorPolicy.cpp 2018-11-19 14:04:57.773163200 -0800 @@ -55,3 +55,19 @@ size_t page_size = UseLargePages ? os::large_page_size() : os::vm_page_size(); _heap_alignment = MAX3(card_table_alignment, _space_alignment, page_size); } + +size_t G1CollectorPolicy::heap_reservation_size_bytes() { + return _max_heap_byte_size; +} + +G1HeteroCollectorPolicy::G1HeteroCollectorPolicy() : _heap_reservation_size_bytes(MaxHeapSize*2) { +} + +void G1HeteroCollectorPolicy::initialize_size_info() { + G1CollectorPolicy::initialize_size_info(); + _heap_reservation_size_bytes = 2 * _max_heap_byte_size; +} + +size_t G1HeteroCollectorPolicy::heap_reservation_size_bytes() { + return _heap_reservation_size_bytes; +} \ No newline at end of file --- old/src/hotspot/share/gc/g1/g1CollectorPolicy.hpp 2018-11-19 14:05:10.442402300 -0800 +++ new/src/hotspot/share/gc/g1/g1CollectorPolicy.hpp 2018-11-19 14:05:09.490944700 -0800 @@ -38,6 +38,17 @@ public: G1CollectorPolicy(); + virtual size_t heap_reservation_size_bytes(); +}; + +class G1HeteroCollectorPolicy : public G1CollectorPolicy { + size_t _heap_reservation_size_bytes; + +protected: + void initialize_size_info(); +public: + G1HeteroCollectorPolicy(); + size_t heap_reservation_size_bytes(); }; #endif // SHARE_VM_GC_G1_G1COLLECTORPOLICY_HPP --- old/src/hotspot/share/gc/g1/g1FullCollector.cpp 2018-11-19 14:05:22.258849200 -0800 +++ new/src/hotspot/share/gc/g1/g1FullCollector.cpp 2018-11-19 14:05:21.294129300 -0800 @@ -193,6 +193,7 @@ _heap->prepare_heap_for_mutators(); _heap->g1_policy()->record_full_collection_end(); + _heap->gc_epilogue(true); _heap->verify_after_full_collection(); --- old/src/hotspot/share/gc/g1/g1HeapRegionTraceType.hpp 2018-11-19 14:05:33.644532500 -0800 +++ new/src/hotspot/share/gc/g1/g1HeapRegionTraceType.hpp 2018-11-19 14:05:32.698710300 -0800 @@ -37,6 +37,7 @@ StartsHumongous, ContinuesHumongous, Old, + PreMatureOld, OpenArchive, ClosedArchive, G1HeapRegionTypeEndSentinel @@ -50,6 +51,7 @@ case StartsHumongous: return "Starts Humongous"; case ContinuesHumongous: return "Continues Humongous"; case Old: return "Old"; + case PreMatureOld: return "PreMatureOld"; case OpenArchive: return "OpenArchive"; case ClosedArchive: return "ClosedArchive"; default: ShouldNotReachHere(); return NULL; --- old/src/hotspot/share/gc/g1/g1HeapVerifier.cpp 2018-11-19 14:05:45.167976300 -0800 +++ new/src/hotspot/share/gc/g1/g1HeapVerifier.cpp 2018-11-19 14:05:44.194851900 -0800 @@ -539,14 +539,14 @@ assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */); // First, check the explicit lists. - _g1h->_hrm.verify(); + _g1h->_hrm->verify(); // Finally, make sure that the region accounting in the lists is // consistent with what we see in the heap. - VerifyRegionListsClosure cl(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, &_g1h->_hrm); + VerifyRegionListsClosure cl(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, _g1h->_hrm); _g1h->heap_region_iterate(&cl); - cl.verify_counts(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, &_g1h->_hrm); + cl.verify_counts(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, _g1h->_hrm); } void G1HeapVerifier::prepare_for_verify() { @@ -787,7 +787,7 @@ bool G1HeapVerifier::check_cset_fast_test() { G1CheckCSetFastTableClosure cl; - _g1h->_hrm.iterate(&cl); + _g1h->_hrm->iterate(&cl); return !cl.failures(); } #endif // PRODUCT --- old/src/hotspot/share/gc/g1/g1Policy.cpp 2018-11-19 14:05:56.842304600 -0800 +++ new/src/hotspot/share/gc/g1/g1Policy.cpp 2018-11-19 14:05:55.874096500 -0800 @@ -96,7 +96,7 @@ if (!adaptive_young_list_length()) { _young_list_fixed_length = _young_gen_sizer.min_desired_young_length(); } - _young_gen_sizer.adjust_max_new_size(_g1h->max_regions()); + _young_gen_sizer.adjust_max_new_size(_g1h->max_expandable_regions()); _free_regions_at_end_of_collection = _g1h->num_free_regions(); @@ -218,6 +218,7 @@ uint G1Policy::update_young_list_target_length(size_t rs_lengths) { YoungTargetLengths young_lengths = young_list_target_lengths(rs_lengths); _young_list_target_length = young_lengths.first; + return young_lengths.second; } --- old/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp 2018-11-19 14:06:08.501678800 -0800 +++ new/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp 2018-11-19 14:06:07.545697900 -0800 @@ -22,14 +22,17 @@ * */ +#include "logging/log.hpp" #include "precompiled.hpp" #include "gc/g1/g1BiasedArray.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" #include "memory/allocation.inline.hpp" #include "memory/virtualspace.hpp" +#include "runtime/java.hpp" #include "services/memTracker.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" +#include "utilities/formatBuffer.hpp" G1RegionToSpaceMapper::G1RegionToSpaceMapper(ReservedSpace rs, size_t used_size, @@ -170,13 +173,140 @@ } } +static bool map_nvdimm_space(ReservedSpace rs) { + assert(AllocateOldGenAt != NULL, ""); + int _backing_fd = os::create_file_for_heap(AllocateOldGenAt); + if (_backing_fd == -1) { + return false; + log_error(gc, init)("Could not create file for Old generation at location %s", AllocateOldGenAt); + } + // commit this memory in nv-dimm + char* ret = os::attempt_reserve_memory_at(rs.size(), rs.base(), _backing_fd); + + if (ret != rs.base()) { + if (ret != NULL) { + os::unmap_memory(rs.base(), rs.size()); + } + log_error(gc, init)("Error in mapping Old Gen to given AllocateOldGenAt = %s", AllocateOldGenAt); + return false; + } + return true; +} + +G1RegionToHeteroSpaceMapper::G1RegionToHeteroSpaceMapper(ReservedSpace rs, + size_t actual_size, + size_t page_size, + size_t alloc_granularity, + size_t commit_factor, + MemoryType type) : + G1RegionToSpaceMapper(rs, actual_size, page_size, alloc_granularity, commit_factor, type), + _num_committed_dram(0), _num_committed_nvdimm(0), _success(false) { + assert(actual_size == 2 * MaxHeapSize, "For 2-way heterogenuous heap, reserved space is two times MaxHeapSize"); + + // Since we need to re-map the reserved space - 'Xmx' to nv-dimm and 'Xmx' to dram, we need to release the reserved memory first. + // Because on some OSes (e.g. Windows) you cannot do a file mapping on memory reserved with regular mapping. + os::release_memory(rs.base(), rs.size()); + // First half of size Xmx is for nv-dimm. + ReservedSpace rs_nvdimm = rs.first_part(MaxHeapSize); + assert(rs_nvdimm.base() == rs.base(), "We should get the same base address"); + + // Second half of reserved memory is mapped to dram. + ReservedSpace rs_dram = rs.last_part(MaxHeapSize); + + assert(rs_dram.size() == rs_nvdimm.size() && rs_nvdimm.size() == MaxHeapSize, "They all should be same"); + + // Reserve dram memory + char* base = os::attempt_reserve_memory_at(rs_dram.size(), rs_dram.base()); + if (base != rs_dram.base()) { + if (base != NULL) { + os::release_memory(base, rs_dram.size()); + } + log_error(gc, init)("Error in re-mapping memory on dram during G1 heterogenous memory initialization"); + return; + } + + // We reserve and commit this entire space to NV-DIMM. + if (!map_nvdimm_space(rs_nvdimm)) { + log_error(gc, init)("Error in re-mapping memory to nv-dimm during G1 heterogenous memory initialization"); + return; + } + + if (alloc_granularity >= (page_size * commit_factor)) { + _dram_mapper = new G1RegionsLargerThanCommitSizeMapper(rs_dram, rs_dram.size(), page_size, alloc_granularity, commit_factor, type); + } + else { + _dram_mapper = new G1RegionsSmallerThanCommitSizeMapper(rs_dram, rs_dram.size(), page_size, alloc_granularity, commit_factor, type); + } + + _start_index_of_nvdimm = 0; + _start_index_of_dram = (uint)(rs_nvdimm.size() / alloc_granularity); + _success = true; +} + +void G1RegionToHeteroSpaceMapper::commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) { + uint end_idx = (start_idx + (uint)num_regions - 1); + + uint num_dram = end_idx >= _start_index_of_dram ? MIN2((end_idx - _start_index_of_dram + 1), (uint)num_regions) : 0; + uint num_nvdimm = (uint)num_regions - num_dram; + + if (num_nvdimm > 0) { + // We do not need to commit nv-dimm regions, since they are committed in the beginning. + _num_committed_nvdimm += num_nvdimm; + } + if (num_dram > 0) { + _dram_mapper->commit_regions(start_idx > _start_index_of_dram ? (start_idx - _start_index_of_dram) : 0, num_dram, pretouch_gang); + _num_committed_dram += num_dram; + } +} + +void G1RegionToHeteroSpaceMapper::uncommit_regions(uint start_idx, size_t num_regions) { + uint end_idx = (start_idx + (uint)num_regions - 1); + uint num_dram = end_idx >= _start_index_of_dram ? MIN2((end_idx - _start_index_of_dram + 1), (uint)num_regions) : 0; + uint num_nvdimm = (uint)num_regions - num_dram; + + if (num_nvdimm > 0) { + // We do not uncommit memory for nv-dimm regions. + _num_committed_nvdimm -= num_nvdimm; + } + + if (num_dram > 0) { + _dram_mapper->uncommit_regions(start_idx > _start_index_of_dram ? (start_idx - _start_index_of_dram) : 0, num_dram); + _num_committed_dram -= num_dram; + } +} + +uint G1RegionToHeteroSpaceMapper::num_committed_dram() const { + return _num_committed_dram; +} + +uint G1RegionToHeteroSpaceMapper::num_committed_nvdimm() const { + return _num_committed_nvdimm; +} + +G1RegionToSpaceMapper* G1RegionToSpaceMapper::create_heap_mapper(ReservedSpace rs, + size_t actual_size, + size_t page_size, + size_t region_granularity, + size_t commit_factor, + MemoryType type) { + if (AllocateOldGenAt != NULL) { + G1RegionToHeteroSpaceMapper* mapper = new G1RegionToHeteroSpaceMapper(rs, actual_size, page_size, region_granularity, commit_factor, type); + if (!mapper->success()) { + delete mapper; + return NULL; + } + return (G1RegionToSpaceMapper*)mapper; + } else { + return create_mapper(rs, actual_size, page_size, region_granularity, commit_factor, type); + } +} + G1RegionToSpaceMapper* G1RegionToSpaceMapper::create_mapper(ReservedSpace rs, size_t actual_size, size_t page_size, size_t region_granularity, size_t commit_factor, MemoryType type) { - if (region_granularity >= (page_size * commit_factor)) { return new G1RegionsLargerThanCommitSizeMapper(rs, actual_size, page_size, region_granularity, commit_factor, type); } else { --- old/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp 2018-11-19 14:06:19.941866500 -0800 +++ new/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp 2018-11-19 14:06:18.981091300 -0800 @@ -87,6 +87,34 @@ size_t region_granularity, size_t byte_translation_factor, MemoryType type); + + static G1RegionToSpaceMapper* create_heap_mapper(ReservedSpace rs, + size_t actual_size, + size_t page_size, + size_t region_granularity, + size_t byte_translation_factor, + MemoryType type); }; +// G1RegionToSpaceMapper implementation where +// part of space is mapped to dram and part to nv-dimm +class G1RegionToHeteroSpaceMapper : public G1RegionToSpaceMapper { +private: + size_t _pages_per_region; + G1RegionToSpaceMapper* _dram_mapper; + uint _num_committed_dram; + uint _num_committed_nvdimm; + uint _start_index_of_nvdimm; + uint _start_index_of_dram; + bool _success; + +public: + G1RegionToHeteroSpaceMapper(ReservedSpace rs, size_t used_size, size_t page_size, size_t region_granularity, size_t commit_factor, MemoryType type); + uint num_committed_dram() const; + uint num_committed_nvdimm() const; + bool success() { return _success; } + + virtual void commit_regions(uint start_idx, size_t num_regions = 1, WorkGang* pretouch_workers = NULL); + virtual void uncommit_regions(uint start_idx, size_t num_regions = 1); +}; #endif // SHARE_VM_GC_G1_G1REGIONTOSPACEMAPPER_HPP --- old/src/hotspot/share/gc/g1/g1_globals.hpp 2018-11-19 14:06:31.386925200 -0800 +++ new/src/hotspot/share/gc/g1/g1_globals.hpp 2018-11-19 14:06:30.439130100 -0800 @@ -302,6 +302,15 @@ "Verify the code root lists attached to each heap region.") \ \ develop(bool, G1VerifyBitmaps, false, \ - "Verifies the consistency of the marking bitmaps") + "Verifies the consistency of the marking bitmaps") \ + \ + experimental(uintx, G1YoungExpansionBufferPercent, 10, \ + "When heterogenous heap is enabled by AllocateOldGenAt " \ + "option, after every GC, young gen is re-sized which " \ + "involves system calls to commit/uncommit memory. To " \ + "reduce these calls, we keep a buffer of extra regions to " \ + "absorb small changes in young gen length. This flag takes " \ + "the buffer size as an percentage of young gen length") \ + range(0, 100) #endif // SHARE_VM_GC_G1_G1_GLOBALS_HPP --- old/src/hotspot/share/gc/g1/heapRegion.cpp 2018-11-19 14:06:43.106674300 -0800 +++ new/src/hotspot/share/gc/g1/heapRegion.cpp 2018-11-19 14:06:42.101835200 -0800 @@ -184,6 +184,11 @@ _type.set_old(); } +void HeapRegion::set_premature_old() { + report_region_type_change(G1HeapRegionTraceType::Old); + _type.set_premature_old(); +} + void HeapRegion::set_open_archive() { report_region_type_change(G1HeapRegionTraceType::OpenArchive); _type.set_open_archive(); --- old/src/hotspot/share/gc/g1/heapRegion.hpp 2018-11-19 14:06:54.676259800 -0800 +++ new/src/hotspot/share/gc/g1/heapRegion.hpp 2018-11-19 14:06:53.717475500 -0800 @@ -424,6 +424,8 @@ bool is_old() const { return _type.is_old(); } + bool is_premature_old() const { return _type.is_premature_old(); } + bool is_old_or_humongous() const { return _type.is_old_or_humongous(); } bool is_old_or_humongous_or_archive() const { return _type.is_old_or_humongous_or_archive(); } @@ -613,6 +615,7 @@ void move_to_old(); void set_old(); + void set_premature_old(); void set_open_archive(); void set_closed_archive(); --- old/src/hotspot/share/gc/g1/heapRegionManager.cpp 2018-11-19 14:07:06.706353100 -0800 +++ new/src/hotspot/share/gc/g1/heapRegionManager.cpp 2018-11-19 14:07:05.736880400 -0800 @@ -27,7 +27,9 @@ #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/heapRegion.hpp" #include "gc/g1/heapRegionManager.inline.hpp" +#include "gc/g1/heterogeneousHeapRegionManager.hpp" #include "gc/g1/heapRegionSet.inline.hpp" +#include "gc/shared/collectorPolicy.hpp" #include "memory/allocation.hpp" #include "utilities/bitMap.inline.hpp" @@ -54,18 +56,25 @@ }; HeapRegionManager::HeapRegionManager() : - _regions(), _heap_mapper(NULL), _prev_bitmap_mapper(NULL), _next_bitmap_mapper(NULL), _bot_mapper(NULL), _cardtable_mapper(NULL), _card_counts_mapper(NULL), - _free_list("Free list", new MasterFreeRegionListChecker()), _available_map(mtGC), _num_committed(0), - _allocated_heapregions_length(0) + _allocated_heapregions_length(0), + _regions(), _heap_mapper(NULL), + _free_list("Free list", new MasterFreeRegionListChecker()) { } +HeapRegionManager* HeapRegionManager::create_manager(G1CollectedHeap* heap, CollectorPolicy* policy) { + if (heap->is_hetero_heap()) { + return new HeterogeneousHeapRegionManager((uint)(policy->max_heap_byte_size() / HeapRegion::GrainBytes) /*heap size as num of regions*/); + } + return new HeapRegionManager(); +} + void HeapRegionManager::initialize(G1RegionToSpaceMapper* heap_storage, G1RegionToSpaceMapper* prev_bitmap, G1RegionToSpaceMapper* next_bitmap, @@ -514,7 +523,7 @@ #endif // PRODUCT HeapRegionClaimer::HeapRegionClaimer(uint n_workers) : - _n_workers(n_workers), _n_regions(G1CollectedHeap::heap()->_hrm._allocated_heapregions_length), _claims(NULL) { + _n_workers(n_workers), _n_regions(G1CollectedHeap::heap()->_hrm->_allocated_heapregions_length), _claims(NULL) { assert(n_workers > 0, "Need at least one worker."); uint* new_claims = NEW_C_HEAP_ARRAY(uint, _n_regions, mtGC); memset(new_claims, Unclaimed, sizeof(*_claims) * _n_regions); --- old/src/hotspot/share/gc/g1/heapRegionManager.hpp 2018-11-19 14:07:18.219280100 -0800 +++ new/src/hotspot/share/gc/g1/heapRegionManager.hpp 2018-11-19 14:07:17.257733500 -0800 @@ -26,6 +26,7 @@ #define SHARE_VM_GC_G1_HEAPREGIONMANAGER_HPP #include "gc/g1/g1BiasedArray.hpp" +#include "gc/g1/g1CollectorPolicy.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" #include "gc/g1/heapRegionSet.hpp" #include "services/memoryUsage.hpp" @@ -71,17 +72,12 @@ friend class VMStructs; friend class HeapRegionClaimer; - G1HeapRegionTable _regions; - - G1RegionToSpaceMapper* _heap_mapper; G1RegionToSpaceMapper* _prev_bitmap_mapper; G1RegionToSpaceMapper* _next_bitmap_mapper; G1RegionToSpaceMapper* _bot_mapper; G1RegionToSpaceMapper* _cardtable_mapper; G1RegionToSpaceMapper* _card_counts_mapper; - FreeRegionList _free_list; - // Each bit in this bitmap indicates that the corresponding region is available // for allocation. CHeapBitMap _available_map; @@ -95,11 +91,8 @@ HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); } HeapWord* heap_end() const {return _regions.end_address_mapped(); } - void make_regions_available(uint index, uint num_regions = 1, WorkGang* pretouch_gang = NULL); - // Pass down commit calls to the VirtualSpace. void commit_regions(uint index, size_t num_regions = 1, WorkGang* pretouch_gang = NULL); - void uncommit_regions(uint index, size_t num_regions = 1); // Notify other data structures about change in the heap layout. void update_committed_space(HeapWord* old_end, HeapWord* new_end); @@ -117,6 +110,13 @@ // the heap. Returns the length of the sequence found. If this value is zero, no // sequence could be found, otherwise res_idx contains the start index of this range. uint find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const; + +protected: + G1HeapRegionTable _regions; + G1RegionToSpaceMapper* _heap_mapper; + FreeRegionList _free_list; + void make_regions_available(uint index, uint num_regions = 1, WorkGang* pretouch_gang = NULL); + void uncommit_regions(uint index, size_t num_regions = 1); // Allocate a new HeapRegion for the given index. HeapRegion* new_heap_region(uint hrm_index); #ifdef ASSERT @@ -127,6 +127,8 @@ // Empty constructor, we'll initialize it with the initialize() method. HeapRegionManager(); + static HeapRegionManager* create_manager(G1CollectedHeap* heap, CollectorPolicy* policy); + void initialize(G1RegionToSpaceMapper* heap_storage, G1RegionToSpaceMapper* prev_bitmap, G1RegionToSpaceMapper* next_bitmap, @@ -134,11 +136,16 @@ G1RegionToSpaceMapper* cardtable, G1RegionToSpaceMapper* card_counts); + // Prepare heap regions before and after full collection. + // Nothing to be done in this class. + virtual void prepare_for_full_collection_start() {} + virtual void prepare_for_full_collection_end() {} + // Return the "dummy" region used for G1AllocRegion. This is currently a hardwired // new HeapRegion that owns HeapRegion at index 0. Since at the moment we commit // the heap from the lowest address, this region (and its associated data // structures) are available and we do not need to check further. - HeapRegion* get_dummy_region() { return new_heap_region(0); } + virtual HeapRegion* get_dummy_region() { return new_heap_region(0); } // Return the HeapRegion at the given index. Assume that the index // is valid. @@ -167,7 +174,7 @@ _free_list.add_ordered(list); } - HeapRegion* allocate_free_region(bool is_old) { + virtual HeapRegion* allocate_free_region(bool is_old) { HeapRegion* hr = _free_list.remove_region(is_old); if (hr != NULL) { @@ -201,6 +208,9 @@ // Return the maximum number of regions in the heap. uint max_length() const { return (uint)_regions.length(); } + + // Return maximum number of regions that heap can expand to. + virtual uint max_expandable_length() const { return (uint)_regions.length(); } MemoryUsage get_auxiliary_data_memory_usage() const; @@ -210,26 +220,26 @@ // HeapRegions, or re-use existing ones. Returns the number of regions the // sequence was expanded by. If a HeapRegion allocation fails, the resulting // number of regions might be smaller than what's desired. - uint expand_by(uint num_regions, WorkGang* pretouch_workers); + virtual uint expand_by(uint num_regions, WorkGang* pretouch_workers); // Makes sure that the regions from start to start+num_regions-1 are available // for allocation. Returns the number of regions that were committed to achieve // this. - uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers); + virtual uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers); // Find a contiguous set of empty regions of length num. Returns the start index of // that set, or G1_NO_HRM_INDEX. - uint find_contiguous_only_empty(size_t num) { return find_contiguous(num, true); } + virtual uint find_contiguous_only_empty(size_t num) { return find_contiguous(num, true); } // Find a contiguous set of empty or unavailable regions of length num. Returns the // start index of that set, or G1_NO_HRM_INDEX. - uint find_contiguous_empty_or_unavailable(size_t num) { return find_contiguous(num, false); } + virtual uint find_contiguous_empty_or_unavailable(size_t num) { return find_contiguous(num, false); } HeapRegion* next_region_in_heap(const HeapRegion* r) const; // Find the highest free or uncommitted region in the reserved heap, // and if uncommitted, commit it. If none are available, return G1_NO_HRM_INDEX. // Set the 'expanded' boolean true if a new region was committed. - uint find_highest_free(bool* expanded); + virtual uint find_highest_free(bool* expanded); // Allocate the regions that contain the address range specified, committing the // regions if necessary. Return false if any of the regions is already committed @@ -244,13 +254,13 @@ // Uncommit up to num_regions_to_remove regions that are completely free. // Return the actual number of uncommitted regions. - uint shrink_by(uint num_regions_to_remove); + virtual uint shrink_by(uint num_regions_to_remove); // Uncommit a number of regions starting at the specified index, which must be available, // empty, and free. void shrink_at(uint index, size_t num_regions); - void verify(); + virtual void verify(); // Do some sanity checking. void verify_optional() PRODUCT_RETURN; --- old/src/hotspot/share/gc/g1/heapRegionSet.cpp 2018-11-19 14:07:30.470998400 -0800 +++ new/src/hotspot/share/gc/g1/heapRegionSet.cpp 2018-11-19 14:07:29.489038200 -0800 @@ -234,6 +234,21 @@ verify_optional(); } +uint FreeRegionList::num_of_regions_in_range(uint start, uint end) const { + HeapRegion* cur = _head; + uint num = 0; + while (cur != NULL) { + uint index = cur->hrm_index(); + if (index > end) { + break; + } else if (index >= start) { + num++; + } + cur = cur->next(); + } + return num; +} + void FreeRegionList::verify() { // See comment in HeapRegionSetBase::verify() about MT safety and // verification. --- old/src/hotspot/share/gc/g1/heapRegionSet.hpp 2018-11-19 14:07:44.373466100 -0800 +++ new/src/hotspot/share/gc/g1/heapRegionSet.hpp 2018-11-19 14:07:43.303377900 -0800 @@ -194,6 +194,8 @@ void remove_starting_at(HeapRegion* first, uint num_regions); virtual void verify(); + + uint num_of_regions_in_range(uint start, uint end) const; }; // Iterator class that provides a convenient way to iterate over the --- old/src/hotspot/share/gc/g1/heapRegionType.cpp 2018-11-19 14:07:56.919535900 -0800 +++ new/src/hotspot/share/gc/g1/heapRegionType.cpp 2018-11-19 14:07:55.860312900 -0800 @@ -34,6 +34,7 @@ case StartsHumongousTag: case ContinuesHumongousTag: case OldTag: + case PreMatureOldTag: case OpenArchiveTag: case ClosedArchiveTag: return true; @@ -51,6 +52,7 @@ case StartsHumongousTag: return "HUMS"; case ContinuesHumongousTag: return "HUMC"; case OldTag: return "OLD"; + case PreMatureOldTag: return "PMOLD"; case OpenArchiveTag: return "OARC"; case ClosedArchiveTag: return "CARC"; default: @@ -68,6 +70,7 @@ case StartsHumongousTag: return "HS"; case ContinuesHumongousTag: return "HC"; case OldTag: return "O"; + case PreMatureOldTag: return "PO"; case OpenArchiveTag: return "OA"; case ClosedArchiveTag: return "CA"; default: @@ -85,6 +88,7 @@ case StartsHumongousTag: return G1HeapRegionTraceType::StartsHumongous; case ContinuesHumongousTag: return G1HeapRegionTraceType::ContinuesHumongous; case OldTag: return G1HeapRegionTraceType::Old; + case PreMatureOldTag: return G1HeapRegionTraceType::PreMatureOld; case OpenArchiveTag: return G1HeapRegionTraceType::OpenArchive; case ClosedArchiveTag: return G1HeapRegionTraceType::ClosedArchive; default: --- old/src/hotspot/share/gc/g1/heapRegionType.hpp 2018-11-19 14:08:09.004970500 -0800 +++ new/src/hotspot/share/gc/g1/heapRegionType.hpp 2018-11-19 14:08:08.015589600 -0800 @@ -74,7 +74,9 @@ ContinuesHumongousTag = HumongousMask | PinnedMask + 1, OldMask = 16, + PreMatureOldMask = OldMask + 1, OldTag = OldMask, + PreMatureOldTag = PreMatureOldMask, // Archive regions are regions with immutable content (i.e. not reclaimed, and // not allocated into during regular operation). They differ in the kind of references @@ -137,6 +139,8 @@ // is_old regions may or may not also be pinned bool is_old() const { return (get() & OldMask) != 0; } + bool is_premature_old() const { return get() == PreMatureOldTag; } + bool is_old_or_humongous() const { return (get() & (OldMask | HumongousMask)) != 0; } bool is_old_or_humongous_or_archive() const { return (get() & (OldMask | HumongousMask | ArchiveMask)) != 0; } @@ -157,6 +161,8 @@ void set_old() { set(OldTag); } + void set_premature_old() { set(PreMatureOldTag); } + // Change the current region type to be of an old region type if not already done so. // Returns whether the region type has been changed or not. bool relabel_as_old() { --- old/src/hotspot/share/gc/g1/vmStructs_g1.hpp 2018-11-19 14:08:23.263295900 -0800 +++ new/src/hotspot/share/gc/g1/vmStructs_g1.hpp 2018-11-19 14:08:22.112726700 -0800 @@ -53,7 +53,7 @@ nonstatic_field(HeapRegionManager, _num_committed, uint) \ \ nonstatic_field(G1CollectedHeap, _summary_bytes_used, size_t) \ - nonstatic_field(G1CollectedHeap, _hrm, HeapRegionManager) \ + nonstatic_field(G1CollectedHeap, _hrm, HeapRegionManager*) \ nonstatic_field(G1CollectedHeap, _g1mm, G1MonitoringSupport*) \ nonstatic_field(G1CollectedHeap, _old_set, HeapRegionSetBase) \ nonstatic_field(G1CollectedHeap, _archive_set, HeapRegionSetBase) \ @@ -83,6 +83,7 @@ declare_constant(HeapRegionType::StartsHumongousTag) \ declare_constant(HeapRegionType::ContinuesHumongousTag) \ declare_constant(HeapRegionType::OldMask) \ + declare_constant(HeapRegionType::PreMatureOldMask) \ declare_constant(BarrierSet::G1BarrierSet) \ declare_constant(G1CardTable::g1_young_gen) --- old/src/hotspot/share/gc/shared/gcArguments.cpp 2018-11-19 14:08:36.098591800 -0800 +++ new/src/hotspot/share/gc/shared/gcArguments.cpp 2018-11-19 14:08:35.034190400 -0800 @@ -28,6 +28,7 @@ #include "runtime/arguments.hpp" #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" +#include "utilities/defaultStream.hpp" #include "utilities/macros.hpp" void GCArguments::initialize() { @@ -54,3 +55,18 @@ FLAG_SET_CMDLINE(bool, ClassUnloadingWithConcurrentMark, false); } } + +bool GCArguments::check_args_consistency() { + bool status = true; + if (!FLAG_IS_DEFAULT(AllocateHeapAt) && !FLAG_IS_DEFAULT(AllocateOldGenAt)) { + jio_fprintf(defaultStream::error_stream(), + "AllocateHeapAt and AllocateOldGenAt cannot be used together.\n"); + status = false; + } + if (!FLAG_IS_DEFAULT(AllocateOldGenAt) && (UseSerialGC || UseConcMarkSweepGC || UseEpsilonGC || UseZGC)) { + jio_fprintf(defaultStream::error_stream(), + "AllocateOldGenAt not supported for selected GC.\n"); + status = false; + } + return status; +} --- old/src/hotspot/share/gc/shared/gcArguments.hpp 2018-11-19 14:08:48.603702900 -0800 +++ new/src/hotspot/share/gc/shared/gcArguments.hpp 2018-11-19 14:08:47.569437800 -0800 @@ -39,6 +39,7 @@ virtual void initialize(); virtual size_t conservative_max_heap_alignment() = 0; virtual CollectedHeap* create_heap() = 0; + static bool check_args_consistency(); }; #endif // SHARE_GC_SHARED_GCARGUMENTS_HPP --- old/src/hotspot/share/prims/whitebox.cpp 2018-11-19 14:09:02.043719000 -0800 +++ new/src/hotspot/share/prims/whitebox.cpp 2018-11-19 14:09:00.851518900 -0800 @@ -82,6 +82,7 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.hpp" #include "gc/g1/heapRegionRemSet.hpp" +#include "gc/g1/heterogeneousHeapRegionManager.hpp" #endif // INCLUDE_G1GC #if INCLUDE_PARALLELGC #include "gc/parallel/parallelScavengeHeap.inline.hpp" @@ -370,6 +371,10 @@ return !gch->is_in_young(p); WB_END +WB_ENTRY(jlong, WB_GetHeapBase(JNIEnv* env, jobject o)) + return (jlong)Universe::heap()->base(); +WB_END + WB_ENTRY(jlong, WB_GetObjectSize(JNIEnv* env, jobject o, jobject obj)) oop p = JNIHandles::resolve(obj); return Universe::heap()->obj_size(p) * HeapWordSize; @@ -497,6 +502,60 @@ THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1RegionSize: G1 GC is not enabled"); WB_END +WB_ENTRY(jlong, WB_G1DramReservedStart(JNIEnv* env, jobject o)) + if (UseG1GC) { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (g1h->is_hetero_heap()) { + uint start_region = static_cast(g1h->hrm())->start_index_of_dram(); + return (jlong)(Universe::heap()->base() + start_region * HeapRegion::GrainBytes); + } else { + return (jlong)Universe::heap()->base(); + } + } + THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1DramReservedStart: G1 GC is not enabled"); +WB_END + +WB_ENTRY(jlong, WB_G1DramReservedEnd(JNIEnv* env, jobject o)) +if (UseG1GC) { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (g1h->is_hetero_heap()) { + uint end_region = static_cast(g1h->hrm())->end_index_of_dram(); + return (jlong)(Universe::heap()->base() + (end_region + 1) * HeapRegion::GrainBytes - 1); + } + else { + return (jlong)Universe::heap()->base() + Universe::heap()->collector_policy()->max_heap_byte_size(); + } +} +THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1DramReservedEnd: G1 GC is not enabled"); +WB_END + +WB_ENTRY(jlong, WB_G1NvdimmReservedStart(JNIEnv* env, jobject o)) +if (UseG1GC) { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (g1h->is_hetero_heap()) { + uint start_region = static_cast(g1h->hrm())->start_index_of_nvdimm(); + return (jlong)(Universe::heap()->base() + start_region * HeapRegion::GrainBytes); + } else { + THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1NvdimmReservedStart: G1 heap is not heterogeneous"); + } +} +THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1NvdimmReservedStart: G1 GC is not enabled"); +WB_END + +WB_ENTRY(jlong, WB_G1NvdimmReservedEnd(JNIEnv* env, jobject o)) +if (UseG1GC) { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (g1h->is_hetero_heap()) { + uint end_region = static_cast(g1h->hrm())->start_index_of_nvdimm(); + return (jlong)(Universe::heap()->base() + (end_region + 1) * HeapRegion::GrainBytes - 1); + } + else { + THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1NvdimmReservedEnd: G1 heap is not heterogeneous"); + } +} +THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1NvdimmReservedEnd: G1 GC is not enabled"); +WB_END + #endif // INCLUDE_G1GC #if INCLUDE_PARALLELGC @@ -2015,6 +2074,7 @@ static JNINativeMethod methods[] = { {CC"getObjectAddress0", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectAddress }, {CC"getObjectSize0", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectSize }, + {CC"getHeapBase", CC"()J", (void*)&WB_GetHeapBase }, {CC"isObjectInOldGen0", CC"(Ljava/lang/Object;)Z", (void*)&WB_isObjectInOldGen }, {CC"getHeapOopSize", CC"()I", (void*)&WB_GetHeapOopSize }, {CC"getVMPageSize", CC"()I", (void*)&WB_GetVMPageSize }, @@ -2049,6 +2109,10 @@ {CC"g1NumMaxRegions", CC"()J", (void*)&WB_G1NumMaxRegions }, {CC"g1NumFreeRegions", CC"()J", (void*)&WB_G1NumFreeRegions }, {CC"g1RegionSize", CC"()I", (void*)&WB_G1RegionSize }, + {CC"g1DramReservedStart", CC"()J", (void*)&WB_G1DramReservedStart }, + {CC"g1DramReservedEnd", CC"()J", (void*)&WB_G1DramReservedEnd }, + {CC"g1NvdimmReservedStart", CC"()J", (void*)&WB_G1NvdimmReservedStart }, + {CC"g1NvdimmReservedEnd", CC"()J", (void*)&WB_G1NvdimmReservedEnd }, {CC"g1StartConcMarkCycle", CC"()Z", (void*)&WB_G1StartMarkCycle }, {CC"g1AuxiliaryMemoryUsage", CC"()Ljava/lang/management/MemoryUsage;", (void*)&WB_G1AuxiliaryMemoryUsage }, --- old/src/hotspot/share/runtime/arguments.cpp 2018-11-19 14:09:15.151683700 -0800 +++ new/src/hotspot/share/runtime/arguments.cpp 2018-11-19 14:09:14.002091100 -0800 @@ -1622,6 +1622,10 @@ } void Arguments::set_use_compressed_oops() { +if (AllocateOldGenAt != NULL) { + FLAG_SET_ERGO(bool, UseCompressedOops, false); + return; +} #ifndef ZERO #ifdef _LP64 // MaxHeapSize is not set up properly at this point, but @@ -2062,6 +2066,9 @@ log_warning(arguments) ("NUMA support for Heap depends on the file system when AllocateHeapAt option is used.\n"); } } + + status = status && GCArguments::check_args_consistency(); + return status; } --- old/src/hotspot/share/runtime/globals.hpp 2018-11-19 14:09:28.610640800 -0800 +++ new/src/hotspot/share/runtime/globals.hpp 2018-11-19 14:09:27.537720400 -0800 @@ -2601,7 +2601,10 @@ "Start flight recording with options")) \ \ experimental(bool, UseFastUnorderedTimeStamps, false, \ - "Use platform unstable time where supported for timestamps only") + "Use platform unstable time where supported for timestamps only") \ + \ + experimental(ccstr, AllocateOldGenAt, NULL, \ + "Directory to use for allocating old generation") #define VM_FLAGS(develop, \ develop_pd, \ --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/G1CollectedHeap.java 2018-11-19 14:09:41.551827600 -0800 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/G1CollectedHeap.java 2018-11-19 14:09:40.482917600 -0800 @@ -47,7 +47,7 @@ public class G1CollectedHeap extends CollectedHeap { // HeapRegionManager _hrm; - static private long hrmFieldOffset; + static private AddressField hrmField; // MemRegion _g1_reserved; static private long g1ReservedFieldOffset; // size_t _summary_bytes_used; @@ -72,7 +72,7 @@ static private synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("G1CollectedHeap"); - hrmFieldOffset = type.getField("_hrm").getOffset(); + hrmField = type.getAddressField("_hrm"); summaryBytesUsedField = type.getCIntegerField("_summary_bytes_used"); g1mmField = type.getAddressField("_g1mm"); oldSetFieldOffset = type.getField("_old_set").getOffset(); @@ -93,7 +93,7 @@ } public HeapRegionManager hrm() { - Address hrmAddr = addr.addOffsetTo(hrmFieldOffset); + Address hrmAddr = hrmField.getValue(addr); return (HeapRegionManager) VMObjectFactory.newObject(HeapRegionManager.class, hrmAddr); } --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegion.java 2018-11-19 14:09:54.166069500 -0800 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegion.java 2018-11-19 14:09:53.112598100 -0800 @@ -122,6 +122,10 @@ return type.isOld(); } + public boolean isPreMatureOld() { + return type.isPreMatureOld(); + } + public static long getPointerSize() { return pointerSize; } --- old/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegionType.java 2018-11-19 14:10:06.789602400 -0800 +++ new/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegionType.java 2018-11-19 14:10:05.713525400 -0800 @@ -48,6 +48,7 @@ private static int pinnedMask; private static int archiveMask; private static int oldMask; + private static int preMatureOldMask; private static CIntegerField tagField; private int tag; @@ -74,6 +75,7 @@ humongousMask = db.lookupIntConstant("HeapRegionType::HumongousMask"); pinnedMask = db.lookupIntConstant("HeapRegionType::PinnedMask"); oldMask = db.lookupIntConstant("HeapRegionType::OldMask"); + preMatureOldMask = db.lookupIntConstant("HeapRegionType::PreMatureOldMask"); } public boolean isFree() { @@ -116,6 +118,10 @@ return (tagField.getValue(addr) & oldMask) != 0; } + public boolean isPreMatureOld() { + return (tagField.getValue(addr) & preMatureOldMask) != 0; + } + public HeapRegionType(Address addr) { super(addr); } @@ -145,6 +151,9 @@ if (isOld()) { return "Old"; } + if (isPreMatureOld()) { + return "PreMature Old"; + } return "Unknown Region Type"; } } --- old/test/lib/sun/hotspot/WhiteBox.java 2018-11-19 14:10:19.838415500 -0800 +++ new/test/lib/sun/hotspot/WhiteBox.java 2018-11-19 14:10:18.736914900 -0800 @@ -182,6 +182,10 @@ public native long g1NumMaxRegions(); public native long g1NumFreeRegions(); public native int g1RegionSize(); + public native long g1DramReservedStart(); + public native long g1DramReservedEnd(); + public native long g1NvdimmReservedStart(); + public native long g1NvdimmReservedEnd(); public native MemoryUsage g1AuxiliaryMemoryUsage(); private native Object[] parseCommandLine0(String commandline, char delim, DiagnosticCommand[] args); public Object[] parseCommandLine(String commandline, char delim, DiagnosticCommand[] args) { --- /dev/null 2018-11-19 14:10:34.000000000 -0800 +++ new/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.cpp 2018-11-19 14:10:31.311159900 -0800 @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2018, 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. + * + */ + +#include "precompiled.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1ConcurrentRefine.hpp" +#include "gc/g1/heapRegion.hpp" +#include "gc/g1/heapRegionManager.inline.hpp" +#include "gc/g1/heterogeneousHeapRegionManager.hpp" +#include "gc/g1/heapRegionSet.inline.hpp" +#include "memory/allocation.hpp" + +// expand_by() is called to grow the heap. We grow into nvdimm now. +// Dram regions are committed later as needed during mutator region allocation or +// when young list target length is determined after gc cycle. +uint HeterogeneousHeapRegionManager::expand_by(uint num_regions, WorkGang* pretouch_workers) { + uint num_expanded = expand_nvdimm(MIN2(num_regions, max_expandable_length() - total_regions_committed()), pretouch_workers); + assert(total_regions_committed() <= max_expandable_length(), "must be"); + return num_expanded; +} + +// Expands heap starting from 'start' index. The question is should we expand from one memory (e.g. nvdimm) to another (e.g. dram). +// Looking at the code, expand_at() is called for humongous allocation where 'start' is in nv-dimm. +// So we only allocate regions in the same kind of memory as 'start'. +uint HeterogeneousHeapRegionManager::expand_at(uint start, uint num_regions, WorkGang* pretouch_workers) { + if (num_regions == 0) { + return 0; + } + uint target_num_regions = MIN2(num_regions, max_expandable_length() - total_regions_committed()); + uint end = is_in_nvdimm(start) ? end_index_of_nvdimm() : end_index_of_dram(); + uint num_expanded = expand_in_range(start, end, target_num_regions, pretouch_workers); + assert(total_regions_committed() <= max_expandable_length(), "must be"); + return num_expanded; +} + +// This function ensures that there are 'expected_num_regions' committed regions in dram. +// If new regions are committed, it un-commits that many regions from nv-dimm. +// If there are already more regions committed in dram, extra regions are un-committed. +void HeterogeneousHeapRegionManager::adjust_dram_regions(uint expected_num_regions, WorkGang* pretouch_workers) { + + assert(total_regions_committed() <= max_expandable_length(), "must be"); + if(expected_num_regions > free_list_dram_length()) { + // If we are going to expand DRAM, we expand a little more so that we can absorb small variations in Young gen sizing. + uint targeted_dram_regions = expected_num_regions * (1 + (double)G1YoungExpansionBufferPerc / 100); + uint to_be_made_available = targeted_dram_regions - free_list_dram_length(); + +#ifdef ASSERT + uint total_committed_before = total_regions_committed(); +#endif + uint can_be_made_available = shrink_nvdimm(to_be_made_available); + uint ret = expand_dram(can_be_made_available, pretouch_workers); +#ifdef ASSERT + assert(ret == can_be_made_available, "should be equal"); + assert(total_committed_before == total_regions_committed(), "invariant not met"); + assert(total_regions_committed() <= _max_regions, "post-condition"); +#endif + } else { + uint to_be_released = free_list_dram_length() - expected_num_regions; + // if number of extra DRAM regions is small, do not shrink. + if (to_be_released < expected_num_regions * G1YoungExpansionBufferPerc / 100) { + return; + } + +#ifdef ASSERT + uint total_committed_before = total_regions_committed(); +#endif + uint ret = shrink_dram(to_be_released); + assert(ret == to_be_released, "Should be able to shrink by given amount"); + ret = expand_nvdimm(to_be_released, pretouch_workers); +#ifdef ASSERT + assert(ret == to_be_released, "Should be able to expand by given amount"); + assert(total_committed_before == total_regions_committed(), "invariant not met"); + assert(total_regions_committed() <= _max_regions, "post-condition"); +#endif + } + assert(total_regions_committed() <= max_expandable_length(), "must be"); +} + +uint HeterogeneousHeapRegionManager::total_regions_committed() const { + return num_committed_dram() + num_committed_nvdimm(); +} + +uint HeterogeneousHeapRegionManager::num_committed_dram() const { + // This class does not keep count of committed regions in dram and nv-dimm. + // G1RegionToHeteroSpaceMapper keeps this information. + return static_cast(_heap_mapper)->num_committed_dram(); +} + +uint HeterogeneousHeapRegionManager::num_committed_nvdimm() const { + // See comment for num_committed_dram() + return static_cast(_heap_mapper)->num_committed_nvdimm(); +} + +// Return maximum number of regions that heap can expand to. +uint HeterogeneousHeapRegionManager::max_expandable_length() const { + return _max_regions; +} + +uint HeterogeneousHeapRegionManager::find_unavailable_in_range(uint start_idx, uint end_idx, uint* res_idx) const { + guarantee(res_idx != NULL, "checking"); + guarantee(start_idx <= (max_length() + 1), "checking"); + + uint num_regions = 0; + + uint cur = start_idx; + while (cur <= end_idx && is_available(cur)) { + cur++; + } + if (cur == end_idx + 1) { + return num_regions; + } + *res_idx = cur; + while (cur <= end_idx && !is_available(cur)) { + cur++; + } + num_regions = cur - *res_idx; + +#ifdef ASSERT + for (uint i = *res_idx; i < (*res_idx + num_regions); i++) { + assert(!is_available(i), "just checking"); + } + assert(cur == end_idx + 1 || num_regions == 0 || is_available(cur), + "The region at the current position %u must be available or at the end", cur); +#endif + return num_regions; +} + +uint HeterogeneousHeapRegionManager::expand_dram(uint num_regions, WorkGang* pretouch_workers) { + return expand_in_range(start_index_of_dram(), end_index_of_dram(), num_regions, pretouch_workers); +} + +uint HeterogeneousHeapRegionManager::expand_nvdimm(uint num_regions, WorkGang* pretouch_workers) { + return expand_in_range(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, pretouch_workers); +} + +// Follows same logic as expand_at() form HeapRegionManager. +uint HeterogeneousHeapRegionManager::expand_in_range(uint start, uint end, uint num_regions, WorkGang* pretouch_gang) { + + uint so_far = 0; + uint chunk_start = 0; + uint num_last_found = 0; + while (so_far < num_regions && + (num_last_found = find_unavailable_in_range(start, end, &chunk_start)) > 0) { + uint to_commit = MIN2(num_regions - so_far, num_last_found); + make_regions_available(chunk_start, to_commit, pretouch_gang); + so_far += to_commit; + start = chunk_start + to_commit + 1; + } + + return so_far; +} + +// Shrink in the range of indexes which are reserved for dram. +uint HeterogeneousHeapRegionManager::shrink_dram(uint num_regions, bool update_free_list) { + return shrink_in_range(start_index_of_dram(), end_index_of_dram(), num_regions, update_free_list); +} + +// Shrink in the range of indexes which are reserved for nv-dimm. +uint HeterogeneousHeapRegionManager::shrink_nvdimm(uint num_regions, bool update_free_list) { + return shrink_in_range(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, update_free_list); +} + +// Find empty regions in given range, un-commit them and return the count. +uint HeterogeneousHeapRegionManager::shrink_in_range(uint start, uint end, uint num_regions, bool update_free_list) { + + if (num_regions == 0) { + return 0; + } + uint so_far = 0; + uint idx_last_found = 0; + uint num_last_found; + while (so_far < num_regions && + (num_last_found = find_empty_in_range_reverse(start, end, &idx_last_found)) > 0) { + uint to_uncommit = MIN2(num_regions - so_far, num_last_found); + if(update_free_list) { + _free_list.remove_starting_at(at(idx_last_found + num_last_found - to_uncommit), to_uncommit); + } + uncommit_regions(idx_last_found + num_last_found - to_uncommit, to_uncommit); + so_far += to_uncommit; + end = idx_last_found; + } + return so_far; +} + +uint HeterogeneousHeapRegionManager::find_empty_in_range_reverse(uint start_idx, uint end_idx, uint* res_idx) { + guarantee(res_idx != NULL, "checking"); + guarantee(start_idx < max_length(), "checking"); + guarantee(end_idx < max_length(), "checking"); + if(start_idx > end_idx) { + return 0; + } + + uint num_regions_found = 0; + + jlong cur = end_idx; + while (cur >= start_idx && !(is_available(cur) && at(cur)->is_empty())) { + cur--; + } + if (cur == start_idx - 1) { + return num_regions_found; + } + jlong old_cur = cur; + // cur indexes the first empty region + while (cur >= start_idx && is_available(cur) && at(cur)->is_empty()) { + cur--; + } + *res_idx = cur + 1; + num_regions_found = old_cur - cur; + +#ifdef ASSERT + for (uint i = *res_idx; i < (*res_idx + num_regions_found); i++) { + assert(at(i)->is_empty(), "just checking"); + } +#endif + return num_regions_found; +} + +HeapRegion* HeterogeneousHeapRegionManager::allocate_free_region(bool is_old) { + // old region is allocated from nv-dimm, non-old region from dram + // assumption: dram regions take higher indexes + assert(total_regions_committed() <= max_expandable_length(), "must be"); + bool from_head = is_old ? true : false; + HeapRegion* hr = _free_list.remove_region(from_head); + + if (hr != NULL && ( (is_old && !is_in_nvdimm(hr->hrm_index())) || (!is_old && !is_in_dram(hr->hrm_index())) ) ) { + _free_list.add_ordered(hr); + hr = NULL; + } + +#ifdef ASSERT + uint total_committed_before = total_regions_committed(); +#endif + + if (hr == NULL) { + if (!is_old) { + uint ret = shrink_nvdimm(1); + if (ret == 1) { + ret = expand_dram(1, NULL); + assert(ret == 1, "We should be able to commit one region"); + hr = _free_list.remove_region(from_head); + } + } + else { /*is_old*/ + uint ret = shrink_dram(1); + if (ret == 1) { + ret = expand_nvdimm(1, NULL); + assert(ret == 1, "We should be able to commit one region"); + hr = _free_list.remove_region(from_head); + } + } + } +#ifdef ASSERT + assert(total_committed_before == total_regions_committed(), "invariant not met"); + assert(total_regions_committed() <= max_expandable_length(), "post-condition"); +#endif + + if (hr != NULL) { + assert(hr->next() == NULL, "Single region should not have next"); + assert(is_available(hr->hrm_index()), "Must be committed"); + } + assert(total_regions_committed() <= max_expandable_length(), "must be"); + return hr; +} + +uint HeterogeneousHeapRegionManager::find_contiguous_only_empty(size_t num) { + return find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num, true); +} + +uint HeterogeneousHeapRegionManager::find_contiguous_empty_or_unavailable(size_t num) { + return find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num, false); +} + +uint HeterogeneousHeapRegionManager::find_contiguous(size_t start, size_t end, size_t num, bool empty_only) { + uint found = 0; + size_t length_found = 0; + uint cur = (uint)start; + uint length_unavailable = 0; + + while (length_found < num && cur <= end) { + HeapRegion* hr = _regions.get_by_index(cur); + if ((!empty_only && !is_available(cur)) || (is_available(cur) && hr != NULL && hr->is_empty())) { + // This region is a potential candidate for allocation into. + if (!is_available(cur)) { + length_unavailable++; + } + length_found++; + } + else { + // This region is not a candidate. The next region is the next possible one. + found = cur + 1; + length_found = 0; + } + cur++; + } + + if (length_found == num) { + for (uint i = found; i < (found + num); i++) { + HeapRegion* hr = _regions.get_by_index(i); + // sanity check + guarantee((!empty_only && !is_available(i)) || (is_available(i) && hr != NULL && hr->is_empty()), + "Found region sequence starting at " UINT32_FORMAT ", length " SIZE_FORMAT + " that is not empty at " UINT32_FORMAT ". Hr is " PTR_FORMAT, found, num, i, p2i(hr)); + } + if (!empty_only && length_unavailable > (max_expandable_length() - total_regions_committed())) { + // if 'length_unavailable' number of regions will be made available, we will exceed max regions. + return G1_NO_HRM_INDEX; + } + return found; + } + else { + return G1_NO_HRM_INDEX; + } +} + +uint HeterogeneousHeapRegionManager::find_highest_free(bool* expanded) { + // Loop downwards from the highest dram region index, looking for an + // entry which is either free or not yet committed. If not yet + // committed, expand_at that index. + uint curr = end_index_of_dram(); + while (true) { + HeapRegion *hr = _regions.get_by_index(curr); + if (hr == NULL && !(total_regions_committed() < _max_regions)) { + uint res = shrink_nvdimm(1); + if (res == 1) { + res = expand_in_range(curr, curr, 1, NULL); + assert(res == 1, "We should be able to expand since shrink was successful"); + *expanded = true; + return curr; + } + } + else { + if (hr->is_free()) { + *expanded = false; + return curr; + } + } + if (curr == start_index_of_dram()) { + return G1_NO_HRM_INDEX; + } + curr--; + } +} + +// We need to override this since region 0 which serves are dummy region in base class may not be available here. +// This is a corner condition when either number of regions is small. When adaptive sizing is used, initial heap size +// could be just one region. This region is commited in dram to be used for young generation, leaving region 0 (which is in nvdimm) +// unavailable. +HeapRegion* HeterogeneousHeapRegionManager::get_dummy_region() { + uint curr = 0; + + while (curr < _regions.length()) { + if (is_available(curr)) { + return new_heap_region(curr); + } + curr++; + } + assert(false, "We should always find a region available for dummy region"); + return NULL; +} + +// First shrink in dram, then in nv-dimm. +uint HeterogeneousHeapRegionManager::shrink_by(uint num_regions) { + // This call is made at end of full collection. Before making this call the region sets are tore down (tear_down_region_sets()). + // So shrink() calls below do not need to remove uncomitted regions from free list. + uint ret = shrink_dram(num_regions, false /* update_free_list */); + ret += shrink_nvdimm(num_regions - ret, false /* update_free_list */); + return ret; +} + +void HeterogeneousHeapRegionManager::verify() { + HeapRegionManager::verify(); +} + +uint HeterogeneousHeapRegionManager::free_list_dram_length() const { + return _free_list.num_of_regions_in_range(start_index_of_dram(), end_index_of_dram()); +} + +uint HeterogeneousHeapRegionManager::free_list_nvdimm_length() const { + return _free_list.num_of_regions_in_range(start_index_of_nvdimm(), end_index_of_nvdimm()); +} + +bool HeterogeneousHeapRegionManager::is_in_nvdimm(uint index) const { + return index >= start_index_of_nvdimm() && index <= end_index_of_nvdimm(); +} + +bool HeterogeneousHeapRegionManager::is_in_dram(uint index) const { + return index >= start_index_of_dram() && index <= end_index_of_dram(); +} + +// We have to make sure full collection copies all surviving objects to NV-DIMM. +// We might not have enough regions in nvdimm_set, so we need to make more regions on NV-DIMM available for full collection. +// Note: by doing this we are breaking the in-variant that total number of committed regions is equal to current heap size. +// After full collection ends, we will re-establish this in-variant by freeing DRAM regions. +void HeterogeneousHeapRegionManager::prepare_for_full_collection_start() { + _total_commited_before_full_gc = total_regions_committed(); + expand_nvdimm(num_committed_dram(), NULL); + remove_all_free_regions(); +} + +// We need to bring back the total committed regions to before full collection start. +// All regular regions (not pinned regions) in DRAM should be free. +// We shrink all free regions in DRAM and if needed from NV-DIMM (when there are pinned DRAM regions) +void HeterogeneousHeapRegionManager::prepare_for_full_collection_end() { + uint shrink_size = total_regions_committed() - _total_commited_before_full_gc; + uint so_far = 0; + uint idx_last_found = 0; + uint num_last_found; + uint end = (uint)_regions.length() - 1; + while (so_far < shrink_size && + (num_last_found = find_empty_in_range_reverse(0, end, &idx_last_found)) > 0) { + uint to_uncommit = MIN2(shrink_size - so_far, num_last_found); + uncommit_regions(idx_last_found + num_last_found - to_uncommit, to_uncommit); + so_far += to_uncommit; + end = idx_last_found; + } + assert(so_far == shrink_size, "We should be able to shrink this many regions"); +} + +uint HeterogeneousHeapRegionManager::start_index_of_dram() const { return _max_regions;} + +uint HeterogeneousHeapRegionManager::end_index_of_dram() const { return 2*_max_regions - 1; } + +uint HeterogeneousHeapRegionManager::start_index_of_nvdimm() const { return 0; } + +uint HeterogeneousHeapRegionManager::end_index_of_nvdimm() const { return _max_regions - 1; } --- /dev/null 2018-11-19 14:10:43.000000000 -0800 +++ new/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.hpp 2018-11-19 14:10:41.165481800 -0800 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, 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. + * + */ + +#ifndef SHARE_VM_GC_G1_HeterogeneousHeapRegionManager_HPP +#define SHARE_VM_GC_G1_HeterogeneousHeapRegionManager_HPP + +// This class manages heap regions on heterogenous memory comprising of dram and nv-dimm. +// Regions in dram (dram_set) are used for young objects and archive regions (CDS). +// Regions in nv-dimm (nvdimm_set) are used for old objects and humongous objects. +// At any point there are some regions committed on dram and some on nv-dimm with the following guarantees: +// 1. The total number of regions committed in dram and nv-dimm equals the current size of heap. +// 2. Consequently, total number of regions committed is less than or equal to Xmx. +// 3. To maintain the guarantee stated by 1., whenever one set grows (new regions committed), the other set shrinks (regions un-committed). +// 3a. If more dram regions are needed (young generation expansion), corresponding number of regions in nv-dimm are un-committed. +// 3b. When old generation or humongous set grows, and new regions need to be committed to nv-dimm, corresponding number of regions +// are un-committed in dram. +class HeterogeneousHeapRegionManager : public HeapRegionManager { + const uint _max_regions; + uint _max_dram_regions; + uint _max_nvdimm_regions; + uint _total_commited_before_full_gc; + uint _start_index_of_nvdimm; + + uint total_regions_committed() const; + uint num_committed_dram() const; + uint num_committed_nvdimm() const; + + // Similar to find_unavailable_from_idx() function from base class, difference is this function searches in range [start, end]. + uint find_unavailable_in_range(uint start_idx, uint end_idx, uint* res_idx) const; + + // Expand into dram. Maintains the invariant that total number of committed regions is less than current heap size. + uint expand_dram(uint num_regions, WorkGang* pretouch_workers); + + // Expand into nv-dimm. + uint expand_nvdimm(uint num_regions, WorkGang* pretouch_workers); + + // Expand by finding unavailable regions in [start, end] range. + uint expand_in_range(uint start, uint end, uint num_regions, WorkGang* pretouch_workers); + + // Shrink dram set of regions. + uint shrink_dram(uint num_regions, bool update_free_list = true); + + // Shrink nv-dimm set of regions. + uint shrink_nvdimm(uint num_regions, bool update_free_list = true); + + // Shrink regions from [start, end] range. + uint shrink_in_range(uint start, uint end, uint num_regions, bool update_free_list = true); + + // Similar to find_empty_from_idx_reverse() in base class. Only here it searches in a range. + uint find_empty_in_range_reverse(uint start_idx, uint end_idx, uint* res_idx); + + // Similar to find_contiguous() in base class, with [start, end] range + uint find_contiguous(size_t start, size_t end, size_t num, bool empty_only); + + uint free_list_dram_length() const; + uint free_list_nvdimm_length() const; + + // is region with given index in nv-dimm? + bool is_in_nvdimm(uint index) const; + bool is_in_dram(uint index) const; + +public: + + // Empty constructor, we'll initialize it with the initialize() method. + HeterogeneousHeapRegionManager(uint num_regions) : _max_regions(num_regions), _max_dram_regions(0), + _max_nvdimm_regions(0), _start_index_of_nvdimm(0) + {} + + uint start_index_of_nvdimm() const; + uint start_index_of_dram() const; + uint end_index_of_nvdimm() const; + uint end_index_of_dram() const; + + // Override. + HeapRegion* get_dummy_region(); + + // Adjust dram_set to provision 'expected_num_regions' regions. + void adjust_dram_regions(uint expected_num_regions, WorkGang* pretouch_workers); + + // Prepare heap regions before and after full collection. + void prepare_for_full_collection_start(); + void prepare_for_full_collection_end(); + + virtual HeapRegion* allocate_free_region(bool is_old); + + // Return maximum number of regions that heap can expand to. + uint max_expandable_length() const; + + // Override. Expand in nv-dimm. + uint expand_by(uint num_regions, WorkGang* pretouch_workers); + + // Override. + uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers); + + // Override. This function is called for humongous allocation, so we need to find empty regions in nv-dimm. + uint find_contiguous_only_empty(size_t num); + + // Override. This function is called for humongous allocation, so we need to find empty or unavailable regions in nv-dimm. + uint find_contiguous_empty_or_unavailable(size_t num); + + // Overrides base class implementation to find highest free region in dram. + uint find_highest_free(bool* expanded); + + // Override. This fuction is called to shrink the heap, we shrink in dram first then in nv-dimm. + uint shrink_by(uint num_regions_to_remove); + + void verify(); +}; + +#endif --- /dev/null 2018-11-19 14:10:53.000000000 -0800 +++ new/test/hotspot/jtreg/gc/8202286/TestAllocateOldGenAt.java 2018-11-19 14:10:51.106593200 -0800 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018, 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 TestAllocateOldGenAt.java + * @key gc + * @summary Test to check allocation of Java Heap with AllocateOldGenAt option + * @library /test/lib + * @modules java.base/jdk.internal.misc + */ + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import java.util.ArrayList; +import java.util.Collections; + +public class TestAllocateOldGenAt { + private static ArrayList commonOpts; + + public static void main(String args[]) throws Exception { + commonOpts = new ArrayList(); + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(commonOpts, testVmOpts); + } + String test_dir = System.getProperty("test.dir", "."); + Collections.addAll(commonOpts, new String[] {"-XX:+UnlockExperimentalVMOptions", + "-XX:AllocateOldGenAt=" + test_dir, + "-Xmx32m", + "-Xms32m", + "-version"}); + + runTest("-XX:+UseG1GC"); + //runTest("-XX:+UseParallelOldGC"); + } + + private static void runTest(String... extraFlags) throws Exception { + ArrayList testOpts = new ArrayList(); + Collections.addAll(testOpts, commonOpts.toArray(new String[commonOpts.size()])); + Collections.addAll(testOpts, extraFlags); + + System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); + for (int i = 0; i < testOpts.size(); i += 1) { + System.out.print(" " + testOpts.get(i)); + } + System.out.println(); + + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(testOpts.toArray(new String[testOpts.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + + output.shouldHaveExitValue(0); + + } +} --- /dev/null 2018-11-19 14:11:03.000000000 -0800 +++ new/test/hotspot/jtreg/gc/8202286/TestAllocateOldGenAtError.java 2018-11-19 14:11:00.750079000 -0800 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, 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 TestAllocateOldGenAtError.java + * @key gc + * @summary Test to check correct handling of non-existent directory passed to AllocateOldGenAt option + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run main TestAllocateOldGenAtError -XX:+UseG1GC + * @run main TestAllocateOldGenAtError -XX:+UseParallelOldGC + */ + +import java.io.File; +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.UUID; + +public class TestAllocateOldGenAtError { + private static ArrayList commonOpts; + + public static void main(String args[]) throws Exception { + commonOpts = new ArrayList(); + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(commonOpts, testVmOpts); + } + String test_dir = System.getProperty("test.dir", "."); + + File f = null; + do { + f = new File(test_dir, UUID.randomUUID().toString()); + } while(f.exists()); + + Collections.addAll(commonOpts, new String[] {"-XX:+UnlockExperimentalVMOptions", + "-XX:AllocateOldGenAt=" + f.getName(), + "-Xlog:gc+heap=info", + "-Xmx32m", + "-Xms32m", + "-version"}); + + runTest("-XX:+UseG1GC"); + //runTest("-XX:+UseParallelOldGC"); + } + + private static void runTest(String... extraFlags) throws Exception { + ArrayList testOpts = new ArrayList(); + Collections.addAll(testOpts, commonOpts.toArray(new String[commonOpts.size()])); + Collections.addAll(testOpts, extraFlags); + + System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); + for (int i = 0; i < testOpts.size(); i += 1) { + System.out.print(" " + testOpts.get(i)); + } + System.out.println(); + + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(testOpts.toArray(new String[testOpts.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + + output.shouldContain("Could not initialize G1 heap"); + output.shouldContain("Error occurred during initialization of VM"); + output.shouldNotHaveExitValue(0); + + } +} + --- /dev/null 2018-11-19 14:11:13.000000000 -0800 +++ new/test/hotspot/jtreg/gc/8202286/TestAllocateOldGenAtMultiple.java 2018-11-19 14:11:11.017765300 -0800 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018, 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 TestAllocateOldGenAtMultiple.java + * @key gc + * @summary Test to check allocation of Java Heap with AllocateOldGenAt option. Has multiple sub-tests to cover different code paths. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @requires vm.bits == "64" + * @run main TestAllocateOldGenAtMultiple -XX:+UseG1GC + */ + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import java.util.ArrayList; +import java.util.Collections; + +public class TestAllocateOldGenAtMultiple { + public static void main(String args[]) throws Exception { + ArrayList vmOpts = new ArrayList(); + String[] testVmOpts = null; + + String test_dir = System.getProperty("test.dir", "."); + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + testVmOpts = testVmOptsStr.split(" "); + } + + // Extra options for each of the sub-tests + String[] extraOptsList = new String[] { + "-Xmx32m -Xms32m -XX:+UseCompressedOops", // 1. With compressedoops enabled. + "-Xmx32m -Xms32m -XX:-UseCompressedOops", // 2. With compressedoops disabled. + "-Xmx32m -Xms32m -XX:HeapBaseMinAddress=3g", // 3. With user specified HeapBaseMinAddress. + "-Xmx4g -Xms4g", // 4. With larger heap size (UnscaledNarrowOop not possible). + "-Xmx4g -Xms4g -XX:+UseLargePages", // 5. Set UseLargePages. + "-Xmx4g -Xms4g -XX:+UseNUMA" // 6. Set UseNUMA. + }; + + for(String extraOpts : extraOptsList) { + vmOpts.clear(); + if(testVmOpts != null) { + Collections.addAll(vmOpts, testVmOpts); + } + // Add extra options specific to the sub-test. + String[] extraOptsArray = extraOpts.split(" "); + if(extraOptsArray != null) { + Collections.addAll(vmOpts, extraOptsArray); + } + // Add common options + Collections.addAll(vmOpts, new String[] {"-XX:+UnlockExperimentalVMOptions", + "-XX:AllocateOldGenAt=" + test_dir, + "-version"}); + + System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); + for (int i = 0; i < vmOpts.size(); i += 1) { + System.out.print(" " + vmOpts.get(i)); + } + System.out.println(); + + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + + output.shouldHaveExitValue(0); + } + } +} --- /dev/null 2018-11-19 14:11:23.000000000 -0800 +++ new/test/hotspot/jtreg/gc/8202286/TestHumongousObjectsOnNvdimm.java 2018-11-19 14:11:20.532491800 -0800 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018 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 TestHumongousObjectsOnNvdimm + * @summary Check that humongous objects reside in nv-dimm + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main TestHumongousObjectsOnNvdimm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Asserts; +import sun.hotspot.WhiteBox; + +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; +import gc.testlibrary.Helpers; + +/** + * Test spawns HumongousObjectTest in a separate VM and expects that it + * completes without a RuntimeException. + */ +public class TestHumongousObjectsOnNvdimm { + + private static ArrayList testOpts; + + public static void main(String args[]) throws Exception { + testOpts = new ArrayList(); + + String[] common_options = new String[] { + "-Xbootclasspath/a:.", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:AllocateOldGenAt="+System.getProperty("test.dir", "."), + "-Xms10M", "-Xmx10M", + "-XX:G1HeapRegionSize=1m" + }; + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(testOpts, testVmOpts); + } + Collections.addAll(testOpts, common_options); + + // Test with G1 GC + runTest("-XX:+UseG1GC"); + } + + private static void runTest(String... extraFlags) throws Exception { + Collections.addAll(testOpts, extraFlags); + testOpts.add(HumongousObjectTest.class.getName()); + System.out.println(testOpts); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(false, + testOpts.toArray(new String[testOpts.size()])); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + output.shouldHaveExitValue(0); + } +} + +/** + * This class tests that a humongous object resides in NVDIMM. + */ +class HumongousObjectTest { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static void validateObject(Object o) { + Asserts.assertTrue(WB.isObjectInOldGen(o), + "Object is supposed to be in OldGen"); + + long obj_addr = WB.getObjectAddress(o); + long nvdimm_heap_start = WB.g1NvdimmReservedStart(); + long nvdimm_heap_end = WB.g1NvdimmReservedEnd(); + + Asserts.assertTrue(WB.g1BelongsToHumongousRegion(obj_addr), "Object address should be in Humongous set"); + Asserts.assertTrue(obj_addr >= nvdimm_heap_start && obj_addr < nvdimm_heap_end, + "Humongous object does not reside in NVDIMM"); + } + + public static void main(String args[]) throws Exception { + // allocate an humongous object + int byteArrayMemoryOverhead = Helpers.detectByteArrayAllocationOverhead(); + int MinByteArrayHumongousSize = (WB.g1RegionSize() / 2) - byteArrayMemoryOverhead + 1; + byte[] obj = new byte[MinByteArrayHumongousSize]; + + validateObject(obj); + } +} --- /dev/null 2018-11-19 14:11:32.000000000 -0800 +++ new/test/hotspot/jtreg/gc/8202286/TestOldObjectsOnNvdimm.java 2018-11-19 14:11:29.843078600 -0800 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018 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 TestOldObjectsOnNvdimm + * @summary Check that objects in old generation reside in dram. + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main TestOldObjectsOnNvdimm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Asserts; +import sun.hotspot.WhiteBox; + +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; + +/* + * Test spawns OldObjectTest in a separate VM and expects that it + * completes without a RuntimeException. + */ +public class TestOldObjectsOnNvdimm { + + public static final int ALLOCATION_SIZE = 100; + private static ArrayList testOpts; + + public static void main(String args[]) throws Exception { + testOpts = new ArrayList(); + + String[] common_options = new String[] { + "-Xbootclasspath/a:.", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:AllocateOldGenAt="+System.getProperty("test.dir", "."), + "-Xms10M", "-Xmx10M", + "-XX:MaxTenuringThreshold=1" // Promote objects to Old Gen + }; + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(testOpts, testVmOpts); + } + Collections.addAll(testOpts, common_options); + + // Test with G1 GC + runTest("-XX:+UseG1GC"); + // Test with ParallelOld GC + // runTest("-XX:+UseParallelOldGC"); + } + + private static void runTest(String... extraFlags) throws Exception { + Collections.addAll(testOpts, extraFlags); + testOpts.add(OldObjectTest.class.getName()); + System.out.println(testOpts); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(false, + testOpts.toArray(new String[testOpts.size()])); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + output.shouldHaveExitValue(0); + } +} + +/* + * This class tests that object is in Old generation after tenuring and resides in NVDIMM. + * The necessary condition for this test is running in VM with the following flags: + * -XX:AllocateOldGenAt=, -XX:MaxTenuringThreshold=1 + */ +class OldObjectTest { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static void validateOldObject(Object o) { + Asserts.assertTrue(WB.isObjectInOldGen(o), + "Object is supposed to be in OldGen"); + + long oldObj_addr = WB.getObjectAddress(o); + long nvdimm_heap_start = WB.g1NvdimmReservedStart(); + long nvdimm_heap_end = WB.g1NvdimmReservedEnd(); + + Asserts.assertTrue(oldObj_addr >= nvdimm_heap_start && oldObj_addr <= nvdimm_heap_end, + "Old object does not reside in NVDIMM"); + } + + public static void main(String args[]) throws Exception { + // allocate an object and perform Young GCs to promote it to Old + byte[] oldObj = new byte[TestOldObjectsOnNvdimm.ALLOCATION_SIZE]; + WB.youngGC(); + WB.youngGC(); + validateOldObject(oldObj); + } +} --- /dev/null 2018-11-19 14:11:42.000000000 -0800 +++ new/test/hotspot/jtreg/gc/8202286/TestYoungObjectsOnDram.java 2018-11-19 14:11:39.577205300 -0800 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 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 TestYoungObjectsOnDram + * @summary Check that objects in young generation reside in dram. + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main TestYoungObjectsOnDram -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Asserts; +import sun.hotspot.WhiteBox; + +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; + +/** + * Test spawns YoungObjectTest in a separate VM and expects that it + * completes without a RuntimeException. + */ +public class TestYoungObjectsOnDram { + + public static final int ALLOCATION_SIZE = 100; + private static ArrayList testOpts; + + public static void main(String args[]) throws Exception { + testOpts = new ArrayList(); + + String[] common_options = new String[] { + "-Xbootclasspath/a:.", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:AllocateOldGenAt="+System.getProperty("test.dir", "."), + "-XX:SurvivorRatio=1", // Survivor-to-eden ratio is 1:1 + "-Xms10M", "-Xmx10M", + "-XX:InitialTenuringThreshold=15" // avoid promotion of objects to Old Gen + }; + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(testOpts, testVmOpts); + } + Collections.addAll(testOpts, common_options); + + // Test with G1 GC + runTest("-XX:+UseG1GC"); + // Test with ParallelOld GC + //runTest("-XX:+UseParallelOldGC"); + } + + private static void runTest(String... extraFlags) throws Exception { + Collections.addAll(testOpts, extraFlags); + testOpts.add(YoungObjectTest.class.getName()); + System.out.println(testOpts); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(false, + testOpts.toArray(new String[testOpts.size()])); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + output.shouldHaveExitValue(0); + } +} + +/** + * This class tests that newly created object is in Young generation and resides in DRAM. + * The necessary condition for this test is running in VM with the following flags: + * -XX:AllocateOldGenAt=, -XX:InitialTenuringThreshold=15, -XX:SurvivorRatio=1 + */ +class YoungObjectTest { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static void validateYoungObject(Object o) { + Asserts.assertTrue(!WB.isObjectInOldGen(o), + "Object is supposed to be in YoungGen"); + + long youngObj_addr = WB.getObjectAddress(o); + long dram_heap_start = WB.g1DramReservedStart(); + long dram_heap_end = WB.g1DramReservedEnd(); + + Asserts.assertTrue(youngObj_addr >= dram_heap_start && youngObj_addr <= dram_heap_end, + "Young object does not reside in DRAM"); + } + + public static void main(String args[]) throws Exception { + // allocate an object + byte[] youngObj = new byte[TestYoungObjectsOnDram.ALLOCATION_SIZE]; + validateYoungObject(youngObj); + + // Start a Young GC and check that object is still in DRAM. + // We have used -XX:InitialTenuringThreshold=15 to invoke this test + WB.youngGC(); + validateYoungObject(youngObj); + } +}