Closed jairobjunior closed 6 months ago
I ran into the same problem just recently too. Looks good. Thanks for all the details, writing the tests and comment doc! :tada:
Currently we do delete the cache in func (j *JVM) DetachCurrentThread(env *Env) error
here. We should do this in the more general way using the method you provided, replacing the loop.
@timob Sounds good. I just replaced the loop you mentioned with a call to the method env.DeleteGlobalRefCache()
.
Thanks!
✨ Types of changes
💡 Motivation and Context
My application was recently experiencing OOM after running for a couple days. The architecture of the app is Java running on top, calling into JNI using
jnigi
then calling into Go. Java can make many calls to the JNI layer with different types of parameters, includingString
.This was a very interesting/hard leak to find, because the memory stats from the Main app running in Java didn't indicate any abnormal growth, but the RSS values definitely had a very linear curve. So we started isolating the components and verified that the leak was coming from the JNI. I then wrote a stress test app and used
jemalloc
to trace the memory on the stack.jemalloc
gave me this chart:That showed me that there is a large % of memory used by
jni_NewGlobalRef
that wasn't getting freed. For some reason the map symbols were not working on the memory boxes above the global ref. I then started tracing wherejnigi
callsjni_NewGlobalRef
and that was how I got to the problem.🐛 Problem
Here is how my JNI layer looks with the leak:
That looked sort of harmless in the beginning, until we had to run the app for days.
The issue lives inside of the
env.classCache
. Whenenv
gets out of scope, all of theenv.classCache
will also get destroyed. The catch is that those objects inside ofenv.classCache
were created usingNewGlobalRef
bycallFindClass
and are not deleted whenenv
gets out of scope, creating dangling references.🎯 Solution
This PR is introducing a new method called
env.DeleteGlobalRefCache()
, this method should be called after you want to dispose an instance of*jnigi.Env
. It'll calldeleteGlobalRef
in all of theclassCache
and clear the map as well.