8205908: Unnecessarily strong memory barriers in ParNewGeneration::copy_to_survivor_space

Doerr, Martin martin.doerr at sap.com
Mon Jul 2 07:55:57 UTC 2018


Hi Michihiro,

thanks for addressing this issue.

The change looks good to me. I only have a comment on the coding style (oop.inline.hpp): “if ()” should be followed by braces “{ … }”.

Seems like a user of the forwardee needs to rely on memory_order_consume in the current implementation. I guess it will be appreciated that you’re fixing this.

Please note that SAP still supports CMS in the commercial VM so this change is still relevant and we’d like to push it to jdk11 if possible.

But we definitely need an OK from a CMS expert (which I’m not).

Best regards,
Martin


From: Michihiro Horie [mailto:HORIE at jp.ibm.com]
Sent: Mittwoch, 27. Juni 2018 02:23
To: hotspot-gc-dev at openjdk.java.net
Cc: Doerr, Martin <martin.doerr at sap.com>; Kim Barrett <kim.barrett at oracle.com>; Gustavo Romero <gromero at linux.vnet.ibm.com>
Subject: RFR: 8205908: Unnecessarily strong memory barriers in ParNewGeneration::copy_to_survivor_space


Dear all,

Would you please review the following change?
Bug: https://bugs.openjdk.java.net/browse/JDK-8205908
Webrev: http://cr.openjdk.java.net/~mhorie/8205908/webrev.00/


[Current implementation]
ParNewGeneration::copy_to_survivor_space tries to move live objects to a different location. There are two patterns on how to copy an object depending on whether there is space to allocate new_obj in to-space or not. If a thread cannot find space to allocate new_obj in to-space, the thread first executes the CAS with a dummy forwarding pointer "ClaimedForwardPtr", which is a sentinel to mark an object as claimed. After succeeding in the CAS, a thread can copy the new_obj in the old space. Here, suppose thread A succeeds in the CAS, while thread B fails in the CAS. When thread A finishes the copy, it replaces the dummy forwarding pointer with a real forwarding pointer. After thread B fails in the CAS, thread B returns the forwardee after waiting for the copy of the forwardee is completed. This is observable by checking the dummy forwarding pointer is replaced with a real forwarding pointer by thread A. In contrast, if a thread can find space to allocate new_obj in to-space, the thread first copies the new_obj and then executes the CAS with the new_obj. If a thread fails in the CAS, it deallocates the copied new_obj and returns the forwardee.

Procedure of ParNewGeneration::copy_to_survivor_space : ([L****] represents the line number in src/hotspot/share/gc/cms/parNewGeneration.cpp)
1. Try to each allocate space for new_obj in to-space [L.1110]
2. If fail in the allocation in to-space [L1117]
2.1. Execute the CAS with the dummy forwarding pointer [L1122] ——— (A)
2.2. If fail in the CAS, return the forwardee via real_forwardee() [L1123]
2.3. If succeed in the CAS [L1128]
2.3.1. If promotion is allowed, copy new_obj in the old area [L1129]
2.3.2. If promotion is not allowed, forward to obj itself [L1133]
2.4. Set new_obj as forwardee [L1142]
3. If succeed in the allocation in to-space [L1144]
3.1. Copy new_obj [L1146]
3.2. Execute the CAS with new_obj [L1148] ——— (B)
4. Dereference the new_obj for logging. Each new_obj copied by each thread at step 3.1 is used instead of forwardee() [L1159]
5. If succeed in either CAS (A) or CAS (B), return new_obj [L1163]
6. If fail in CAS (B), get the forwardee via real_forwardee(). Unallocate new_obj in to-space [L1193]
7. Return forwardee [L1203]

For reference, real_forwardee() is as shown below:
oop ParNewGeneration::real_forwardee(oop obj) {
oop forward_ptr = obj->forwardee();
if (forward_ptr != ClaimedForwardPtr) {
return forward_ptr;
} else {
// manually inlined for readability.
oop forward_ptr = obj->forwardee();
while (forward_ptr == ClaimedForwardPtr) {
waste_some_time();
forward_ptr = obj->forwardee();
}
return forward_ptr;
}
}

Regarding the CAS (A),
There is no copy before the CAS.
Dereferencing the forwardee must be allowed after obtaining the forwardee.

Regarding the CAS (B),
There is a copy before the CAS.
Dereferencing the forwardee must be allowed after obtaining the forwardee.


[Observation on the current implementation]
No fence is necessary before and after the CAS (A).
Release barrier is necessary before the CAS (B).
The forwardee_acquire() must be used instead of forwardee() in real_forwardee().


[Performance measurement]
The critical-jOPS of SPECjbb2015 improved by 12% with this change.


Best regards,
--
Michihiro,
IBM Research - Tokyo
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/hotspot-gc-dev/attachments/20180702/17c681ee/attachment.htm>


More information about the hotspot-gc-dev mailing list