Для большинства объектов JVM все объекты создаются в куче, и исключение не является исключением.;)
JVM может размещать объекты в стеке с помощью Escape-анализа, однако это обычно ограничивается объектами, которые используются только в одном методе и не возвращаются.то есть крайне маловероятно, что исключения будут хорошим кандидатом.
Особенность метода Throwable (в том числе исключения), создаваемого во многих JVM, заключается в том, что элементы трассировки стека не создаются, пока онинеобходимы.Это потому, что большую часть времени они не нужны и их создание дорого.Однако информация для создания трассировки стека сохраняется где-то в 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);
}
}
печатает
Throwable before getStackTrace()
detailMessage: here
cause: {self}
stackTrace: null
Throwable after getStackTrace()
detailMessage: here
cause: {self}
stackTrace: Array of class java.lang.StackTraceElement
Таким образом, возникает вопрос, где хранится информация, которая использовалась для создания 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);
}
Получает размер 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
Размер Throwable намного больше, чем можно было бы ожидать от полей, которые он имеет.Можно предположить, что некоторая дополнительная информация хранится в куче, чтобы помочь этому классу, однако, если вся информация была сохранена в объекте Throwable, вы ожидаете, что второй тип Throwable будет больше.