RFR: 8302154: Hidden classes created by LambdaMetaFactory can't be unloaded [v2]
Jorn Vernee
jvernee at openjdk.org
Wed Feb 22 15:20:00 UTC 2023
On Thu, 9 Feb 2023 18:11:18 GMT, Volker Simonis <simonis at openjdk.org> wrote:
>> Prior to [JDK-8239384](https://bugs.openjdk.org/browse/JDK-8239384)/[JDK-8238358](https://bugs.openjdk.org/browse/JDK-8238358) LambdaMetaFactory has created VM-anonymous classes which could easily be unloaded once they were not referenced any more. Starting with JDK 15 and the new "hidden class" based implementation, this is not the case any more, because the hidden classes will be strongly tied to their defining class loader. If this is the default application class loader, these hidden classes can never be unloaded which can easily lead to Metaspace exhaustion (see the [test case in the JBS issue](https://bugs.openjdk.org/secure/attachment/102601/LambdaClassLeak.java)). This is a regression compared to previous JDK versions which some of our applications have been affected from when migrating to JDK 17.
>>
>> The reason why the newly created hidden classes are strongly linked to their defining class loader is not clear to me. JDK-8239384 mentions it as an "implementation detail":
>>
>>> *4. the lambda proxy class has the strong relationship with the class loader (that will share the VM metaspace for its defining loader - implementation details)*
>>
>> From my current understanding the strong link between a hidden class created by `LambdaMetaFactory` and its defining class loader is not strictly required. In order to prevent potential OOMs and fix the regression compared the JDK 14 and earlier I propose to create these hidden classes without the `STRONG` option.
>>
>> I'll be happy to add the test case as JTreg test to this PR if you think that would be useful.
>
> Volker Simonis has updated the pull request incrementally with two additional commits since the last revision:
>
> - Remove assertions which insist on Lambda proxy classes being strongly linked to their class loader
> - Removed unused import of STRONG und updated copyright year
I'd like to suggest maybe using another option for turning MethodHandles into interface instances, such as using `MethodHandleProxies` or just lambda expressions which capture the particular `MethodHandle`, which is an option if the target type of the functional interface is statically known:
MethodHandle mh = ...
TargetType instance = (Widget w) -> {
try {
return (SomeType) mh.invokeExact(w);
} catch(Throwable t) {
throw new RuntimeException(t); // or something more nuanced
}
};
Most importantly, the above doesn't generate a new class every time, it generates just one. It can be slower in specific situations due to lack of inlining, but in the common use case it's not that bad (I've measure the extra hop through the method handle to be 4ns in the past in microbenchmarks). You could measure the impact for the particular app. (I guess the question is: do you _really_ want a new class to be generated for each MethodHandle?)
-------------
PR: https://git.openjdk.org/jdk/pull/12493
More information about the hotspot-dev
mailing list