The key to the answer lies in the contract to Throwable.getStackTrace()
:
Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this throwable is permitted to return a zero-length array from this method.
The OutOfMemoryError
thrown is in fact not necessarily a preallocated one. If it has enough memory to allocated a new OutOfMemoryError
with a proper stack trace it will. But if it hasn't got memory it will use a preallocated one with no stack trace information. Such preallocated object can therefor be reused if another OutOfMemoryError
needs to be thrown.
Modifying the (best) answer to the question you mentioned explains what is going on:
private static void test(OutOfMemoryError o) {
try {
for (int n = 1; true; n += n) {
int[] foo = new int[n];
}
} catch (OutOfMemoryError e) {
System.out.println("Stack trace length=" + e.getStackTrace().length +
", object id=" + System.identityHashCode(e));
if (e == o)
System.out.println("Got the same OutOfMemoryError twice (abort)");
else
test(e);
}
}
public static void main (String[] args) {
test(null);
}
Output:
Stack trace length=2, object id=1743911840
Stack trace length=3, object id=2136955031
Stack trace length=4, object id=903470137
Stack trace length=5, object id=1607576787
Stack trace length=0, object id=2103957824 <--- new object cannot be allocated
Stack trace length=0, object id=2103957824 <--- same object reused
Got the same OutOfMemoryError twice (abort)