On Thu, 9 Feb 2023 18:11:18 GMT, Volker Simonis <simo...@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

Hi Volker,

just a short feedback since I'm entering vacation mode.

> In general it seems to me that creating a new `ClassLoaderData` for each 
> hidden (or VM anonymous) class is a lot of overhead and was probably only 
> done as "a hack" to simplify the implementation of class unloading for such 
> classes. This might have been appropriate in the "early days" when we had 
> just a few VM anonymous classes. But as the usage of 
> `LambdaMetafactory.metafactory()` becomes more common it may make sense to 
> revise the implementation such that for example all hidden classes defined by 
> a class loader can live in a single `ClassLoaderData` structure. I think this 
> should be technically possible although it would complicate the unloading 
> logic (and should therefore be postponed to a later change).

Interesting numbers. I underestimated the impact of native allocations.

A side note, the amount of waste avoided by JEP 371 depends highly on the 
actual size of the lambda (precisely, how close a lambda would be to the chunk 
size). That's pretty much random, but nice to know since it can shift the 
numbers from almost nil overhead to almost twofold overhead.

>I'd argue that Metaspace is more "valuable" than native memory because it is 
>limited by -XX:MetaspaceSize, whereas native memory is only limited by the 
>amount of available memory on the system. Also, growing Metaspace will trigger 
>garbage collections which might be unexpected.

I don't follow that logic, for several reasons:
- Metaspace is more easily reclaimed, since it returns memory to the OS eagerly 
(since JEP 387). C-Heap, otoh, sticks to the process unless you (atm) manually 
trim. Depends on the libc used, but with glibc - arguable the main platform - 
this is the state. We are working on C-heap autotrim, but it is not completed 
yet. 
So while Metaspace is limited, it recovers from spikes more easily.
- The metaspace GC threshold is an (impreceise) mechanism to trigger GC based 
on native resource consumption. That this is not affected by C-heap memory is 
just a side effect of an imperfect implementation - ideally, all native 
resources associated with a class loader - be it metaspace or C-heap - together 
would weight into the decision of when to do a GC. In fact, your numbers may be 
taken as a hint that maybe CLDs should live in Metaspace instead.

---

As I stated before, we do have the ability to prematurely repurpose metaspace 
before the loader dies via Metaspace::deallocate(). It would be nice if there 
were a mechanism to trigger this at least from Java. E.g. by making Lambdas 
closable.

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

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

Reply via email to