Closed LudoPL closed 4 years ago
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).
:memo: Please visit https://cla.developers.google.com/ to sign.
Once you've signed (or fixed any issues), please reply here (e.g. I signed it!
) and we'll verify it.
ℹ️ Googlers: Go here for more info.
I signed it!
This is some awesome investigation! Thanks so much for figuring out what was going on and putting together a fix. It's obviously a huge hack, but everything about GwtMockito is a huge hack, so that's not a dealbreaker :)
I think it makes sense to allow what you've defined here as an experimental opt-in feature. Like you said, tying all GwtMockito tests to the implementation details of Thread probably isn't the best idea, and it's probably only a very small percentage of users who are having problems with memory leaks. So here's what I'd suggest:
@WithExperimentalGarbageCollection
, and have this logic apply only if the test class has that annotation. You can document on that annotation what exactly it does and what the caveats are.ThreadLocalCleaner
just to keep it separate from the existing business logic of the runner.GwtMockitoMemoryLeakTest
so we can isolate it easily if we find it misbehaves in certain contexts/JVMs.How does that sound? Thanks again for looking at it!
Thanks for the remarks, I have tried to implement them, hope it will match what you have in mind. You are right it is better to make this clean optional and to isolate the logic ! Do not hesitate to do or ask for other modifications. And also thanks for your project, it is very useful !
Thanks again for working on this!
Thanks for this PR!
This is a proposal of a fix (or a beginning of a fix) for https://github.com/google/gwtmockito/issues/53
Explanation of the issue (sorry if it's not very clear): Mockito is using the ThreadLocal java feature to keep a context with instances of MockitoConfiguration and MockingProgressImpl between calls. GwtMockitoTestRunner set a specific ClassLoader in the TreadContext before running each test and restore the previous one at the end. The JVM ThreadLocal implementation stored the thread contextual information in a map in the thread where the key is a ThreadLocal instance and the value the object stored. These ThreadLocal instances are referenced statically by two mockito classes loaded through the GwtMockitoClassLoader. This mean these instances can not be garbage collected until the GwtMockitoClassLoader that loaded them is. It also mean, as there is several instances of the GwtMockitoClassLoader, a same static field definition in the source code will reference a different object in the different version of the classes loaded in the different class loaders. These instances can not be garbage collected until the GwtMockitoClassLoader that loaded them is. So as there is several instances of the GwtMockitoClassLoader, a same static field definition in the source code will reference a different object in the different version of the classes loaded in the different class loaders. The entries in the ThreadLocalMap won't be removed from the Map while the key is not garbage collected (a weak reference is used in the Map for the key so it's not the map itself that directly prevent the garbage collection of the key). So the DefaultMockitoConfiguration and MockingProgressImpl objects stored as value in the ThreadLocalMap won't be removed until the GwtMockitoClassLoader that created their associated map keys is garbage collected (the value of the ThreadLocalMap are note weakly referenced themselves). The probem is that these map value objects are them selves loaded by the GwtMockitoClassLoader. So they prevent the garbage collection of the GwtMockitoClassLoader instances that created. Thus there is a references loop and memory leak thak keeps all the GwtMockitoClassLoader created for each tests in the memory stack of the thread that run them. In order to break this references loop, the patch proposes to remove all values loaded by the gwtMockitoClassLoader and stored in the ThreadLocalMap at the end of the test run.
Note about the patch:
Modifications done in the patch: