Hi all,

I would like to propose a patch for an issue discussed in [1][2] that fixes an 
OOME in the current code base. The issue appears when a SunJCE provider object 
reference is stored in a non-static variable, which eventually leads to a Java 
heap OOME.

The solution proposed earlier [1] raised a concern that the identity of 
provider objects may be somehow broken when using `WeakHashMap<Class<? extends 
Provider>, Object>` instead of `IdentityHashMap<Provider, Object>`. The 
solution hasn't been integrated that time. Later in 2019 a performance 
improvement was introduced with [3][4] that changed `IdentityHashMap` to 
`ConcurrentHashMap` of `IdentityWrapper<Provider> `objects that maintained the 
object identity while improved performance.

The solution being proposed keeps up with performance improvement in [3], 
further narrowing the synchronization down to the hash table node, avoids 
lambdas that may cause startup time regressions and maintains providers 
identity using `WeakIdentityWrapper` that extends `WeakReference<Object>` while 
avoiding depletion of Java heap by using weak pointers as the mapping key. Once 
a provider object becomes weakly reachable it is queued along with its 
reference object to the `ReferenceQueue` (a new static member in 
`JceSecurity`). The `equals()` method of the `WeakIdentityWrapper` will never 
match a new wrapper object to anything inside the hash table after the 
corresponding element's `WeakReference.get()` returned null. This leads to 
accumulating "empty" elements in the hash table. The new static function 
`expungeStaleWrappers()` drops the "empty" elements queued by GC each time the 
`getVerificationResult()` method is called.

`ConcurrentHashMap`'s `computeIfAbsent()` does (partially) detect recursive 
updates for keys that are being added to empty bins. For such keys an 
`IllegalStateException` is thrown prior to recursive execution of the 
`mappingFunction`. For nodes that are added to existing TreeBins or linked 
lists, in which case no recursion detection is done prior to calling 
`mappingFunction`, the recursion detection relies on old method with 
`IdentityMap` of Providers.

I include a test that runs under memory constrained conditions (128M) that hits 
the heap limit in current VMs where it is impossible to reclaim providers' 
memory (unless they've been cached under weak references). The updated jdk 
versions pass the test.

Testing: jtreg + JCK on a downport of the patch to JDK 17 with no regressions

[1] https://mail.openjdk.org/pipermail/security-dev/2016-October/015024.html
[2] https://bugs.openjdk.org/browse/JDK-8168469
[3] https://bugs.openjdk.org/browse/JDK-7107615
[4] 
https://github.com/openjdk/jdk/commit/74d45e481d1ad6aa5d7c6315ea86681e1a978ce0

-------------

Commit messages:
 - 8168469: Memory leak in JceSecurity

Changes: https://git.openjdk.org/jdk/pull/13658/files
 Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=13658&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8168469
  Stats: 110 lines in 2 files changed: 84 ins; 7 del; 19 mod
  Patch: https://git.openjdk.org/jdk/pull/13658.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/13658/head:pull/13658

PR: https://git.openjdk.org/jdk/pull/13658

Reply via email to