-
Notifications
You must be signed in to change notification settings - Fork 75
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
Please reuse Objenesis instance to reduce class loading/unloading #400
Conversation
Hi! Thanks so much for submitting this, I've been meaning to profile EqualsVerifier for a long time but never really got around to it, so I'm very happy with your PR! Before I merge, I have a question though. The JavaDoc for We can also change the code such that only 1 instance of Objenesis is constructed per run of EqualsVerifier, but then obviously we only get partial gains. What do you think is the best way to go here? Did you have reasons for choosing |
They recommend that probably because its a global static and no way to clean it out. This library does not seem to have lifecycles where you can do any cleanup, or any way to reuse EqualsVerifier instances (all mutable and not thread safe). I didn't look too deep so maybe that is wrong, so there doesn't seem to be a place to keep shared state around in your library, so a global static the only option I see without API changes to make that possible. The "don't use this" reasons for ObjenesisHelper don't really apply for something not designed to be used in a long running application in my opinion, This library is mainly for usage in tests i think? where they are expected to terminate quite fast. It would require huge amounts of dynamic classes to be created at runtime and then tested by your library to even notice the cache filling up. And nobody is going to be mocking your library like the other reason they give. So the options are to
option 1 "just works", option 2 is then advanced feature to avoid using the static that needs to be documented, maybe a EqualsVerifier.createContext() method, and then maybe something EqualsVerifier.forClass(Foo.class, context), and then the user can optionally decide to use it or not. Another way to do option 2 would be to introduce some sort of threadsafe factory for EqualsVerifier, and then store the cache in the factory, a bit more intuitive to the user that the factory can keep state I saw a bunch of other caches in the code, maybe some of those can also benefit from being shared, anyway i looked at your code maybe 10min, so i trust you can judge the best solution more then me :) |
Thanks for the elaborate reply. I agree with your reasoning: let's use the |
I've just made a release with your PR: version 3.5.5. |
Would it make sense to use
Probably not:
|
Perhaps. I should look into this a little more at some point. The thing that's complicated, of course, is the fact that many users have multiple calls to EqualsVerifier in their code base (I introduced |
Instantiator
creates a new instance of Objenesis for every instance it creates. Objenesis is made to be reused and caches the loaded classes inside, so the jvm does not need to recreate them every time.The above example will continuously create and load new classes in the jvm, which then have to be unloaded.
After some time this can even take the jvm down because it fully freezes/locks for 30+ seconds while unloading classes.
[43,321s][debug][gc,phases ] GC(1) ClassLoaderData 34830,025ms
If you monitor the classes for example in visualvm, you will also see it creating 20-40k classes continuously and struggle to gc them.
The locking/freezing can be seen as a jvm bug however with this change to reuse objenesis no more then 3k classes total are ever created in this example.
This was found while investigating why https://github.com/junit-pioneer/junit-pioneer froze while running tests on repeat to detect concurrency issues.