W jaki sposób JVM obsługuje wyjątek zgłoszony przez metodę główną?

10

Rozumiem wyjątki, rzucając je, obsługując je i propagując do metody znajdującej się niżej na stosie wywołań (tj throws.).

Nie rozumiem tego:

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

Teraz zakładam, że w przypadku, który mainwyrzuca an Exception, JVM obsługuje to (prawda?). Jeśli tak jest, moje pytanie brzmi:

W jaki sposób JVM obsługuje zgłoszone wyjątki main? Co to robi?

Aviv Cohn
źródło

Odpowiedzi:

19

Możesz myśleć, że public static void mainmetoda w Javie lub mainfunkcja w C jest prawdziwym punktem wejścia twojego programu - ale tak nie jest. Wszystkie języki wysokiego poziomu (w tym C) mają język wykonawczy, który inicjuje program, a następnie przenosi przepływ sterowania do punktu wejścia. W przypadku Javy inicjalizacja będzie obejmować:

  • konfigurowanie JVM
  • ładowanie wymaganych klas
  • uruchamianie statycznych bloków inicjalizacyjnych. Może to wykonać kod zdefiniowany przez użytkownika przed mainjego wywołaniem. Te bloki nie powinny generować wyjątków.

Istnieje wiele sposobów implementacji obsługi wyjątków, ale na potrzeby tego pytania wszystkie mogą być postrzegane jako czarna skrzynka. Ważne jest jednak to, że środowisko wykonawcze języka musi zawsze zapewniać najbardziej zewnętrzną procedurę obsługi wyjątków, która przechwytuje wszystkie wyjątki, które nie są przechwytywane przez kod użytkownika. Ten moduł obsługi wyjątków zwykle drukuje ślad stosu, zamyka program w uporządkowany sposób i kończy działanie z kodem błędu. Prawidłowe zamknięcie programu obejmuje zniszczenie grafu obiektowego, wywołanie finalizatorów i zwolnienie zasobów, takich jak pamięć, uchwyty plików lub połączenia sieciowe.

Na potrzeby ilustracji można zobrazować środowisko wykonawcze, zawijając cały kod w gigantyczny, podobny do try-catch

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

z wyjątkiem tego, że język nie musi faktycznie wykonywać takiego kodu. Tę samą semantykę można zaimplementować w kodzie throw(lub równoważnym), który wyszukuje pierwszą odpowiednią procedurę obsługi wyjątków.

amon
źródło
9

Cały kod Java działa w kontekście wątku . Połączony JavaDoc wyjaśnia obsługę błędów i kryteria wyjścia, ale oto ich sedno:

  • JVM uruchamia się i przygotowuje środowisko wykonawcze.
  • JVM tworzy wątek, który uruchomi main()metodę przy użyciu dowolnych parametrów wiersza poleceń.
  • JVM ustawia domyślną procedurę przechwytywania wyjątków, która drukuje wyjątki na błąd standardowy i kończy działanie.
  • JVM wykonuje wątek.

W przypadku niewyłapanego wyjątku program skutecznie umiera zgodnie z trzecim punktem powyżej. To zachowanie jest bardziej szczegółowo określone w specyfikacji języka Java, sekcja 11.3


dodatkowe informacje

Inni wspominali o blokach statycznych i ich wcześniejszym działaniu main(). Wymaga to jednak nieco więcej wyjaśnień, aby poprawnie zrozumieć.

Podczas ładowania klasy moduł ładujący klasy musi zainicjować wszystkie static finalstany i uruchomić wszystkie staticbloki, zanim będzie można użyć klasy, aby uwzględnić wystąpienia instancji klasy (na bok: utwórz klasę Java, w której stała klasy jest inicjowana w bloku statycznym po utworzeniu wystąpienie klasy, a konstruktor odwołuje się do stałej. Boom!). Wszystko to dzieje się jednak w logice modułu ładującego klasy, zanim jakikolwiek kod będzie mógł odwoływać się do klasy . Ponadto klasa jest ładowana do dowolnego wątku, do którego się ona odnosi.

Oznacza to, że jeśli klasa zawierająca main()odwołania odwołuje się do innej klasy (np. Stała klasy), to klasa ta musi zostać załadowana przed main()wykonaniem w celu włączenia bloków statycznych. W przeciwnym razie bloki statyczne są wykonywane jak wyżej. Jeśli klasa się nie załaduje, wówczas klasa zawierająca main()również nie załaduje się, a program zakończy działanie.

Kolejna informacja: bloki statyczne mogą rzucać. Errorssą rzucane takimi, jakimi są. Exceptionssą zabronione (błąd czasu kompilacji). RuntimeExceptionssą opakowane w ExceptionInInitializerError . Są one obsługiwane przez nieprzechwyconą procedurę obsługi wyjątków, która zazwyczaj albo zabije wątek, albo aplikację (główny wątek), chyba że ostrożnie zawiniesz odwołanie do klasy (i załadujesz) w try- catch.

Benni
źródło