Как JVM обрабатывает исключение, выброшенное методом main?

10

Я понимаю исключения, выбрасываю их, обрабатываю их и распространяю на метод ниже в стеке вызовов (т.е. throws).

Что я не понимаю, так это:

public static void main(String[] args) throws Exception {
    ...
}

Теперь я предполагаю, что в случае, когда mainвыбрасывает Exception, JVM обрабатывает это (правильно?). Если это так, то мой вопрос:

Как JVM обрабатывает возникающие исключения main? Что оно делает?

Авив Кон
источник

Ответы:

19

Вы можете подумать, что public static void mainметод в Java или mainфункция в C - это реальная точка входа в вашу программу, но это не так. Все языки высокого уровня (включая C) имеют языковую среду выполнения, которая инициализирует программу, а затем передает поток управления в точку входа. В случае Java инициализация будет включать:

  • настройка JVM
  • загрузка необходимых классов
  • запуск статических блоков инициализатора. Это может выполнить пользовательский код перед mainвызовом. Эти блоки не должны генерировать исключения.

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

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

try {
    loadClasses();
    runInitializers();
    main(argv);
    System.exit(0);
} catch (Throwable e) {
    e.printStackTrace();
    System.exit(-1);
}

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

Амон
источник
9

Весь код Java выполняется в контексте потока . Связанный JavaDoc объясняет критерии обработки ошибок и выхода, но вот суть этого:

  • JVM раскручивается и подготавливает среду исполнения.
  • JVM создает поток, который будет запускать main()метод с использованием любых параметров командной строки.
  • JVM устанавливает обработчик необработанных исключений по умолчанию, который печатает исключение со стандартной ошибкой и завершает работу.
  • JVM выполняет поток.

В случае невыясненного исключения программа фактически умирает согласно третьему пункту выше. Это поведение дополнительно определено в Спецификации языка Java, Раздел 11.3


Дополнительная информация

Другие упоминали статические блоки и как они выполняются раньше main(). Тем не менее, это требует немного большего объяснения, чтобы понять правильно.

При загрузке класса загрузчик класса должен инициализировать все static finalсостояния и запустить все staticблоки перед использованием класса, чтобы включить создание экземпляров класса (кроме: создайте класс Java, где константа класса инициализируется в статическом блоке после создания экземпляр класса, а конструктор ссылается на константу. Бум!). Однако все это происходит в логике загрузчика классов, прежде чем любой код может ссылаться на класс . Кроме того, класс загружается в любом потоке, на который ссылается класс.

Это означает, что если класс, содержащий main()ссылки, ссылается на другой класс (например, константа класса), то этот класс должен быть загружен перед выполнением, main()чтобы включить его статические блоки. В противном случае статические блоки выполняются, как указано выше. Если класс не загружается, то содержащий его класс main()также не загружается, и программа завершается.

Еще одно замечание: статические блоки могут бросать. Errorsброшены как есть. Exceptionsзапрещены (ошибка времени компиляции). RuntimeExceptionsобернуты в ExceptionInInitializerError . Они обрабатываются с помощью обработчика неперехваченных исключений, который обычно либо уничтожает поток, либо приложение (основной поток), если вы не аккуратно заключите ссылку на класс (и загрузку) в try- catch.

Benni
источник