A JNI local is generally deleted when: 1) the JNI method returns 2) it's wrapped in PushLocalFrame/PopLocalFrame calls 3) DeleteLocalRef is called The problem is that a JNI local can be created inside an endless message pump (then the 1st point doesn't work), or out of the push/pop block (2nd doesn't work). Then the only way to avoid its leaking is to delete it manually. (For instance, see JDK-8145984 where missed JNI locals caused memory leaks.) There're two more JNI locals to delete: - AWTWindow.m, sendEvent: platformWindow - CGraphicsEnv.m, displaycb_handle: graphicsEnv