Java 如果抛出两次,预分配的OutOfMemoryError如何真正实现Throwable.getStackTrace?

Java 如果抛出两次,预分配的OutOfMemoryError如何真正实现Throwable.getStackTrace?,java,out-of-memory,Java,Out Of Memory,这是我们的后续问题 我的问题是:如果一个OutOfMemoryError是预分配的(以避免OutOfMemoryError对象内存不足的情况),并且JVM必须抛出这种类型的错误两次或两次以上,那么预分配的对象如何真正实现getStackTrace方法 如果重复使用同一个对象,那么其中一个对象很可能具有无效的getStackTrace,不是吗?答案的关键在于合同: 在某些情况下,某些虚拟机可能会从堆栈跟踪中忽略一个或多个堆栈帧。在极端情况下,如果虚拟机没有与此可丢弃项相关的堆栈跟踪信息,则允许该虚

这是我们的后续问题

我的问题是:如果一个
OutOfMemoryError
是预分配的(以避免
OutOfMemoryError
对象内存不足的情况),并且JVM必须抛出这种类型的错误两次或两次以上,那么预分配的对象如何真正实现
getStackTrace
方法


如果重复使用同一个对象,那么其中一个对象很可能具有无效的getStackTrace,不是吗?

答案的关键在于合同:

在某些情况下,某些虚拟机可能会从堆栈跟踪中忽略一个或多个堆栈帧。在极端情况下,如果虚拟机没有与此可丢弃项相关的堆栈跟踪信息,则允许该虚拟机从此方法返回零长度数组

抛出的
OutOfMemoryError
实际上不一定是预分配的错误。如果它有足够的内存来分配一个新的
OutOfMemoryError
,并带有一个适当的堆栈跟踪,那么它将被分配。但是如果它没有内存,它将使用带有堆栈跟踪信息的预分配内存。因此,如果需要抛出另一个
OutOfMemoryError
,则可以重用此类预分配对象


修改您提到的问题可以解释发生了什么:

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);
}
输出:

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)
堆栈跟踪长度=2,对象id=1743911840
堆栈跟踪长度=3,对象id=2136955031
堆栈跟踪长度=4,对象id=903470137
堆栈跟踪长度=5,对象id=1607576787

堆栈跟踪长度=0,对象id=2103957824答案的关键在于合同:

在某些情况下,某些虚拟机可能会从堆栈跟踪中忽略一个或多个堆栈帧。在极端情况下,如果虚拟机没有与此可丢弃项相关的堆栈跟踪信息,则允许该虚拟机从此方法返回零长度数组

抛出的
OutOfMemoryError
实际上不一定是预分配的错误。如果它有足够的内存来分配一个新的
OutOfMemoryError
,并带有一个适当的堆栈跟踪,那么它将被分配。但是如果它没有内存,它将使用带有堆栈跟踪信息的预分配内存。因此,如果需要抛出另一个
OutOfMemoryError
,则可以重用此类预分配对象


修改您提到的问题可以解释发生了什么:

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);
}
输出:

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)
堆栈跟踪长度=2,对象id=1743911840
堆栈跟踪长度=3,对象id=2136955031
堆栈跟踪长度=4,对象id=903470137
堆栈跟踪长度=5,对象id=1607576787
堆栈跟踪长度=0,object id=2103957824从另一个问题中可以看出,HotSpot JVM实际上会预分配
OutOfMemoryErrors
,而不带任何堆栈跟踪,并且其中有许多具有预分配的空间来填充堆栈跟踪。

从另一个问题中可以看出,HotSpot JVM实际上会预分配
OutOfMemoryErrors
不包含任何堆栈跟踪,并且其中许多具有预先分配的空间以填充堆栈跟踪