-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
8339648: ZGC: Division by zero in rule_major_allocation_rate #20888
Conversation
👋 Welcome back mbaesken! A progress list of the required criteria for merging this PR into |
@MBaesken 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 120 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 |
Webrevs
|
So we should avoid the division in case the divisor is zero and rewrite the coding a bit. |
8339648: ZGC: Division by zero in rule_major_allocation_rate
src/hotspot/share/gc/z/zDirector.cpp
Outdated
bool old_garbage_is_cheaper = true; | ||
if (reclaimed_per_old_gc != 0) { | ||
const double current_old_gc_time_per_bytes_freed = double(old_gc_time) / double(reclaimed_per_old_gc); | ||
old_garbage_is_cheaper = current_old_gc_time_per_bytes_freed < current_young_gc_time_per_bytes_freed; | ||
} |
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.
Ending up with old_garbage_is_cheaper == true
when reclaimed_per_old_gc == 0
seems wrong to me.
Division by 0.0 is weird in C++. Do we even build for systems where it would not be supported. But regardless to me I feel like the change here should be more like:
- const double current_old_gc_time_per_bytes_freed = double(old_gc_time) / double(reclaimed_per_old_gc);
+ const double current_old_gc_time_per_bytes_freed = reclaimed_per_old_gc == 0 ? std::numeric_limits<double>::infinity : double(old_gc_time) / double(reclaimed_per_old_gc);
Which is the behaviour I expect us to currently have, given that old_gc_time
should be a positive number (>0.0
). The !stats._old_stats._cycle._is_time_trustable
check above should protect against 0.0
.
I expect that this division we see happens when we have run a warmup major collection which did no reclaim any memory. And this change would trigger us to try and promote a minor collection to a major collection.
I am no expert on our supported platforms matrix w.r.t. floating numbers and std::numeric_limits<T>::has_infinity
.
looks like for clang |
Hi Axel, I adjusted the coding following your suggestion. |
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 am fine with this change. But I am not 100% about the use of std::numeric_limits<double>::infinity()
. Maybe someone else can chime in.
Not sure if there are any other places we have expect division by zero to result in infinity.
Thanks for the review ! Seems this exists since c++11 https://en.cppreference.com/w/cpp/types/numeric_limits/infinity so usage should be okay. |
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.
Lgtm but see the additional comment.
@@ -488,7 +488,7 @@ static bool rule_major_allocation_rate(const ZDirectorStats& stats) { | |||
|
|||
// Calculate the GC cost for each reclaimed byte | |||
const double current_young_gc_time_per_bytes_freed = double(young_gc_time) / double(reclaimed_per_young_gc); |
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.
Could this division have the same issue?
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.
Yes, it could if no memory has been reclaimed at all (since the VM started). Similar issues would occur in the call to calculate_extra_young_gc_time
below. And there I think the problem is even worse, because we might end up with inf - inf == -nan
.
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.
The case where we have performed a major collection and no young collection has reclaim any memory seems like a very degenerate situation. The solution is probably to handle that case separately, and not try to adapt the current heuristics to handle the extreme values.
src/hotspot/share/gc/z/zDirector.cpp
Outdated
@@ -488,7 +488,7 @@ static bool rule_major_allocation_rate(const ZDirectorStats& stats) { | |||
|
|||
// Calculate the GC cost for each reclaimed byte | |||
const double current_young_gc_time_per_bytes_freed = double(young_gc_time) / double(reclaimed_per_young_gc); | |||
const double current_old_gc_time_per_bytes_freed = double(old_gc_time) / double(reclaimed_per_old_gc); | |||
const double current_old_gc_time_per_bytes_freed = reclaimed_per_old_gc == 0 ? std::numeric_limits<double>::infinity() : double(old_gc_time) / double(reclaimed_per_old_gc); |
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.
How about using some parentheses? To my understanding, the division has a higher precedence than the ternary conditional expression. See: https://en.cppreference.com/w/cpp/language/operator_precedence
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 do not mind parentheses. But ternary are the lowest precedence (if you do not count the ,
which I would almost always say is wrong to use without a surrounding () / [] / {}
), so to me it seems superfluous.
Just to clarify the intent of this code is what we are getting with a higher precedence on division. That is:
const double current_old_gc_time_per_bytes_freed = ((reclaimed_per_old_gc == 0) ? (std::numeric_limits<double>::infinity()) : (double(old_gc_time) / double(reclaimed_per_old_gc)));
Side Note:
I also think I prefer immediately invoked lambdas when the ternaries get this long.
const double current_old_gc_time_per_bytes_freed = [&]() {
if (reclaimed_per_old_gc == 0) {
return std::numeric_limits<double>::infinity();
}
return double(old_gc_time) / double(reclaimed_per_old_gc);
}();
src/hotspot/share/gc/z/zDirector.cpp
Outdated
const double current_old_gc_time_per_bytes_freed = ((reclaimed_per_old_gc == 0) ? (std::numeric_limits<double>::infinity()) | ||
: (double(old_gc_time) / double(reclaimed_per_old_gc))); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
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 now. Thanks
Thanks for the reviews ! /integrate |
Going to push as commit 80db6e7.
Your commit was automatically rebased without conflicts. |
The HS jtreg test gc/stringdedup/TestStringDeduplicationAgeThreshold_ZGenerational
shows this error when running with ubsan enabled
src/hotspot/share/gc/z/zDirector.cpp:491:74: runtime error: division by zero
#0 0x7f09886401d4 in rule_major_allocation_rate src/hotspot/share/gc/z/zDirector.cpp:491
#1 0x7f09886401d4 in start_gc src/hotspot/share/gc/z/zDirector.cpp:822
#2 0x7f09886401d4 in ZDirector::run_thread() src/hotspot/share/gc/z/zDirector.cpp:912
#3 0x7f098c1404e8 in ZThread::run_service() src/hotspot/share/gc/z/zThread.cpp:29
#4 0x7f09897cac19 in ConcurrentGCThread::run() src/hotspot/share/gc/shared/concurrentGCThread.cpp:48
#5 0x7f098bb46b0a in Thread::call_run() src/hotspot/share/runtime/thread.cpp:225
#6 0x7f098b1a9881 in thread_native_entry src/hotspot/os/linux/os_linux.cpp:858
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/20888/head:pull/20888
$ git checkout pull/20888
Update a local copy of the PR:
$ git checkout pull/20888
$ git pull https://git.openjdk.org/jdk.git pull/20888/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 20888
View PR using the GUI difftool:
$ git pr show -t 20888
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/20888.diff
Webrev
Link to Webrev Comment