--- old/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp 2011-03-15 09:38:19.184253000 -0700 +++ new/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp 2011-03-15 09:38:18.971518000 -0700 @@ -31,6 +31,7 @@ #include "gc_implementation/g1/heapRegionSeq.inline.hpp" #include "memory/space.inline.hpp" #include "runtime/atomic.hpp" +#include "runtime/java.hpp" #include "utilities/copy.hpp" // Possible sizes for the card counts cache: odd primes that roughly double in size. @@ -43,7 +44,7 @@ ConcurrentG1Refine::ConcurrentG1Refine() : _card_counts(NULL), _card_epochs(NULL), - _n_card_counts(0), _max_n_card_counts(0), + _n_card_counts(0), _max_cards(0), _max_n_card_counts(0), _cache_size_index(0), _expand_card_counts(false), _hot_cache(NULL), _def_use_cache(false), _use_cache(false), @@ -98,27 +99,35 @@ void ConcurrentG1Refine::init() { if (G1ConcRSLogCacheSize > 0) { _g1h = G1CollectedHeap::heap(); - _max_n_card_counts = - (unsigned) (_g1h->max_capacity() >> CardTableModRefBS::card_shift); + + _max_cards = (unsigned)(_g1h->max_capacity() >> CardTableModRefBS::card_shift); + _max_n_card_counts = (unsigned)(_max_cards * ((float)G1MaxHotCardCountSizePercent / 100.0)); size_t max_card_num = ((size_t)1 << (sizeof(unsigned)*BitsPerByte-1)) - 1; - guarantee(_max_n_card_counts < max_card_num, "card_num representation"); + guarantee(_max_cards < max_card_num, "card_num representation"); - int desired = _max_n_card_counts / InitialCacheFraction; - for (_cache_size_index = 0; - _cc_cache_sizes[_cache_size_index] >= 0; _cache_size_index++) { - if (_cc_cache_sizes[_cache_size_index] >= desired) break; - } - _cache_size_index = MAX2(0, (_cache_size_index - 1)); + int desired = _max_cards / InitialCacheFraction; - int initial_size = _cc_cache_sizes[_cache_size_index]; - if (initial_size < 0) initial_size = _max_n_card_counts; + // Find the index into cache size array that is of a size that's + // large enough to hold "desired". + assert(_n_card_counts == 0, "pre-condition"); + assert(_max_n_card_counts > 0, "pre-condition"); - // Make sure we don't go bigger than we will ever need - _n_card_counts = MIN2((unsigned) initial_size, _max_n_card_counts); + int index; + for (index = 0; _cc_cache_sizes[index] >= 0; index++) { + if (_cc_cache_sizes[index] >= desired) break; + } + assert(index < MAX_CC_CACHE_INDEX, "post condition"); - _card_counts = NEW_C_HEAP_ARRAY(CardCountCacheEntry, _n_card_counts); - _card_epochs = NEW_C_HEAP_ARRAY(CardEpochCacheEntry, _n_card_counts); + // _cc_cache_sizes[index - 1] <= desired < _cc_cache_sizes[index] + index = MAX2(0, (index - 1)); + + if (!expand_card_count_cache(index)) { + // Allocation was unsuccessful - exit + vm_exit_during_initialization("Could not reserve enough space for card count cache"); + } + assert(_n_card_counts > 0, "post-condition"); + assert(_cache_size_index == index, "post-condition"); Copy::fill_to_bytes(&_card_counts[0], _n_card_counts * sizeof(CardCountCacheEntry)); @@ -163,10 +172,13 @@ ConcurrentG1Refine::~ConcurrentG1Refine() { if (G1ConcRSLogCacheSize > 0) { + // We access the allocation routines directly for + // the counts and epochs. assert(_card_counts != NULL, "Logic"); - FREE_C_HEAP_ARRAY(CardCountCacheEntry, _card_counts); + os::free(_card_counts); assert(_card_epochs != NULL, "Logic"); - FREE_C_HEAP_ARRAY(CardEpochCacheEntry, _card_epochs); + os::free(_card_epochs); + assert(_hot_cache != NULL, "Logic"); FREE_C_HEAP_ARRAY(jbyte*, _hot_cache); } @@ -382,29 +394,81 @@ } } -void ConcurrentG1Refine::expand_card_count_cache() { +// The arrays used to hold the card counts and the epochs must have +// a 1:1 correspondence. Hence they are allocated and freed together +// Returns true if the allocations of both the counts and epochs +// were successful; false otherwise. +bool ConcurrentG1Refine::allocate_card_count_cache(int n, + CardCountCacheEntry** counts, + CardEpochCacheEntry** epochs) { + assert(*counts == NULL, "out param"); + assert(*epochs == NULL, "out param"); + + size_t counts_size = n * sizeof(CardCountCacheEntry); + size_t epochs_size = n * sizeof(CardEpochCacheEntry); + + *counts = (CardCountCacheEntry*) os::malloc(counts_size); + if (*counts == NULL) { + // allocation was unsuccessful + return false; + } + + *epochs = (CardEpochCacheEntry*) os::malloc(epochs_size); + if (*epochs == NULL) { + // allocation was unsuccessful - free counts array + assert(*counts != NULL, "must be"); + os::free(*counts); + *counts = NULL; + return false; + } + + // We successfully allocated both counts and epochs + return true; +} + +// Returns true if the card counts/epochs cache was +// successfully expanded; false otherwise. +bool ConcurrentG1Refine::expand_card_count_cache(int cache_size_idx) { + // Can we expand the card count and epoch tables? if (_n_card_counts < _max_n_card_counts) { - int new_idx = _cache_size_index+1; - int new_size = _cc_cache_sizes[new_idx]; - if (new_size < 0) new_size = _max_n_card_counts; + int cache_size = (cache_size_idx < MAX_CC_CACHE_INDEX ? _cc_cache_sizes[cache_size_idx] + : _max_n_card_counts); + if (cache_size < 0) cache_size = _max_n_card_counts; // Make sure we don't go bigger than we will ever need - new_size = MIN2((unsigned) new_size, _max_n_card_counts); + cache_size = MIN2((unsigned) cache_size, _max_n_card_counts); + + // Should we expand the card count and card epoch tables? + if (cache_size > (int)_n_card_counts) { + // We have been asked to allocate new, larger, arrays for + // the card counts and the epochs. Attempt the allocation + // of both before we free the existing arrays in case + // the allocation is unsuccessful... + CardCountCacheEntry* counts = NULL; + CardEpochCacheEntry* epochs = NULL; + + if (allocate_card_count_cache(cache_size, &counts, &epochs)) { + // Allocation was successful. + // We can just free the old arrays; we're + // not interested in preserving the contents + if (_card_counts != NULL) os::free(_card_counts); + if (_card_epochs != NULL) os::free(_card_epochs); + + // Cache the size of the arrays and the index that got us there. + _n_card_counts = cache_size; + _cache_size_index = cache_size_idx; - // Expand the card count and card epoch tables - if (new_size > (int)_n_card_counts) { - // We can just free and allocate a new array as we're - // not interested in preserving the contents - assert(_card_counts != NULL, "Logic!"); - assert(_card_epochs != NULL, "Logic!"); - FREE_C_HEAP_ARRAY(CardCountCacheEntry, _card_counts); - FREE_C_HEAP_ARRAY(CardEpochCacheEntry, _card_epochs); - _n_card_counts = new_size; - _card_counts = NEW_C_HEAP_ARRAY(CardCountCacheEntry, _n_card_counts); - _card_epochs = NEW_C_HEAP_ARRAY(CardEpochCacheEntry, _n_card_counts); - _cache_size_index = new_idx; + _card_counts = counts; + _card_epochs = epochs; + + // We successfully allocated/expanded the caches. + return true; + } } } + + // We did not successfully expand the caches. + return false; } void ConcurrentG1Refine::clear_and_record_card_counts() { @@ -415,10 +479,16 @@ #endif if (_expand_card_counts) { - expand_card_count_cache(); + int new_idx = _cache_size_index + 1; + + if (expand_card_count_cache(new_idx)) { + // Allocation was successful and _n_card_counts has + // been updated to the new size. We only need to clear + // the epochs so we don't read a bogus epoch value + // when inserting a card into the hot card cache. + Copy::fill_to_bytes(&_card_epochs[0], _n_card_counts * sizeof(CardEpochCacheEntry)); + } _expand_card_counts = false; - // Only need to clear the epochs. - Copy::fill_to_bytes(&_card_epochs[0], _n_card_counts * sizeof(CardEpochCacheEntry)); } int this_epoch = (int) _n_periods; --- old/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp 2011-03-15 09:38:20.538981000 -0700 +++ new/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp 2011-03-15 09:38:20.328235000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2011, 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 @@ -94,7 +94,7 @@ } CardEpochCacheEntry; julong make_epoch_entry(unsigned int card_num, unsigned int epoch) { - assert(0 <= card_num && card_num < _max_n_card_counts, "Bounds"); + assert(0 <= card_num && card_num < _max_cards, "Bounds"); assert(0 <= epoch && epoch <= _n_periods, "must be"); return ((julong) card_num << card_num_shift) | epoch; @@ -119,13 +119,22 @@ // The current number of buckets in the card count cache unsigned _n_card_counts; - // The max number of buckets required for the number of - // cards for the entire reserved heap + // The number of cards for the entire reserved heap + unsigned _max_cards; + + // The max number of buckets for the card counts and epochs caches. + // This is the maximum that the counts and epochs will grow to. + // It is specified as a fraction or percentage of _max_cards using + // G1MaxHotCardCountSizePercent (currently 50%). unsigned _max_n_card_counts; // Possible sizes of the cache: odd primes that roughly double in size. // (See jvmtiTagMap.cpp). - static int _cc_cache_sizes[]; + enum { + MAX_CC_CACHE_INDEX = 15 // maximum index into the cache size array. + }; + + static int _cc_cache_sizes[MAX_CC_CACHE_INDEX]; // The index in _cc_cache_sizes corresponding to the size of // _card_counts. @@ -147,8 +156,18 @@ CardTableModRefBS* _ct_bs; G1CollectedHeap* _g1h; - // Expands the array that holds the card counts to the next size up - void expand_card_count_cache(); + // Helper routine for expand_card_count_cache(). + // The arrays used to hold the card counts and the epochs must have + // a 1:1 correspondence. Hence they are allocated and freed together. + // Returns true if the allocations of both the counts and epochs + // were successful; false otherwise. + bool allocate_card_count_cache(int n, CardCountCacheEntry** counts, + CardEpochCacheEntry** epochs); + + // Expands the arrays that hold the card counts and epochs + // to the cache size at index. Returns true if the expansion/ + // allocation was successful; false otherwise. + bool expand_card_count_cache(int index); // hash a given key (index of card_ptr) with the specified size static unsigned int hash(size_t key, int size) { --- old/src/share/vm/gc_implementation/g1/g1_globals.hpp 2011-03-15 09:38:21.873103000 -0700 +++ new/src/share/vm/gc_implementation/g1/g1_globals.hpp 2011-03-15 09:38:21.658688000 -0700 @@ -193,6 +193,10 @@ develop(intx, G1ConcRSHotCardLimit, 4, \ "The threshold that defines (>=) a hot card.") \ \ + develop(intx, G1MaxHotCardCountSizePercent, 50, \ + "The maximum size of the hot card count cache as a " \ + "percentage of the number of cards for the maximum heap.") \ + \ develop(bool, G1PrintOopAppls, false, \ "When true, print applications of closures to external locs.") \ \