JCL: ensure JNI cache is initialized only once - #17800
Conversation
|
@keithc-ca - thanks for the review, all the suggestions have been addressed. regarding squashing of mixup commits - not sure what is customary in this project (squash right away or wait for the review to complete and squash it before landing, or the committer squashes the commits while landing) . pls suggest. |
|
My preference is that you squash the commits before this change is merged. You can do it as the change evolves or all at once at the end - either way works for me. |
2788cd7 to
ff571e6
Compare
|
@keithc-ca - done, addressed review comments and squashed commits into one, pls have a look. |
|
I'd like a second opinion from @gacholio. |
ff571e6 to
265cc75
Compare
|
I don't think any of these changes are actually necessary - the changes are in two static functions that are called only from a JNI native, so looping case is simply not going to happen. |
265cc75 to
91e93ba
Compare
gacholio
left a comment
There was a problem hiding this comment.
Approved for correctness, but I'm still not convinced the extra code provides any benefit.
import java.lang.management.*;
public class TX {
public static void main(String args[]) {
while(true) {
start();
collect();
sleep();
}
}
public static void start() {
Thread t[] = new Thread[32];
for(int i=0; i<32; i++) {
t[i] = new Thread() {
public void run() {
measure();
}
};
}
for(int i=0; i<32; i++) {
t[i].start();
}
for(int i=0; i<32; i++) {
try {
t[i].join();
} catch(Exception e) {}
}
}
public static void measure() {
ThreadMXBean tb = ManagementFactory.getThreadMXBean();
long[] ids = tb.getAllThreadIds();
ThreadInfo[] info = tb.getThreadInfo(ids, true, true);
}
public static void collect() {
System.gc();
}
public static void sleep() {
try {
Thread.sleep(1);
} catch(Exception e) {}
}
}
@gacholio - here is a test case that manifests the leak. You can take dumps and look at the number of jniLocalReferences or their pool counts. The actual test case is much larger, in which case the program makes use of a service loop in the JNI code (not in Java), and frequently invoke leaking functions or its caller. Given the fact that there is no exit into Java, the frames are never auto-cleared. |
|
Calling VM-provided natives from a C loop is not something we can support - they will change or be removed without notice. @tajila Comments? |
sorry, I should have been more explicit. The C loop is calling the public JNI function - in this case |
|
My comment stands - this is not a dependable way to use the VM. |
ok, fair point, let us focus only on the above test case then. |
|
Oh, the java test leaks as well? |
yes. reviewing my original use case, I think it is slightly different:
looks like the local references are not cleared when the JNI returns to immediate calling Java frame [2], instead it expects all the native frames (including [1]) to be unwound? I can share my test case if need be. |
|
That case makes a lot more sense, though I can't see a reason why the references aren't automatically freed by the native return (there's nothing special about that native). |
|
And yes, please do share your testcase - maybe there's something unexpected there that's exposing this issue. |
produced ~2k leak in a day's time for me. the leak can be seen through walking through the memory pools attached with the vmthread->jniLocalReferences. edit: the leaking objects found were of type |
|
Stepping through the JNI code performs as expected: |
|
You mention |
ok - so you mean the
Yes, those are class references, not object references. So is it root caused by a different issue? |
|
I guess I addressed all the review comments, pls have a look @gacholio @keithc-ca ! |
db18294 to
7070f91
Compare
7070f91 to
4ae7f4c
Compare
02ce70d to
a22141f
Compare
a22141f to
39d1ace
Compare
|
Back to you @keithc-ca |
d6a9bd2 to
6094ead
Compare
There was a problem hiding this comment.
Please remove all of these from this function.
|
I think the PR title and description should be updated - the history of this change isn't really relevant. |
6094ead to
8064369
Compare
the title is already modified to reflect the current change set. pls let me know if you think it can be improved. the description is changed to reflect the current change set, the history is removed. |
|
I question "streamline" when we're actually fixing a leak problem. Something more like "ensure JNI cache is initialized only once". |
|
Please update the commit message as well: this isn't a change to "streamline" anything. |
initIDCache is currently subjected for race condition, and if concurrently entered by multiple threads can result in reference leaks. Fix this by introducing a wrapper class that acts as a bridge, invoking the method from the static initializer of the wrapper class, so that the synchronization is taken care through class initializer.
8064369 to
bb97b1e
Compare
|
@keithc-ca - done |
|
Jenkins test sanity amac jdk17 |
initIDCache is currently subjected for race condition, and if concurrently entered by multiple threads can result in reference leaks. Streamline the cache population by invoking it from a class static initialiser which is inherently synchronised.