I am trying to understand the implementation of Throwable.fillInStackTrace():
private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
[...]
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
[...]
/**
* Fills in the execution stack trace. This method records within this
* {@code Throwable} object information about the current state of
* the stack frames for the current thread.
*
* <p>If the stack trace of this {@code Throwable} {@linkplain
* Throwable#Throwable(String, Throwable, boolean, boolean) is not
* writable}, calling this method has no effect.
*
* @return a reference to this {@code Throwable} instance.
* @see java.lang.Throwable#printStackTrace()
*/
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
private native Throwable fillInStackTrace(int dummy);
(source OpenJDK)
I would have guess that fillInStackTrace(int) collects the stack trace from VM internals, and then creates an array of java/lang/StackTraceElement and assigns it to the Throwable.stackTrace property.
However, the standard implementation seems to clear the value of Throwable.stackTrace directly after. When running a simple sample, the information is clearly stored in the Throwable. Sample:
Throwable th = new Throwable();
th.printStackTrace(); // prints the stack up to this point
So, where is the magic / what am I misunderstanding?
PS: what does "out of protocol state" mean in the source code?
The native
fillInStackTrace(int)method does not create an array ofStackTraceElementinstances. It captures a snapshot of the current stack trace in an implementation specific format and stores it in thebacktracevariable.Only when the array of
StackTraceElementinstances is requested, the methodgetOurStackTrace()will generate the array:It’s using the helper method
which knows how to decode the
backtrace.So if the application never accesses the stacktrace, this might be cheaper than generating the full array in advance.
You have already posted the declaration of
stackTracewhich initializes the field withUNASSIGNED_STACK. But there is this special constructor which allows to suppress stack traces altogether.So, normally
stackTraceshould be either,UNASSIGNED_STACKor an actual array created bygetOurStackTrace()or set usingsetStackTrace(StackTraceElement[]).If
stackTraceisnull, thebacktraceshould also benull, if stack traces are disabled. If this is not the case, the state is “out of protocol”, which means the throwable has not been generated using normal constructor invocation, but by native code.