Explanation on the anatomy of a JNI call and return
Fiterau Paul
fiteraup at yahoo.com
Fri Jan 25 03:11:00 PST 2013
Thanks guys for all of your responses. They have all been helpful. I'll try Maxine out.I think I made one major mistake in my question. Confusing a native call with a call to an already compiled method. Let's say, we can have only compiled methods and interpreted methods with JIT no longer active. What would yield the worst stack consumption, in terms of a simple sequence call: a -> b -> c. Having b and c compiled, or having b compiled c interpreted.
>> Hello guys. Sorry for the intrusion. Looked everywhere for this information, no luck. Browsed the code, very difficult to get an idea from it. 20 hours of search >>with no result. Also posted on StackOverflow, http://stackoverflow.com/questions/14497754/the-jni-call-its-effect-on-the-two-stacks, got no response. If >>someone could explain to me the evolution of the native and the java stack during a JNI call invocation, I'd be very appreciative.
>>
>> What I could find over the internet is this: http://weblogs.java.net/blog/mlam/archive/2006/12/a_tale_of_two_s.html . But this is not Hotspot. Plus, I'm doubtful >>on whether the information is accurate, since, in a native to native call, you'd get a recurse in executeJava, the interpreter (like shown bellow) and I suspect the >>interpreter does not have the lightest of stack frames.
>> * native stack: executeJava -> mNa -> executeJava -> mNb
>>
>> * Java stack: mNa -> mNb
>> My question is, what's the anatomy of a java to native, native to native call and native to java in terms of stack frames. The purpose is, for a School project I >>have involving static analysis, where I should infer a very conservative upper bound for both the Java stack and the native stack. Right now there's a tool >>inferring the upper bound for the Java stack, but it doesn't take into account that some methods might already be compiled. I can use that measure in my >>inference.
>Not a simple question to answer as the details are very complex.
>The Java stack and the native stack are quite distinct and the details depend on whether you are running in interpreted code or compiled code. The Java stack is a >logical stack of linked frames. The native stack frames need to be linked to the Java stack frames by some mechanism so that we can do stack walking.
>When you hit an invocation bytecode for a native method you call into a stub routine that prepares the Java stack and native stack so that the native method can >be called directly. This involves marshalling arguments and performing thread-state transitions to mark the thread as executing native code. When the native >method returns it marshalls any return value, cleans up the stack and returns to the point where you can execute the next bytecode.
>If your native method wants to call a Java method then it needs to use the JNI API, so that adds further marshalling etc and prepares the thread to recommence >executing bytecodes.
By this, do you mean there's a kindof like a transitional frame added, whenever a interpreted calls a compiled and vice versa. What I suspected originally is that, let's say we have a, b, c all interpreted methods. a calls b, which calls c.
The stack would look like:
java stack: a frame -> b frame -> c frame
native stack: interpreter frame
Where the interpreter executes (interprets) all the bytecodes of the methods.
The tool my research is done on, only does stack size upper bound approximation considering this case and only for the java stack. The goal would be to get some sort of upper bound for both the native stack size and the java stack size considering the fact that some methods might already be compiled. As input information, you can use the java stack size measurements done presuming all methods are interpreted.
If in the sequence a b c, b is already compiled, while a and c are interpreted:
java stack: a frame -> transitional frame from b to c -> c frame ->
native stack: interpreter frame -> transitional frame from a to b -> b frame -> interpreter frame
I suspected that the recurse through the interpreter frame would be the main source of stack cost. Another source of cost would consist of the transition frames.
If both b and c are compiled, then, in the article I posted, it was stated that detecting whether the a method is already compiled or not is done in the interpreter. By that, I deduce that, in the case both are compiled, you'd still need a recurse through the interpreter.
java stack: a frame ->
native stack: interpreter frame -> transitional frame from a to b -> b frame -> interpreter frame -> c frame
Now, I'm not sure if this holds any water for Hotspot. Especially the last scenario.
Analyzing statically to get an upper measurement is pretty much impossible, if you have an active JIT compiler. But presuming all compilation is done ahead of time (not what's done with Hotspot, I know), is the evolution similar to what I've mentioned above? Because if that's the case, then, the worst case scenario for the native stack size would be having all these interpreter frames.
If transitional frames do exist between compiled methods and interpreted methods and interpreted and compiled, can there be an upper bound placed on them? For example, something like, a transitional stack frame from "a to b" consumes less on the stack than the stack frame "a". And is the recursing through the interpreter frames done like how I pictured?
>> I'm especially interested in what would be the worst case with regards to stack consumption in a call chain. Let's say A calls B which calls C which calls D.
>> Would it be more stack consuming if B, C and D were all native or it's more consuming if they are interleaved (one native one not).
>> From what tests I made, it seems that having all of them native does seem to yield the highest consumption. On the Native stack and on the java stack.
>I'm not completely clear on what you mean with the interleaving. Once you are in a native method then you can call another native method directly - not via JNI - >so that yields minimum stack usage. Going back and forth between Java and native would consume the most stack as you have a lot of "management" code >around the transitions.
I think I got JNI and compiled methods mixed up. But would interleaving between already compiled and interpreted method calls yield the same result? Higher maximum reached size if you have interleaving than if subsequent calls are all compiled:
interpreted a calls already compiled b, which calls interpreted c
than
interpreted a calls already compiled b, which calls already compiled c
>There is some information here:
>https://wikis.oracle.com/display/HotSpotInternals/Home
>but unfortunately most of the CallingSequence information is missing.
>David Holmes
>------------
Yeah, I noticed that already. Information not completed there is exactly the information I was looking for.
Thanks loads for the responses so far. Feel a bit uncomfortable asking the gurus around here, but my efforts of digging through the code and Internet haven't been at all fruitful. Another problem is, the tool I'm supposed to be helping, I don't have access to it. So there's quite a bit of confusion with regards to requirements.
Paul.
More information about the hotspot-dev
mailing list