Comment la JVM gère-t-elle une exception levée par la méthode principale?

10

Je comprends les exceptions, les jeter, les gérer et les propager vers une méthode plus basse dans la pile des appels (ie throws).

Ce que je ne comprends pas, c'est ceci:

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

Maintenant, je suppose que dans le cas qui mainlance un Exception, la JVM le gère (correct?). Si tel est le cas, alors ma question est:

Comment la JVM gère-t-elle les exceptions levées main? Qu'est ce que ça fait?

Aviv Cohn
la source

Réponses:

19

Vous pourriez penser que la public static void mainméthode en Java ou la mainfonction en C est le véritable point d'entrée de votre programme - mais ce n'est pas le cas. Toutes les langues de haut niveau (y compris C) ont un runtime de langue qui initialise le programme, puis transfère le flux de contrôle au point d'entrée. Dans le cas de Java, l'initialisation comprendra:

  • configuration de la JVM
  • chargement des classes requises
  • exécuter des blocs d'initialisation statiques. Cela peut exécuter du code défini par l'utilisateur avant mainson appel. Ces blocs ne sont pas censés lever d'exceptions.

Il existe plusieurs façons d'implémenter la gestion des exceptions, mais pour les besoins de cette question, elles peuvent toutes être considérées comme une boîte noire. L'important, cependant, est que le langage d'exécution doit toujours fournir un gestionnaire d'exceptions le plus à l'extérieur qui intercepte toutes les exceptions qui ne sont pas détectées par le code utilisateur. Ce gestionnaire d'exceptions imprime généralement une trace de pile, arrête le programme de manière ordonnée et se termine avec un code d'erreur. L'arrêt correct du programme comprend la destruction du graphique d'objet, l'appel des finaliseurs et la libération de ressources telles que la mémoire, les descripteurs de fichiers ou les connexions réseau.

À des fins d'illustration, vous pouvez créer une image du runtime enveloppant tout le code dans un essai géant qui ressemble à

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

sauf qu'il n'est pas nécessaire qu'un langage exécute réellement du code comme celui-ci. La même sémantique peut être implémentée dans le code throw(ou équivalent) qui recherche le premier gestionnaire d'exceptions applicable.

amon
la source
9

Tout le code Java s'exécute dans le contexte d'un thread . Le JavaDoc lié explique le traitement des erreurs et les critères de sortie, mais voici l'essentiel:

  • La JVM se tourne et prépare l'environnement d'exécution.
  • La JVM crée un thread qui exécutera la main()méthode en utilisant les paramètres de ligne de commande applicables.
  • La JVM définit un gestionnaire d'exceptions non capturé par défaut qui imprime l'exception à l'erreur standard et se termine.
  • La JVM exécute le thread.

Dans le cas d'une exception non capturée, le programme meurt effectivement selon le troisième élément ci-dessus. Ce comportement est spécifié dans la spécification du langage Java, section 11.3


information additionnelle

D'autres ont mentionné des blocs statiques et comment ils s'exécutent auparavant main(). Cependant, cela nécessite un peu plus d'explications pour bien comprendre.

Lors du chargement d'une classe, le chargeur de classe doit initialiser tous les static finalétats et exécuter tous les staticblocs avant que la classe puisse être utilisée, pour inclure des instances instanciantes de la classe (à part: créer une classe Java où une constante de classe est initialisée dans un bloc statique après avoir créé un instance de la classe et le constructeur référence la constante. Boom!). Cependant, tout cela se produit dans la logique du chargeur de classe avant qu'un code puisse référencer la classe . De plus, la classe est chargée dans le thread référencé par la classe.

Cela signifie que si la classe contenant fait main()référence à une autre classe (par exemple une constante de classe), cette classe doit être chargée avant d'être main()exécutée pour inclure ses blocs statiques. Sinon, les blocs statiques sont exécutés comme ci-dessus. Si la classe ne parvient pas à charger, alors la classe contenant main()échouera également à charger et le programme se terminera.

Autre info: les blocs statiques peuvent se lancer. Errorssont jetés tels quels. Exceptionssont interdits (erreur de compilation). RuntimeExceptionssont enveloppés dans ExceptionInInitializerError . Celles-ci sont gérées par le gestionnaire d'exceptions non capturé, qui généralement tuera le thread ou l'application (thread principal) sauf si vous encapsulez soigneusement la référence de classe (et le chargement) dans un try- catch.

Benni
la source