Mercurial > hg > graal-jvmci-8
annotate src/share/vm/gc_implementation/g1/collectionSetChooser.cpp @ 3979:4dfb2df418f2
6484982: G1: process references during evacuation pauses
Summary: G1 now uses two reference processors - one is used by concurrent marking and the other is used by STW GCs (both full and incremental evacuation pauses). In an evacuation pause, the reference processor is embedded into the closures used to scan objects. Doing so causes causes reference objects to be 'discovered' by the reference processor. At the end of the evacuation pause, these discovered reference objects are processed - preserving (and copying) referent objects (and their reachable graphs) as appropriate.
Reviewed-by: ysr, jwilhelm, brutisso, stefank, tonyp
author | johnc |
---|---|
date | Thu, 22 Sep 2011 10:57:37 -0700 |
parents | 20213c8a3c40 |
children | b9390528617c |
rev | line source |
---|---|
342 | 1 /* |
3914
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
2 * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. |
342 | 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * | |
5 * This code is free software; you can redistribute it and/or modify it | |
6 * under the terms of the GNU General Public License version 2 only, as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1103
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1103
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1103
diff
changeset
|
21 * questions. |
342 | 22 * |
23 */ | |
24 | |
1972 | 25 #include "precompiled.hpp" |
26 #include "gc_implementation/g1/collectionSetChooser.hpp" | |
27 #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" | |
28 #include "gc_implementation/g1/g1CollectorPolicy.hpp" | |
3914
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
29 #include "gc_implementation/g1/g1ErgoVerbose.hpp" |
1972 | 30 #include "memory/space.inline.hpp" |
342 | 31 |
32 CSetChooserCache::CSetChooserCache() { | |
33 for (int i = 0; i < CacheLength; ++i) | |
34 _cache[i] = NULL; | |
35 clear(); | |
36 } | |
37 | |
38 void CSetChooserCache::clear() { | |
39 _occupancy = 0; | |
40 _first = 0; | |
41 for (int i = 0; i < CacheLength; ++i) { | |
42 HeapRegion *hr = _cache[i]; | |
43 if (hr != NULL) | |
44 hr->set_sort_index(-1); | |
45 _cache[i] = NULL; | |
46 } | |
47 } | |
48 | |
49 #ifndef PRODUCT | |
50 bool CSetChooserCache::verify() { | |
51 int index = _first; | |
52 HeapRegion *prev = NULL; | |
53 for (int i = 0; i < _occupancy; ++i) { | |
54 guarantee(_cache[index] != NULL, "cache entry should not be empty"); | |
55 HeapRegion *hr = _cache[index]; | |
56 guarantee(!hr->is_young(), "should not be young!"); | |
57 if (prev != NULL) { | |
58 guarantee(prev->gc_efficiency() >= hr->gc_efficiency(), | |
59 "cache should be correctly ordered"); | |
60 } | |
61 guarantee(hr->sort_index() == get_sort_index(index), | |
62 "sort index should be correct"); | |
63 index = trim_index(index + 1); | |
64 prev = hr; | |
65 } | |
66 | |
67 for (int i = 0; i < (CacheLength - _occupancy); ++i) { | |
68 guarantee(_cache[index] == NULL, "cache entry should be empty"); | |
69 index = trim_index(index + 1); | |
70 } | |
71 | |
72 guarantee(index == _first, "we should have reached where we started from"); | |
73 return true; | |
74 } | |
75 #endif // PRODUCT | |
76 | |
77 void CSetChooserCache::insert(HeapRegion *hr) { | |
78 assert(!is_full(), "cache should not be empty"); | |
79 hr->calc_gc_efficiency(); | |
80 | |
81 int empty_index; | |
82 if (_occupancy == 0) { | |
83 empty_index = _first; | |
84 } else { | |
85 empty_index = trim_index(_first + _occupancy); | |
86 assert(_cache[empty_index] == NULL, "last slot should be empty"); | |
87 int last_index = trim_index(empty_index - 1); | |
88 HeapRegion *last = _cache[last_index]; | |
89 assert(last != NULL,"as the cache is not empty, last should not be empty"); | |
90 while (empty_index != _first && | |
91 last->gc_efficiency() < hr->gc_efficiency()) { | |
92 _cache[empty_index] = last; | |
93 last->set_sort_index(get_sort_index(empty_index)); | |
94 empty_index = last_index; | |
95 last_index = trim_index(last_index - 1); | |
96 last = _cache[last_index]; | |
97 } | |
98 } | |
99 _cache[empty_index] = hr; | |
100 hr->set_sort_index(get_sort_index(empty_index)); | |
101 | |
102 ++_occupancy; | |
103 assert(verify(), "cache should be consistent"); | |
104 } | |
105 | |
106 HeapRegion *CSetChooserCache::remove_first() { | |
107 if (_occupancy > 0) { | |
108 assert(_cache[_first] != NULL, "cache should have at least one region"); | |
109 HeapRegion *ret = _cache[_first]; | |
110 _cache[_first] = NULL; | |
111 ret->set_sort_index(-1); | |
112 --_occupancy; | |
113 _first = trim_index(_first + 1); | |
114 assert(verify(), "cache should be consistent"); | |
115 return ret; | |
116 } else { | |
117 return NULL; | |
118 } | |
119 } | |
120 | |
121 // this is a bit expensive... but we expect that it should not be called | |
122 // to often. | |
123 void CSetChooserCache::remove(HeapRegion *hr) { | |
124 assert(_occupancy > 0, "cache should not be empty"); | |
125 assert(hr->sort_index() < -1, "should already be in the cache"); | |
126 int index = get_index(hr->sort_index()); | |
127 assert(_cache[index] == hr, "index should be correct"); | |
128 int next_index = trim_index(index + 1); | |
129 int last_index = trim_index(_first + _occupancy - 1); | |
130 while (index != last_index) { | |
131 assert(_cache[next_index] != NULL, "should not be null"); | |
132 _cache[index] = _cache[next_index]; | |
133 _cache[index]->set_sort_index(get_sort_index(index)); | |
134 | |
135 index = next_index; | |
136 next_index = trim_index(next_index+1); | |
137 } | |
138 assert(index == last_index, "should have reached the last one"); | |
139 _cache[index] = NULL; | |
140 hr->set_sort_index(-1); | |
141 --_occupancy; | |
142 assert(verify(), "cache should be consistent"); | |
143 } | |
144 | |
145 static inline int orderRegions(HeapRegion* hr1, HeapRegion* hr2) { | |
146 if (hr1 == NULL) { | |
147 if (hr2 == NULL) return 0; | |
148 else return 1; | |
149 } else if (hr2 == NULL) { | |
150 return -1; | |
151 } | |
152 if (hr2->gc_efficiency() < hr1->gc_efficiency()) return -1; | |
153 else if (hr1->gc_efficiency() < hr2->gc_efficiency()) return 1; | |
154 else return 0; | |
155 } | |
156 | |
157 static int orderRegions(HeapRegion** hr1p, HeapRegion** hr2p) { | |
158 return orderRegions(*hr1p, *hr2p); | |
159 } | |
160 | |
161 CollectionSetChooser::CollectionSetChooser() : | |
162 // The line below is the worst bit of C++ hackery I've ever written | |
163 // (Detlefs, 11/23). You should think of it as equivalent to | |
164 // "_regions(100, true)": initialize the growable array and inform it | |
1688
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
165 // that it should allocate its elem array(s) on the C heap. |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
166 // |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
167 // The first argument, however, is actually a comma expression |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
168 // (set_allocation_type(this, C_HEAP), 100). The purpose of the |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
169 // set_allocation_type() call is to replace the default allocation |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
170 // type for embedded objects STACK_OR_EMBEDDED with C_HEAP. It will |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
171 // allow to pass the assert in GenericGrowableArray() which checks |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
172 // that a growable array object must be on C heap if elements are. |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
173 // |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
174 // Note: containing object is allocated on C heap since it is CHeapObj. |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
175 // |
2dfd013a7465
6975078: assert(allocated_on_res_area() || allocated_on_C_heap() || allocated_on_arena()
kvn
parents:
1552
diff
changeset
|
176 _markedRegions((ResourceObj::set_allocation_type((address)&_markedRegions, |
342 | 177 ResourceObj::C_HEAP), |
178 100), | |
179 true), | |
180 _curMarkedIndex(0), | |
181 _numMarkedRegions(0), | |
182 _unmarked_age_1_returned_as_new(false), | |
183 _first_par_unreserved_idx(0) | |
184 {} | |
185 | |
186 | |
187 | |
188 #ifndef PRODUCT | |
189 bool CollectionSetChooser::verify() { | |
190 int index = 0; | |
191 guarantee(_curMarkedIndex <= _numMarkedRegions, | |
192 "_curMarkedIndex should be within bounds"); | |
193 while (index < _curMarkedIndex) { | |
194 guarantee(_markedRegions.at(index++) == NULL, | |
195 "all entries before _curMarkedIndex should be NULL"); | |
196 } | |
197 HeapRegion *prev = NULL; | |
198 while (index < _numMarkedRegions) { | |
199 HeapRegion *curr = _markedRegions.at(index++); | |
200 if (curr != NULL) { | |
201 int si = curr->sort_index(); | |
202 guarantee(!curr->is_young(), "should not be young!"); | |
203 guarantee(si > -1 && si == (index-1), "sort index invariant"); | |
204 if (prev != NULL) { | |
205 guarantee(orderRegions(prev, curr) != 1, "regions should be sorted"); | |
206 } | |
207 prev = curr; | |
208 } | |
209 } | |
210 return _cache.verify(); | |
211 } | |
212 #endif | |
213 | |
214 bool | |
215 CollectionSetChooser::addRegionToCache() { | |
216 assert(!_cache.is_full(), "cache should not be full"); | |
217 | |
218 HeapRegion *hr = NULL; | |
219 while (hr == NULL && _curMarkedIndex < _numMarkedRegions) { | |
220 hr = _markedRegions.at(_curMarkedIndex++); | |
221 } | |
222 if (hr == NULL) | |
223 return false; | |
224 assert(!hr->is_young(), "should not be young!"); | |
225 assert(hr->sort_index() == _curMarkedIndex-1, "sort_index invariant"); | |
226 _markedRegions.at_put(hr->sort_index(), NULL); | |
227 _cache.insert(hr); | |
228 assert(!_cache.is_empty(), "cache should not be empty"); | |
229 assert(verify(), "cache should be consistent"); | |
230 return false; | |
231 } | |
232 | |
233 void | |
234 CollectionSetChooser::fillCache() { | |
235 while (!_cache.is_full() && addRegionToCache()) { | |
236 } | |
237 } | |
238 | |
239 void | |
240 CollectionSetChooser::sortMarkedHeapRegions() { | |
241 guarantee(_cache.is_empty(), "cache should be empty"); | |
242 // First trim any unused portion of the top in the parallel case. | |
243 if (_first_par_unreserved_idx > 0) { | |
244 if (G1PrintParCleanupStats) { | |
245 gclog_or_tty->print(" Truncating _markedRegions from %d to %d.\n", | |
246 _markedRegions.length(), _first_par_unreserved_idx); | |
247 } | |
248 assert(_first_par_unreserved_idx <= _markedRegions.length(), | |
249 "Or we didn't reserved enough length"); | |
250 _markedRegions.trunc_to(_first_par_unreserved_idx); | |
251 } | |
252 _markedRegions.sort(orderRegions); | |
253 assert(_numMarkedRegions <= _markedRegions.length(), "Requirement"); | |
254 assert(_numMarkedRegions == 0 | |
255 || _markedRegions.at(_numMarkedRegions-1) != NULL, | |
256 "Testing _numMarkedRegions"); | |
257 assert(_numMarkedRegions == _markedRegions.length() | |
258 || _markedRegions.at(_numMarkedRegions) == NULL, | |
259 "Testing _numMarkedRegions"); | |
260 if (G1PrintParCleanupStats) { | |
261 gclog_or_tty->print_cr(" Sorted %d marked regions.", _numMarkedRegions); | |
262 } | |
263 for (int i = 0; i < _numMarkedRegions; i++) { | |
264 assert(_markedRegions.at(i) != NULL, "Should be true by sorting!"); | |
265 _markedRegions.at(i)->set_sort_index(i); | |
2435
371bbc844bf1
7027766: G1: introduce flag to dump the liveness information per region at the end of marking
tonyp
parents:
1972
diff
changeset
|
266 } |
371bbc844bf1
7027766: G1: introduce flag to dump the liveness information per region at the end of marking
tonyp
parents:
1972
diff
changeset
|
267 if (G1PrintRegionLivenessInfo) { |
371bbc844bf1
7027766: G1: introduce flag to dump the liveness information per region at the end of marking
tonyp
parents:
1972
diff
changeset
|
268 G1PrintRegionLivenessInfoClosure cl(gclog_or_tty, "Post-Sorting"); |
371bbc844bf1
7027766: G1: introduce flag to dump the liveness information per region at the end of marking
tonyp
parents:
1972
diff
changeset
|
269 for (int i = 0; i < _numMarkedRegions; ++i) { |
371bbc844bf1
7027766: G1: introduce flag to dump the liveness information per region at the end of marking
tonyp
parents:
1972
diff
changeset
|
270 HeapRegion* r = _markedRegions.at(i); |
371bbc844bf1
7027766: G1: introduce flag to dump the liveness information per region at the end of marking
tonyp
parents:
1972
diff
changeset
|
271 cl.doHeapRegion(r); |
342 | 272 } |
273 } | |
274 assert(verify(), "should now be sorted"); | |
275 } | |
276 | |
277 void | |
278 CollectionSetChooser::addMarkedHeapRegion(HeapRegion* hr) { | |
279 assert(!hr->isHumongous(), | |
280 "Humongous regions shouldn't be added to the collection set"); | |
281 assert(!hr->is_young(), "should not be young!"); | |
282 _markedRegions.append(hr); | |
283 _numMarkedRegions++; | |
284 hr->calc_gc_efficiency(); | |
285 } | |
286 | |
287 void | |
288 CollectionSetChooser:: | |
289 prepareForAddMarkedHeapRegionsPar(size_t n_regions, size_t chunkSize) { | |
290 _first_par_unreserved_idx = 0; | |
291 size_t max_waste = ParallelGCThreads * chunkSize; | |
292 // it should be aligned with respect to chunkSize | |
293 size_t aligned_n_regions = | |
294 (n_regions + (chunkSize - 1)) / chunkSize * chunkSize; | |
295 assert( aligned_n_regions % chunkSize == 0, "should be aligned" ); | |
296 _markedRegions.at_put_grow((int)(aligned_n_regions + max_waste - 1), NULL); | |
297 } | |
298 | |
299 jint | |
300 CollectionSetChooser::getParMarkedHeapRegionChunk(jint n_regions) { | |
301 jint res = Atomic::add(n_regions, &_first_par_unreserved_idx); | |
302 assert(_markedRegions.length() > res + n_regions - 1, | |
303 "Should already have been expanded"); | |
304 return res - n_regions; | |
305 } | |
306 | |
307 void | |
308 CollectionSetChooser::setMarkedHeapRegion(jint index, HeapRegion* hr) { | |
309 assert(_markedRegions.at(index) == NULL, "precondition"); | |
310 assert(!hr->is_young(), "should not be young!"); | |
311 _markedRegions.at_put(index, hr); | |
312 hr->calc_gc_efficiency(); | |
313 } | |
314 | |
315 void | |
316 CollectionSetChooser::incNumMarkedHeapRegions(jint inc_by) { | |
317 (void)Atomic::add(inc_by, &_numMarkedRegions); | |
318 } | |
319 | |
320 void | |
321 CollectionSetChooser::clearMarkedHeapRegions(){ | |
322 for (int i = 0; i < _markedRegions.length(); i++) { | |
323 HeapRegion* r = _markedRegions.at(i); | |
324 if (r != NULL) r->set_sort_index(-1); | |
325 } | |
326 _markedRegions.clear(); | |
327 _curMarkedIndex = 0; | |
328 _numMarkedRegions = 0; | |
329 _cache.clear(); | |
330 }; | |
331 | |
332 void | |
333 CollectionSetChooser::updateAfterFullCollection() { | |
334 clearMarkedHeapRegions(); | |
335 } | |
336 | |
337 void CollectionSetChooser::removeRegion(HeapRegion *hr) { | |
338 int si = hr->sort_index(); | |
339 assert(si == -1 || hr->is_marked(), "Sort index not valid."); | |
340 if (si > -1) { | |
341 assert(_markedRegions.at(si) == hr, "Sort index not valid." ); | |
342 _markedRegions.at_put(si, NULL); | |
343 } else if (si < -1) { | |
344 assert(_cache.region_in_cache(hr), "should be in the cache"); | |
345 _cache.remove(hr); | |
346 assert(hr->sort_index() == -1, "sort index invariant"); | |
347 } | |
348 hr->set_sort_index(-1); | |
349 } | |
350 | |
351 // if time_remaining < 0.0, then this method should try to return | |
352 // a region, whether it fits within the remaining time or not | |
353 HeapRegion* | |
354 CollectionSetChooser::getNextMarkedRegion(double time_remaining, | |
355 double avg_prediction) { | |
356 G1CollectedHeap* g1h = G1CollectedHeap::heap(); | |
357 G1CollectorPolicy* g1p = g1h->g1_policy(); | |
358 fillCache(); | |
359 if (_cache.is_empty()) { | |
360 assert(_curMarkedIndex == _numMarkedRegions, | |
361 "if cache is empty, list should also be empty"); | |
3914
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
362 ergo_verbose0(ErgoCSetConstruction, |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
363 "stop adding old regions to CSet", |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
364 ergo_format_reason("cache is empty")); |
342 | 365 return NULL; |
366 } | |
367 | |
368 HeapRegion *hr = _cache.get_first(); | |
369 assert(hr != NULL, "if cache not empty, first entry should be non-null"); | |
370 double predicted_time = g1h->predict_region_elapsed_time_ms(hr, false); | |
371 | |
372 if (g1p->adaptive_young_list_length()) { | |
373 if (time_remaining - predicted_time < 0.0) { | |
374 g1h->check_if_region_is_too_expensive(predicted_time); | |
3914
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
375 ergo_verbose2(ErgoCSetConstruction, |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
376 "stop adding old regions to CSet", |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
377 ergo_format_reason("predicted old region time higher than remaining time") |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
378 ergo_format_ms("predicted old region time") |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
379 ergo_format_ms("remaining time"), |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
380 predicted_time, time_remaining); |
342 | 381 return NULL; |
382 } | |
383 } else { | |
3914
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
384 double threshold = 2.0 * avg_prediction; |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
385 if (predicted_time > threshold) { |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
386 ergo_verbose2(ErgoCSetConstruction, |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
387 "stop adding old regions to CSet", |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
388 ergo_format_reason("predicted old region time higher than threshold") |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
389 ergo_format_ms("predicted old region time") |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
390 ergo_format_ms("threshold"), |
20213c8a3c40
7050392: G1: Introduce flag to generate a log of the G1 ergonomic decisions
tonyp
parents:
2435
diff
changeset
|
391 predicted_time, threshold); |
342 | 392 return NULL; |
393 } | |
394 } | |
395 | |
396 HeapRegion *hr2 = _cache.remove_first(); | |
397 assert(hr == hr2, "cache contents should not have changed"); | |
398 | |
399 return hr; | |
400 } |