异常的内存分配

jde*_*lop 6 java memory garbage-collection

你能解释一下,Exception或它的孩子的实例在内存中被分配了吗?是堆还是堆栈,还是其他什么?

谢谢!

Pet*_*rey 4

对于大多数 JVM 来说,所有对象都是在堆上创建的,异常也不例外。;)

JVM 可以使用逃逸分析在堆栈上分配对象,但这通常仅限于仅在一种方法中使用且不返回的对象。即,异常不太可能是一个好的候选者。


在许多 JVM 上创建 Throwables(包括异常)的方式的特殊之处在于,直到需要时才创建堆栈跟踪元素。这是因为大多数时候不需要它们,而且创建它们的成本很高。然而,创建堆栈跟踪的信息由 JVM 保留在某些位置并与 Throwable 相关联,但它对调试器或反射不可见。

public static void main(String... args) {
    Throwable t = new Throwable("here");
    System.out.println("Throwable before getStackTrace()");
    shallowDump(t);

    System.out.println("\nThrowable after getStackTrace()");
    t.getStackTrace();
    shallowDump(t);
}

private static void shallowDump(Object pojo) {
    for (Field f : pojo.getClass().getDeclaredFields()) {
        if (Modifier.isStatic(f.getModifiers())) continue;
        f.setAccessible(true);
        Object o;
        try {
            o = f.get(pojo);
            if (o == pojo)
                o = "{self}";
            if (o instanceof Object[])
                o = "Array of "+(o.getClass().getComponentType());
        } catch (Exception e) {
            o = e;
        }
        System.out.println(f.getName() + ": " + o);
    }
}
Run Code Online (Sandbox Code Playgroud)

印刷

Throwable before getStackTrace()
detailMessage: here
cause: {self}
stackTrace: null

Throwable after getStackTrace()
detailMessage: here
cause: {self}
stackTrace: Array of class java.lang.StackTraceElement
Run Code Online (Sandbox Code Playgroud)

那么问题来了,用于创建StackTraceElement的信息保留在哪里。查看代码,使用本机方法来访问信息。有一个神秘的领域backtrace,你无法使用反射看到它。

System.gc();
for (int i = 0; i < 5; i++) {
    Throwable[] ts = new Throwable[10000];
    long free = Runtime.getRuntime().freeMemory();
    for (int j = 0; j < ts.length; j++)
        ts[j] = new Throwable();
    long used = free - Runtime.getRuntime().freeMemory();
    System.out.printf("Average Throwable size was %,d%n", used / ts.length);
}
System.gc();
for (int i = 0; i < 5; i++) {
    Throwable[] ts = new Throwable[10000];
    long free = Runtime.getRuntime().freeMemory();
    for (int j = 0; j < ts.length; j++)
        ts[j] = Throwable.class.newInstance();
    long used = free - Runtime.getRuntime().freeMemory();
    System.out.printf("Average Throwable.class.newInstance() size was %,d%n", used / ts.length);
}
Run Code Online (Sandbox Code Playgroud)

这将获取当前方法中创建的 Throwable 的大小以及通过反射在更深的方法中创建的 Throwable(具有更深的堆栈)的大小

Average Throwable size was 302
Average Throwable size was 302
Average Throwable size was 302
Average Throwable size was 302
Average Throwable size was 302
Average Throwable.class.newInstance() size was 247
Average Throwable.class.newInstance() size was 296
Average Throwable.class.newInstance() size was 296
Average Throwable.class.newInstance() size was 296
Average Throwable.class.newInstance() size was 296
Run Code Online (Sandbox Code Playgroud)

Throwable 的大小比您对它所包含的字段的预期要大得多。可以假设堆上存储了一些附加信息来帮助此类,但是,如果所有信息都存储在 Throwable 对象中,则您预计第二种类型的 Throwable 会更大。