Mercurial > hg > truffle
comparison src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @ 4727:67fdcb391461
7119027: G1: use atomics to update RS length / predict time of inc CSet
Summary: Make sure that the updates to the RS length and inc CSet predicted time are updated in an MT-safe way.
Reviewed-by: brutisso, iveresov
author | tonyp |
---|---|
date | Wed, 21 Dec 2011 07:53:53 -0500 |
parents | 41406797186b |
children | 441e946dc1af |
comparison
equal
deleted
inserted
replaced
4726:d15b458c4225 | 4727:67fdcb391461 |
---|---|
228 _inc_cset_head(NULL), | 228 _inc_cset_head(NULL), |
229 _inc_cset_tail(NULL), | 229 _inc_cset_tail(NULL), |
230 _inc_cset_bytes_used_before(0), | 230 _inc_cset_bytes_used_before(0), |
231 _inc_cset_max_finger(NULL), | 231 _inc_cset_max_finger(NULL), |
232 _inc_cset_recorded_rs_lengths(0), | 232 _inc_cset_recorded_rs_lengths(0), |
233 _inc_cset_recorded_rs_lengths_diffs(0), | |
233 _inc_cset_predicted_elapsed_time_ms(0.0), | 234 _inc_cset_predicted_elapsed_time_ms(0.0), |
235 _inc_cset_predicted_elapsed_time_ms_diffs(0.0), | |
234 | 236 |
235 #ifdef _MSC_VER // the use of 'this' below gets a warning, make it go away | 237 #ifdef _MSC_VER // the use of 'this' below gets a warning, make it go away |
236 #pragma warning( disable:4355 ) // 'this' : used in base member initializer list | 238 #pragma warning( disable:4355 ) // 'this' : used in base member initializer list |
237 #endif // _MSC_VER | 239 #endif // _MSC_VER |
238 | 240 |
1549 } else { | 1551 } else { |
1550 _mixed_cards_per_entry_ratio_seq->add(cards_per_entry_ratio); | 1552 _mixed_cards_per_entry_ratio_seq->add(cards_per_entry_ratio); |
1551 } | 1553 } |
1552 } | 1554 } |
1553 | 1555 |
1554 // It turns out that, sometimes, _max_rs_lengths can get smaller | 1556 // This is defensive. For a while _max_rs_lengths could get |
1555 // than _recorded_rs_lengths which causes rs_length_diff to get | 1557 // smaller than _recorded_rs_lengths which was causing |
1556 // very large and mess up the RSet length predictions. We'll be | 1558 // rs_length_diff to get very large and mess up the RSet length |
1557 // defensive until we work out why this happens. | 1559 // predictions. The reason was unsafe concurrent updates to the |
1560 // _inc_cset_recorded_rs_lengths field which the code below guards | |
1561 // against (see CR 7118202). This bug has now been fixed (see CR | |
1562 // 7119027). However, I'm still worried that | |
1563 // _inc_cset_recorded_rs_lengths might still end up somewhat | |
1564 // inaccurate. The concurrent refinement thread calculates an | |
1565 // RSet's length concurrently with other CR threads updating it | |
1566 // which might cause it to calculate the length incorrectly (if, | |
1567 // say, it's in mid-coarsening). So I'll leave in the defensive | |
1568 // conditional below just in case. | |
1558 size_t rs_length_diff = 0; | 1569 size_t rs_length_diff = 0; |
1559 if (_max_rs_lengths > _recorded_rs_lengths) { | 1570 if (_max_rs_lengths > _recorded_rs_lengths) { |
1560 rs_length_diff = _max_rs_lengths - _recorded_rs_lengths; | 1571 rs_length_diff = _max_rs_lengths - _recorded_rs_lengths; |
1561 } | 1572 } |
1562 _rs_length_diff_seq->add((double) rs_length_diff); | 1573 _rs_length_diff_seq->add((double) rs_length_diff); |
2434 _inc_cset_tail = NULL; | 2445 _inc_cset_tail = NULL; |
2435 _inc_cset_bytes_used_before = 0; | 2446 _inc_cset_bytes_used_before = 0; |
2436 | 2447 |
2437 _inc_cset_max_finger = 0; | 2448 _inc_cset_max_finger = 0; |
2438 _inc_cset_recorded_rs_lengths = 0; | 2449 _inc_cset_recorded_rs_lengths = 0; |
2439 _inc_cset_predicted_elapsed_time_ms = 0; | 2450 _inc_cset_recorded_rs_lengths_diffs = 0; |
2451 _inc_cset_predicted_elapsed_time_ms = 0.0; | |
2452 _inc_cset_predicted_elapsed_time_ms_diffs = 0.0; | |
2440 _inc_cset_build_state = Active; | 2453 _inc_cset_build_state = Active; |
2454 } | |
2455 | |
2456 void G1CollectorPolicy::finalize_incremental_cset_building() { | |
2457 assert(_inc_cset_build_state == Active, "Precondition"); | |
2458 assert(SafepointSynchronize::is_at_safepoint(), "should be at a safepoint"); | |
2459 | |
2460 // The two "main" fields, _inc_cset_recorded_rs_lengths and | |
2461 // _inc_cset_predicted_elapsed_time_ms, are updated by the thread | |
2462 // that adds a new region to the CSet. Further updates by the | |
2463 // concurrent refinement thread that samples the young RSet lengths | |
2464 // are accumulated in the *_diffs fields. Here we add the diffs to | |
2465 // the "main" fields. | |
2466 | |
2467 if (_inc_cset_recorded_rs_lengths_diffs >= 0) { | |
2468 _inc_cset_recorded_rs_lengths += _inc_cset_recorded_rs_lengths_diffs; | |
2469 } else { | |
2470 // This is defensive. The diff should in theory be always positive | |
2471 // as RSets can only grow between GCs. However, given that we | |
2472 // sample their size concurrently with other threads updating them | |
2473 // it's possible that we might get the wrong size back, which | |
2474 // could make the calculations somewhat inaccurate. | |
2475 size_t diffs = (size_t) (-_inc_cset_recorded_rs_lengths_diffs); | |
2476 if (_inc_cset_recorded_rs_lengths >= diffs) { | |
2477 _inc_cset_recorded_rs_lengths -= diffs; | |
2478 } else { | |
2479 _inc_cset_recorded_rs_lengths = 0; | |
2480 } | |
2481 } | |
2482 _inc_cset_predicted_elapsed_time_ms += | |
2483 _inc_cset_predicted_elapsed_time_ms_diffs; | |
2484 | |
2485 _inc_cset_recorded_rs_lengths_diffs = 0; | |
2486 _inc_cset_predicted_elapsed_time_ms_diffs = 0.0; | |
2441 } | 2487 } |
2442 | 2488 |
2443 void G1CollectorPolicy::add_to_incremental_cset_info(HeapRegion* hr, size_t rs_length) { | 2489 void G1CollectorPolicy::add_to_incremental_cset_info(HeapRegion* hr, size_t rs_length) { |
2444 // This routine is used when: | 2490 // This routine is used when: |
2445 // * adding survivor regions to the incremental cset at the end of an | 2491 // * adding survivor regions to the incremental cset at the end of an |
2453 // retiring the current allocation region) or a concurrent | 2499 // retiring the current allocation region) or a concurrent |
2454 // refine thread (RSet sampling). | 2500 // refine thread (RSet sampling). |
2455 | 2501 |
2456 double region_elapsed_time_ms = predict_region_elapsed_time_ms(hr, true); | 2502 double region_elapsed_time_ms = predict_region_elapsed_time_ms(hr, true); |
2457 size_t used_bytes = hr->used(); | 2503 size_t used_bytes = hr->used(); |
2458 | |
2459 _inc_cset_recorded_rs_lengths += rs_length; | 2504 _inc_cset_recorded_rs_lengths += rs_length; |
2460 _inc_cset_predicted_elapsed_time_ms += region_elapsed_time_ms; | 2505 _inc_cset_predicted_elapsed_time_ms += region_elapsed_time_ms; |
2461 | |
2462 _inc_cset_bytes_used_before += used_bytes; | 2506 _inc_cset_bytes_used_before += used_bytes; |
2463 | 2507 |
2464 // Cache the values we have added to the aggregated informtion | 2508 // Cache the values we have added to the aggregated informtion |
2465 // in the heap region in case we have to remove this region from | 2509 // in the heap region in case we have to remove this region from |
2466 // the incremental collection set, or it is updated by the | 2510 // the incremental collection set, or it is updated by the |
2467 // rset sampling code | 2511 // rset sampling code |
2468 hr->set_recorded_rs_length(rs_length); | 2512 hr->set_recorded_rs_length(rs_length); |
2469 hr->set_predicted_elapsed_time_ms(region_elapsed_time_ms); | 2513 hr->set_predicted_elapsed_time_ms(region_elapsed_time_ms); |
2470 } | 2514 } |
2471 | 2515 |
2472 void G1CollectorPolicy::remove_from_incremental_cset_info(HeapRegion* hr) { | 2516 void G1CollectorPolicy::update_incremental_cset_info(HeapRegion* hr, |
2473 // This routine is currently only called as part of the updating of | 2517 size_t new_rs_length) { |
2474 // existing policy information for regions in the incremental cset that | 2518 // Update the CSet information that is dependent on the new RS length |
2475 // is performed by the concurrent refine thread(s) as part of young list | 2519 assert(hr->is_young(), "Precondition"); |
2476 // RSet sampling. Therefore we should not be at a safepoint. | 2520 assert(!SafepointSynchronize::is_at_safepoint(), |
2477 | 2521 "should not be at a safepoint"); |
2478 assert(!SafepointSynchronize::is_at_safepoint(), "should not be at safepoint"); | 2522 |
2479 assert(hr->is_young(), "it should be"); | 2523 // We could have updated _inc_cset_recorded_rs_lengths and |
2480 | 2524 // _inc_cset_predicted_elapsed_time_ms directly but we'd need to do |
2481 size_t used_bytes = hr->used(); | 2525 // that atomically, as this code is executed by a concurrent |
2482 size_t old_rs_length = hr->recorded_rs_length(); | 2526 // refinement thread, potentially concurrently with a mutator thread |
2527 // allocating a new region and also updating the same fields. To | |
2528 // avoid the atomic operations we accumulate these updates on two | |
2529 // separate fields (*_diffs) and we'll just add them to the "main" | |
2530 // fields at the start of a GC. | |
2531 | |
2532 ssize_t old_rs_length = (ssize_t) hr->recorded_rs_length(); | |
2533 ssize_t rs_lengths_diff = (ssize_t) new_rs_length - old_rs_length; | |
2534 _inc_cset_recorded_rs_lengths_diffs += rs_lengths_diff; | |
2535 | |
2483 double old_elapsed_time_ms = hr->predicted_elapsed_time_ms(); | 2536 double old_elapsed_time_ms = hr->predicted_elapsed_time_ms(); |
2484 | 2537 double new_region_elapsed_time_ms = predict_region_elapsed_time_ms(hr, true); |
2485 // Subtract the old recorded/predicted policy information for | 2538 double elapsed_ms_diff = new_region_elapsed_time_ms - old_elapsed_time_ms; |
2486 // the given heap region from the collection set info. | 2539 _inc_cset_predicted_elapsed_time_ms_diffs += elapsed_ms_diff; |
2487 _inc_cset_recorded_rs_lengths -= old_rs_length; | 2540 |
2488 _inc_cset_predicted_elapsed_time_ms -= old_elapsed_time_ms; | 2541 hr->set_recorded_rs_length(new_rs_length); |
2489 | 2542 hr->set_predicted_elapsed_time_ms(new_region_elapsed_time_ms); |
2490 _inc_cset_bytes_used_before -= used_bytes; | |
2491 | |
2492 // Clear the values cached in the heap region | |
2493 hr->set_recorded_rs_length(0); | |
2494 hr->set_predicted_elapsed_time_ms(0); | |
2495 } | |
2496 | |
2497 void G1CollectorPolicy::update_incremental_cset_info(HeapRegion* hr, size_t new_rs_length) { | |
2498 // Update the collection set information that is dependent on the new RS length | |
2499 assert(hr->is_young(), "Precondition"); | |
2500 | |
2501 remove_from_incremental_cset_info(hr); | |
2502 add_to_incremental_cset_info(hr, new_rs_length); | |
2503 } | 2543 } |
2504 | 2544 |
2505 void G1CollectorPolicy::add_region_to_incremental_cset_common(HeapRegion* hr) { | 2545 void G1CollectorPolicy::add_region_to_incremental_cset_common(HeapRegion* hr) { |
2506 assert(hr->is_young(), "invariant"); | 2546 assert(hr->is_young(), "invariant"); |
2507 assert(hr->young_index_in_cset() > -1, "should have already been set"); | 2547 assert(hr->young_index_in_cset() > -1, "should have already been set"); |
2589 void G1CollectorPolicy::choose_collection_set(double target_pause_time_ms) { | 2629 void G1CollectorPolicy::choose_collection_set(double target_pause_time_ms) { |
2590 // Set this here - in case we're not doing young collections. | 2630 // Set this here - in case we're not doing young collections. |
2591 double non_young_start_time_sec = os::elapsedTime(); | 2631 double non_young_start_time_sec = os::elapsedTime(); |
2592 | 2632 |
2593 YoungList* young_list = _g1->young_list(); | 2633 YoungList* young_list = _g1->young_list(); |
2634 finalize_incremental_cset_building(); | |
2594 | 2635 |
2595 guarantee(target_pause_time_ms > 0.0, | 2636 guarantee(target_pause_time_ms > 0.0, |
2596 err_msg("target_pause_time_ms = %1.6lf should be positive", | 2637 err_msg("target_pause_time_ms = %1.6lf should be positive", |
2597 target_pause_time_ms)); | 2638 target_pause_time_ms)); |
2598 guarantee(_collection_set == NULL, "Precondition"); | 2639 guarantee(_collection_set == NULL, "Precondition"); |