Выделение памяти для исключения

#java #memory #garbage-collection

#java #память #сборка мусора

Вопрос:

Не могли бы вы, пожалуйста, объяснить, где экземпляры Exception или его дочерних элементов выделяются в памяти? Это куча или стек, или что-то еще?

Спасибо!

Ответ №1:

Для большинства JVM все объекты создаются в куче, и Exception не является исключением. 😉

JVM может выделять объекты в стеке с помощью Escape-анализа, однако обычно это ограничивается объектами, которые используются только в одном методе и не возвращаются. т. Е. крайне маловероятно, что исключения были бы хорошим кандидатом.


Особенность способа создания Throwables (включая Exception) во многих 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);
}
  

Это возвращает размер выбрасываемого объекта, созданного в текущем методе, и выбрасываемого объекта, созданного в более глубоком методе с помощью отражения (который имеет более глубокий стек)

 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 будет больше.

Комментарии:

1. Если JVM хочет соответствовать спецификации виртуальной машины, она не может создавать объекты в стеке. Стек может содержать только ссылки. Смотрите раздел 3.6.1 JVMS: java.sun.com/docs/books/jvms/second_edition/html /…

2. @musiKk, я согласен, что компилятор не может выделить объекты в виртуальном стеке для виртуальной машины. Однако то, что делает JVM при компиляции в машинный код, может сильно отличаться.

3. Я тоже думал об анализе Escape, но поскольку поля Throwable также являются объектами, это, вероятно, не сработало бы. Смотрите мой ответ.

4. Анализ выхода выполняет JVM, а не компилятор. Таким образом, не имеет значения, что говорит байт-код для виртуальной машины.

Ответ №2:

Все объекты в Java по умолчанию размещаются в куче. Вы можете специально сказать это о экземплярах исключений, потому что они обычно передаются вызывающему методу, поэтому они никак не могут находиться в стеке.

Ответ №3:

Они создаются в куче, но почему это имеет значение?

Ответ №4:

Exception s (и все Throwable объекты) точно такие же, как и любой другой объект Java. Следовательно, они появятся в куче. Как указал МузиКк, стек может содержать только примитивные значения или ссылки на объекты.

Комментарии:

1. Объекты всегда находятся в куче. В стеке могут быть только ссылки.

2. В 99,9% случаев, да… Однако последние версии JDK могут использовать Escape-анализ , чтобы определить, выходит ли ссылка на объект когда-либо за пределы текущей области видимости, и если нет, то объект вообще не создается, а примитивные поля помещаются непосредственно в стек, если позволяет пространство. С другой стороны, поля Throwable также являются объектами, поэтому им все равно пришлось бы помещаться в кучу, так что, я думаю, я не продумал все до конца. Я отредактирую это.