After upgrading to the latest Spring Boot that includes https://github.com/spring-projects/spring-framework/issues/33601 we detected a memory leak in our application. The good news is that we discovered we have cases of triggering NoTransactionInContextException
(in this case when closeConnection
is happening), but unfortunately it introduced new stability issues by regularly running out of memory.
The static exception introduced by the referenced change (to reduce GC pressure) has introduced a hard path to the GC root for assembly checkpoint records which are growing unbounded for our use-case and causing out-of-memory errors.
Reactor uses the suppressed
exception as a carrier for the assembly data so checkpoints can provide better assembly traces (See this code).
Because Spring is now using the same exception for every time it throws a NoTransactionInContextException
, for code that is regularly failing with this exception, this can result in a persistent growing list of failure checkpoints being captured under a hard GC root.
The path is effectively:
TransactionContextManager.<static fields>
NoTransactionInContextException
suppressed
->OnAssemblyException
nodesPerId
->Map<Integer, ObservedAtInformationNode>
(unbounded growth happens here)
While I don't have a verified reproducible case at this very moment it should be as simple as:
while (true) {
Mono.just(true)
.checkpoint("some-checkpoint-" + System.nanotime())
.then(Mono.fromCallback(() -> /* do something against a DB without a transaction that results in the no context exception */)
.subscribe();
}
Comment From: jhoeller
- Duplicates #34048.
In any case, we'll fix this for 6.2.1.
Comment From: realjenius
In any case, we'll fix this for 6.2.1.
Not sure why I didn't see the dupe, but thank you much for the quick turnaround!