The session itself isn't being shared but the connection/transaction is. When a new session is opened on an existing transaction/connection within a single thread a transaction observer is added to the transaction coordinator associated with the original Session. The problem is that when the child session is closed the transaction coordinator isn't unregistered. In SessionImpl's close() method prior to Hibernate 5 it used to unregister itself from the transaction coordinator. The result is that until the main Session is closed then the child Sessions can not be collected. So given the following example.
- Session is opened on a connection and a transaction is started, a bunch of work is done.
- At some point a child session is created via a sessionWithOptions() call, at this point a TransactionObserver is registered on the TransactionCoordinator to listen for transaction events (i.e. if the session needs to flush prior to commit) Post commit events from the ActionQueue are delegated to the main Session's ActionQueue
- Work is done on the child session, flushed and the session is closed.
- We can't do anything else with the child session since it's closed but since it is still registered with the TransactionCoordinator via a TransactionObserver it cannot be GC'd
- We continue working with the main Session, flush, commit and close.
- Everything is now eligible for GC as nothing is referencing the main session.
In the above example I would end up retaining the closed SessionImpl until the main Session was closed. Not that big of a deal if I only had a single Session but in a long running batch process I might have spun up a large number of Sessions to deal with alternate lookups based on load query influencers or maybe writing audit records that I didn't want to pollute my main session with. Take a look at the associated test case it might be clearer than how I'm describing it. The behaviour seemed to have changed under
HHH-9747 Closed |