Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HHH-14694 Don't clear BytecodeProvider cache when SessionFactory is built or closed #9831

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

beikov
Copy link
Member

@beikov beikov commented Mar 3, 2025


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license
and can be relicensed under the terms of the LGPL v2.1 license in the future at the maintainers' discretion.
For more information on licensing, please check here.


https://hibernate.atlassian.net/browse/HHH-14694
https://hibernate.atlassian.net/browse/HHH-19230

@bstansberry
Copy link
Contributor

@scottmarlow @Sanne FYI re this, with respect to the discussion we had at wildfly/wildfly#18748 (comment). That PR evolved to not include the change we were discussing there but if something like that comes up again this may be relevant.

@beikov
Copy link
Member Author

beikov commented Mar 4, 2025

Hey @bstansberry, since the type cache is somewhat class loader specific (because a Class can only be garbage collected once the class loader is unreachable), I would argue that Wildfly should tie the BytecodeProvider instance lifecycle to the deployment and its associated class loader.

@scottmarlow
Copy link
Member

I'm fine with updating WildFly to tie the BytecodeProvider instance lifecycle to the deployment classloader but I'm not yet sure of what we need to do to avoid the leak reported by https://issues.redhat.com/browse/WFLY-20430 which may be similar to what this change addresses or not.

Perhaps it might help to tie the BytecodeProvider instance lifecycle to the deployment classloader but I'm not sure why that would help avoid leaking the classes as mentioned in https://issues.redhat.com/browse/WFLY-20430?focusedId=26681534&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-26681534

@beikov
Copy link
Member Author

beikov commented Mar 4, 2025

I'm not sure what you are using there exactly for you test runs @scottmarlow, but storing the BytecodeProvider in a static field will most certainly cause a memory leak. It needs to be tied to a deployment or its classloader.

@scottmarlow
Copy link
Member

I'm not sure what you are using there exactly for you test runs @scottmarlow, but storing the BytecodeProvider in a static field will most certainly cause a memory leak. It needs to be tied to a deployment or its classloader.

Thanks, I'll experiment with doing that.

@scottmarlow
Copy link
Member

I have a question about the #9831 change and whether it would work with WildFly in the following situation:

  1. myapplication.ear is deployed
  2. myapplication.ear is undeployed
  3. myapplication.ear is updated to add additional entity class fields + methods
  4. myapplication.ear is deployed

My question is whether reusing the one copy of the HibernateProxy class between application redeployments would mean an incorrect HibernateProxy class would be used after redeployment that is missing certain added methods?

My other question if the answer is that redeployment of applications with use of HibernateProxy would not work if there are any class changes is how can we get to zero leaks of the HibernateProxy classes? Would that be a deeper Byte-Buddy change that should be made in Byte-Buddy?

Also, for reference:

https://issues.redhat.com/browse/WFLY-20430?focusedId=26726972&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-26726972 has a comment from testing with this pull request back ported to the 6.6 branch and also the #9836 change.

From the testing I did, #9836 does help but I still see what looks like a permgen (class) leak with the #9831 change but it is not a HibernateProxy class leak.

@beikov
Copy link
Member Author

beikov commented Mar 10, 2025

My question is whether reusing the one copy of the HibernateProxy class between application redeployments would mean an incorrect HibernateProxy class would be used after redeployment that is missing certain added methods?

This shouldn't happen, unless Wildfly reuses the same class loader, which I doubt. Since these caches are bound to the class loader, this should not be an issue. The change of this PR i.e. #9831 are only effective if the BytecodeProvider is "shared" for a deployment/app and will allow reuse of proxy classes for classes defined on the same class loader throughout different persistence units of the same deployment.

In short, on WF, if you scope BytecodeProvider to the classloader/deployment, this will improve the startup time and reduce the amount of classes defined for deployments/apps that have multiple PUs that refer to the same class.
Apps that do not mess with the BytecodeProvider should not see a difference in behavior, because a new instance is created for every SessionFactory through ServiceRegistry.

My other question if the answer is that redeployment of applications with use of HibernateProxy would not work if there are any class changes is how can we get to zero leaks of the HibernateProxy classes? Would that be a deeper Byte-Buddy change that should be made in Byte-Buddy?

This is only a problem if you reuse the class loader and then also the BytecodeProvider, which WF doesn't do AFAIU.

From the testing I did, #9836 does help but I still see what looks like a permgen (class) leak with the #9831 change but it is not a HibernateProxy class leak.

Sounds like a different problem. You mentioned Instantiator and AccessOptimizer to be the culprit in the chat. Let's continue that discussion in the chat.


public void deregisterEnhancedType(final String name) {
enhancedTypeResolution.remove();
}

Check notice

Code scanning / CodeQL

Useless parameter Note

The parameter 'name' is never used.

public void deregisterEnhancedType(final String name) {
enhancedTypeResolution.remove();
}

Check notice

Code scanning / CodeQL

Useless parameter Note

The parameter 'name' is never used.
Copy link
Member

@yrodiere yrodiere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks... suprisingly simpler. Thanks.

We'll probably need @Sanne to have a look though :)

Comment on lines 87 to +89
public void deregisterClassNameAndBytes(final String className) {
locator.remove( className );
poolCache.deregisterEnhancedType( className );
locator.deregisterEnhancedType( className );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should remove the className parameter here, because it's misleading: it's not used in implementations, which will just remove the currently registered class. Yes in practice it should be the one whose name is being passed, but still, it's misleading.

Comment on lines +64 to +68
// Don't clear the state anymore, since the cache is not static anymore since HHH-16058 was fixed
// final BytecodeProvider bytecodeProvider =
// metadata.getMetadataBuildingOptions().getServiceRegistry()
// .getService( BytecodeProvider.class );
// addSessionFactoryObservers( new SessionFactoryObserverForBytecodeEnhancer( bytecodeProvider ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several things:

  1. Maybe remove the code instead of commenting it out?
  2. We use this observer in Quarkus: https://github.com/quarkusio/quarkus/blob/47ecd0a245b26e85a2b01536f182f3292e95b2e8/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/boot/FastBootEntityManagerFactoryBuilder.java#L194-L198 . I think we should remove it there too, but let me know if you disagree.
  3. Maybe you should remove or at least deprecate this observer, if it's no longer useful?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants