Skip to content

Commit

Permalink
Adding env.DeleteGlobalRefCache method to avoid memory leak after env…
Browse files Browse the repository at this point in the history
… instance gets out of scope
  • Loading branch information
Jairo Barros committed Apr 2, 2024
1 parent 87425b6 commit 29e974f
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 4 deletions.
14 changes: 10 additions & 4 deletions jnigi.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,7 @@ func (j *JVM) AttachCurrentThread() *Env {

// DetachCurrentThread calls JNI DetachCurrentThread, pass Env returned from AttachCurrentThread for current thread.
func (j *JVM) DetachCurrentThread(env *Env) error {
//free cache
for _, v := range env.classCache {
deleteGlobalRef(env.jniEnv, jobject(v))
}
env.DeleteGlobalRefCache()

if detachCurrentThread(j.javaVM) < 0 {
return errors.New("JNIGI: detachCurrentThread error")
Expand Down Expand Up @@ -1909,6 +1906,15 @@ func (j *Env) GetUTF8String() *ObjectRef {
return utf8
}

// DeleteGlobalRefCache deletes all globalRef that are in classCache. This methods should
// be called when an instance of *Env gets out of scope
func (j *Env) DeleteGlobalRefCache() {
for _, v := range j.classCache {
deleteGlobalRef(j.jniEnv, jobject(v))
}
j.classCache = make(map[string]jclass)
}

// StackTraceElement is a struct holding the contents of java.lang.StackTraceElement
// for use in a ThrowableError.
type StackTraceElement struct {
Expand Down
20 changes: 20 additions & 0 deletions jnigi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestAll(t *testing.T) {
PTestCast(t)
PTestNonVirtual(t)
PTestRegisterNative(t)
PTestDeleteGlobalRefCache(t)
PTestDestroy(t)
}

Expand Down Expand Up @@ -378,6 +379,7 @@ func PTestGetJVM(t *testing.T) {
}

func PTestDestroy(t *testing.T) {
env.DeleteGlobalRefCache()
err := jvm.Destroy()
if err != nil {
t.Fatalf("DestroyJVM failed %s", err)
Expand Down Expand Up @@ -595,6 +597,24 @@ func PTestRegisterNative(t *testing.T) {
}
}

func PTestDeleteGlobalRefCache(t *testing.T) {
str, err := env.NewObject("java/lang/String", []byte("hello world"))
if err != nil {
t.Fatal(err)
}
defer env.DeleteLocalRef(str)
var goBytes []byte
if err := str.CallMethod(env, "getBytes", &goBytes); err != nil {
t.Fatal(err)
}

env.DeleteGlobalRefCache()

if !assert.Empty(t, env.classCache) {
t.Fail()
}
}

func toGoStr(t *testing.T, o *ObjectRef) string {
var goBytes []byte
if err := o.CallMethod(env, "getBytes", &goBytes); err != nil {
Expand Down
1 change: 1 addition & 0 deletions jnigi_test_c_callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import "C"
//export go_callback_Greet
func go_callback_Greet(jenv unsafe.Pointer, jobj uintptr, arg_0 uintptr) uintptr {
env := WrapEnv(jenv)
defer env.DeleteGlobalRefCache()
env.ExceptionHandler = ThrowableToStringExceptionHandler

strArgRef := WrapJObject(arg_0, "java/lang/String", false)
Expand Down

0 comments on commit 29e974f

Please sign in to comment.