-
Notifications
You must be signed in to change notification settings - Fork 5.8k
JDK-8264987: G1: Fill BOTs for Survivor-turned-to-Old regions in full gc #3459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
👋 Welcome back mli! A progress list of the required criteria for merging this PR into |
@Hamlin-Li The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
/contributor add Shoubing Ma mashoubing1@huawei.com |
@Hamlin-Li Could not parse
|
Webrevs
|
/contributor add Shoubing Ma mashoubing1@huawei.com |
@Hamlin-Li |
HeapWord* limit = hr->top(); | ||
HeapWord* next_addr = hr->bottom(); | ||
HeapWord* threshold = hr->initialize_threshold(); | ||
HeapWord* pre_addr; | ||
|
||
while (next_addr < limit) { | ||
Prefetch::write(next_addr, PrefetchScanIntervalInBytes); | ||
pre_addr = next_addr; | ||
if (_bitmap->is_marked(next_addr)) { | ||
oop obj = cast_to_oop(next_addr); | ||
next_addr += obj->size(); | ||
} else { | ||
next_addr = _bitmap->get_next_marked_addr(next_addr, limit); | ||
} | ||
|
||
if (next_addr > threshold) { | ||
threshold = hr->cross_threshold(pre_addr, next_addr); | ||
} | ||
} | ||
assert(next_addr == limit, "Should stop the scan at the limit."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be implemented using apply_to_marked_objects()
. Let the closure keep track of the threshold for the region and the apply()
function could look something like this:
size_t apply(oop object) {
size_t size = object->size();
HeapWord* addr = cast_from_oop<HeapWord*>(object);
HeapWord* next_addr = addr + size;
if (next_addr > _threshold) {
_threshold = hr->cross_threshold(addr, next_addr);
}
return size;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize that we need to keep the previous address in the closure as well, for the case when we step over the threshold because of an unmarked object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Stefan, Thanks for the suggestion.
At first thought, I think it's a good way to reuse the traversal ability of HeapRegion::apply_to_marked_objects.
But when I try to implement it actually, seems to me it might make the code a little complicated.
For the first part, in the new G1UpdateBotClosure, we need to indroduce an _pre_addr as you suggested. this part is implemented as below, it's not that complicated, please check the following code snippet:
void G1FullGCPrepareTask::G1CalculatePointersClosure::update_bot(HeapRegion* hr) {
G1UpdateBotClosure updateBot(hr);
hr->apply_to_marked_objects(_bitmap, &updateBot);
}
G1FullGCPrepareTask::G1UpdateBotClosure::G1UpdateBotClosure(HeapRegion* hr) :
_hr(hr),
_pre_addr(hr->bottom()),
_threshold(hr->initialize_threshold()) { }
size_t G1FullGCPrepareTask::G1UpdateBotClosure::apply(oop object) {
HeapWord* addr = cast_from_oop<HeapWord*>(object);
size_t size = object->size();
HeapWord* next_addr = addr + size;
if(addr > _threshold) {
_threshold = _hr->cross_threshold(_pre_addr, addr);
}
if (next_addr > _threshold) {
_threshold = _hr->cross_threshold(addr, next_addr);
}
_pre_addr = next_addr;+ return size;
}
But, the above code does not consider the situation: one or several dead objects are at the end of the heap region, at this situation, we needs to update the bot outside of the G1UpdateBotClosure after hr->apply_to_marked_objects(...). I did not implement this part yet, but seems to me it makes the code a little complicated and not that readable, so would like to discuss with you first.
In summary, seems it does not make the code more readable, and will increase the complexity.
How do you think about it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, may I ask what's the format do you use the quote a snippet of code, seems my snippet of code lost all format info(e.g. indent).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Easiest is to mark the code and push the "Insert code" button (<>). You can also manually write ``` to start a code block.
I'll get back to you about the code above later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Stefan, it works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looked a bit more at the code now and I agree that it didn't become as neat as I had hoped. Solving the problem you describe with dead objects at the end should be fairly easy in the closure-destructor I guess. Something like:
~G1UpdateBotClosure() {
if (_hr->top() > _threshold) {
_hr->cross_threshold(_pre_addr, _hr->top());
}
}
And a few comments in the apply method will help readability:
size_t apply(oop object) {
size_t size = object->size();
HeapWord* addr = cast_from_oop<HeapWord*>(object);
HeapWord* next_addr = addr + size;
// Threshold was crossed by dead object.
if(addr > _threshold) {
_threshold = _hr->cross_threshold(_pre_addr, addr);
}
// Threshold is crossed by this object.
if (next_addr > _threshold) {
_threshold = _hr->cross_threshold(addr, next_addr);
}
_pre_addr = next_addr;
return size;
}
But still it might be better to instead streamline the current approach:
HeapWord* limit = hr->top(); | |
HeapWord* next_addr = hr->bottom(); | |
HeapWord* threshold = hr->initialize_threshold(); | |
HeapWord* pre_addr; | |
while (next_addr < limit) { | |
Prefetch::write(next_addr, PrefetchScanIntervalInBytes); | |
pre_addr = next_addr; | |
if (_bitmap->is_marked(next_addr)) { | |
oop obj = cast_to_oop(next_addr); | |
next_addr += obj->size(); | |
} else { | |
next_addr = _bitmap->get_next_marked_addr(next_addr, limit); | |
} | |
if (next_addr > threshold) { | |
threshold = hr->cross_threshold(pre_addr, next_addr); | |
} | |
} | |
assert(next_addr == limit, "Should stop the scan at the limit."); | |
} | |
HeapWord* limit = hr->top(); | |
HeapWord* next_addr = hr->bottom(); | |
HeapWord* threshold = hr->initialize_threshold(); | |
HeapWord* pre_addr; | |
while (next_addr < limit) { | |
pre_addr = next_addr; | |
next_addr = _bitmap->get_next_marked_addr(next_addr + 1, limit); | |
if (next_addr > threshold) { | |
threshold = hr->cross_threshold(pre_addr, next_addr); | |
} | |
} | |
assert(next_addr == limit, "Should stop the scan at the limit."); | |
} |
Here I removed the Prefetch
because we do not need to touch the heap. For this to be true I also removed the small optimization to use obj->size()
, which is probably not an optimization if we don't need to do anything with the "object".
After doing this I agree that reusing apply_to_marked_objects()
is probably not helping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Stefan, I think the refactoring you suggested is great, I just modified as you suggested and the pre-submit tests run successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nits.
HeapWord* limit = hr->top(); | ||
HeapWord* next_addr = hr->bottom(); | ||
HeapWord* threshold = hr->initialize_threshold(); | ||
HeapWord* pre_addr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit: typically the "friend" of next
is prev
, not pre
in traversals.
@@ -157,6 +166,22 @@ bool G1FullGCPrepareTask::G1CalculatePointersClosure::should_compact(HeapRegion* | |||
return live_words <= live_words_threshold; | |||
} | |||
|
|||
void G1FullGCPrepareTask::G1CalculatePointersClosure::update_bot(HeapRegion* hr) { | |||
HeapWord* limit = hr->top(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make this const
.
@@ -67,6 +67,15 @@ bool G1FullGCPrepareTask::G1CalculatePointersClosure::do_heap_region(HeapRegion* | |||
// Force the high live ration region pinned, | |||
// as we need skip these regions in the later compact step. | |||
force_pinned = true; | |||
if (hr->is_young()) { | |||
// Old regions have BOTs info for performance consideration, but young regions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd replace this comment with something like this:
// G1 updates the BOT for old region contents incrementally, but young regions lack BOT
// information for performance reasons.
// Recreate BOT information of high live ratio young regions here to keep expected
// performance during scanning their card tables in the collection pauses later.
Or something like this...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Thomas for refining the comments.
@Hamlin-Li this pull request can not be integrated into git checkout fill-bots-in-full-gc
git fetch https://git.openjdk.java.net/jdk master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
@Hamlin-Li This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 19 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ship it.
/integrate |
@Hamlin-Li Since your change was applied there have been 20 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit b4ba74e. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
in JDK-8262068, we have an enhancement for full gc which will skip compacting some regions with high survivor ratio. There might be some young regions among these skipped regions. These young regions are not filled with BOTs info as they're young.
But after full GC these young regions will become old regions, and the BOTs info is better to be filled for performance consideration.
Progress
Issue
Reviewers
Contributors
<mashoubing1@huawei.com>
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/3459/head:pull/3459
$ git checkout pull/3459
Update a local copy of the PR:
$ git checkout pull/3459
$ git pull https://git.openjdk.java.net/jdk pull/3459/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 3459
View PR using the GUI difftool:
$ git pr show -t 3459
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/3459.diff