JDK-8273902 : Memory leak in OopStorage due to bug in OopHandle::release()
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 16,17,18
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2021-09-16
  • Updated: 2022-01-17
  • Resolved: 2021-09-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 17 JDK 18
17.0.2Fixed 18 b16Fixed
Related Reports
Relates :  
Description
Currently, OopHandle::release() is implemented as follows:

inline void OopHandle::release(OopStorage* storage) {
  if (peek() != NULL) {
    // Clear the OopHandle first
    NativeAccess<>::oop_store(_obj, (oop)NULL);
    storage->release(_obj);
  }
}

However, peek() returns NULL not only if the oop* `_obj` is NULL, but also when `_obj` points to a zero oop. In the latter case, the oop* `_obj` will not be released from the corresponding OopStorage and the slot it occupies will remain alive forever.

This behavior can be easily triggered with the attached LeakTestMinimal.java test (thanks to Oli Gillespie from the Amazon Profiler team for detecting the issue and providing a reproducer).

The fix is trivial:

diff --git a/src/hotspot/share/oops/oopHandle.inline.hpp b/src/hotspot/share/oops/oopHandle.inline.hpp
index 44362499a2c..20de5146ec3 100644
--- a/src/hotspot/share/oops/oopHandle.inline.hpp
+++ b/src/hotspot/share/oops/oopHandle.inline.hpp
@@ -48,7 +48,7 @@ inline OopHandle::OopHandle(OopStorage* storage, oop obj) :
 }
 
 inline void OopHandle::release(OopStorage* storage) {
-  if (peek() != NULL) {
+  if (_obj != NULL) {
     // Clear the OopHandle first
     NativeAccess<>::oop_store(_obj, (oop)NULL);
     storage->release(_obj);

Comments
To let users search for the related fixes (I just followed up on one 17.0.1 bug report, completely missing this fix because none of the keywords I used matched), I am copy-pasting the symptoms here from the minimal example. When doing continuous: ThreadMXBean bean = ManagementFactory.getThreadMXBean(); bean.getThreadInfo(bean.getAllThreadIds(), Integer.MAX_VALUE); NMT would show the apparent leak here: [0x00007f07fe70ba3e] OopStorage::try_add_block()+0x2e [0x00007f07fe70c0ad] OopStorage::allocate()+0x3d [0x00007f07fe8cfc58] StackFrameInfo::StackFrameInfo(javaVFrame*, bool)+0x68 [0x00007f07fe8d07d4] ThreadStackTrace::dump_stack_at_safepoint(int)+0xe4 (malloc=1940MB type=Serviceability +1578MB #3219458 +2618513)
17-01-2022

I ran the LeakTestMinimal.java program provided with this bug report using both JDK 18-ea+14, which did not contain this fix, and JDK 18-ea+25, which did contain this fix. Without the fix, NMT showed that the total amount of reserved memory increased by 140,000K between test loop iterations 1,000,000 and 2,000,000. With the fix, NMT showed that the total amount of reserved memory increased by only 4k between test loop iterations 1,000,000 and 2,000,000. So, marking this bug fix as verified in JDK-18.
01-12-2021

[~simonis] Would it be possible to add the jtreg regression test for this fix?
30-11-2021

Sorry, my fault. I apologize. Thanks Christoph for fixing things up!
20-09-2021

> Maybe it was because on this bug there was a fix version of both, 17-pool and 18. Correct. The fix version for this bug should have been just 18. We don't use multiple values in the fix version field.
20-09-2021

I think there was a bug in the tools. For some strange reason the integration of the fix created a backport bug for 18. Maybe it was because on this bug there was a fix version of both, 17-pool and 18. I fixed that by resolving this bug for 18 and reopening JDK-8273908, changing it to 17-pool. It should get resolved when the 17u backport is integrated.
16-09-2021

Changeset: bc48a0ac Author: Volker Simonis <simonis@openjdk.org> Date: 2021-09-16 19:56:12 +0000 URL: https://git.openjdk.java.net/jdk/commit/bc48a0ac297b99a997482dcb59f85acc1cdb0c47
16-09-2021

Fix request (17u) This is a fix for a memory leak which should be downported as quickly as possible to jdk17u. Applies cleanly. See: https://github.com/openjdk/jdk17u/pull/79
16-09-2021