28 votes

Comprendre la pile Java

Il y a un code:

 public class Main {
    public static void main(final String[] args) throws Exception {
        System.out.print("1");
        doAnything();
        System.out.println("2");
    }

    private static void doAnything() {
        try {
            doAnything();
        } catch (final Error e) {
            System.out.print("y");
        }
    }
}
 

Et il y a la sortie:

 1yyyyyyyy2
 

La question est de savoir pourquoi imprime 8 fois "y" et pas plus. Je ne comprends pas comment Java peut appeler println() lorsque StackOverflowError rencontré?

13voto

mtk Points 3982

Ici vous attraper Error et pas Exception dans ce cas, votre programme s'est écrasé.

Si vous essayez ce code (modifié pour ajouter un compteur statique)

public class StackError {

static int i = 1;

public static void main(final String[] args) throws Exception {
    System.out.print("1");
    doAnything();
    System.out.println("2");
}

private static void doAnything() {
    try {
        i++;
//          System.out.println(i);
        doAnything();
    } catch (Error e) {
        System.out.print("y"+i+"-");

    }
}
}

Sortie

 1y6869-2

Donc, il a obtenu stackerror 6869 temps(changements pour les différentes courses) et la dernière valeur est imprimée. Si vous suffit d'imprimer l' y comme vous l'avez fait plus tôt, alors qu'il pourrait le cas que la sortie est prise en bufferred et ne pas avoir vidé car il n'est pas un println.


Mise à jour

L' System.out.println en interne appelle l' PrintStream qui est mis en mémoire tampon. Vous n'avez pas perdre toutes les données de la mémoire tampon, il s'est écrit à la sortie( borne dans votre cas), après qu'il se remplit, ou lorsque vous appelez explicitement couleur sur elle.

Revenir à ce scénario, il dépend de la dynamique interne de la pile est rempli et combien d'imprimer des déclarations ont été en mesure d'obtenir exécuté à partir de la prise en doAnything() et ceux nombre de caractères ont été écrites dans la mémoire tampon. Dans le dos principal il finnally est imprimé avec le nombre 2.

javadoc référence à des flux de tampon

5voto

Javier Points 5039

Mon pari est que, en invoquant print dans le bloc catch vous force un autre StackOverflowError qui est interceptée par l'extérieur du bloc. Certains de ces appels n'auront pas assez de pile pour écrire le flux de sortie.

Le JLS dit que:

Notez que StackOverflowError, peuvent être jetés de manière synchrone par la méthode invocation ainsi que de manière asynchrone en raison de l'exécution de la méthode native ou de la machine virtuelle Java limitations de ressources.

La plate-forme Java SE permet une petite mais délimitée montant de l'exécution avant de se produire asynchrone exception est levée.

Le délai mentionné ci-dessus est autorisé à permettre le code optimisé pour détecter et jeter ces exceptions à des points où il est pratique pour gérer eux, tout en obéissant à la sémantique du langage de programmation Java. Un simple de mise en œuvre pourrait sondage pour asynchrones exceptions à l' point de contrôle de transfert d'instruction. Depuis que le programme a un de taille finie, cela fournit un bond sur le retard dans la détection d'un asynchrone exception.

3voto

SylvainL Points 2194

La première fois que l' StackOverFlowError se produit, l'appel à la dernière doAnything() est annulée et le contrôle est retourné au bloc catch de la dernière doAnything().

Cependant, parce que la pile est encore pratiquement complet, le simple fait d'appeler System.out.print("y") causerait un autre StackOverflowError en raison de la nécessité de pousser une valeur sur la pile puis de faire un appel à la fonction print().

Par conséquent, un autre StackOverflowError se produit à nouveau, et le retour est maintenant de retour sur le catch{} bloc de la précédente doAnything(); où un autre StackOverflowError va se produit parce que le besoin d'espace de pile nécessaire pour faire un seul appel à l' System.out.println("y") est supérieure à la quantité d'espace libéré à partir de la réponse à l'appel d' doAnything().

Seulement quand il y aura assez d'espace sur la pile pour exécuter un appel à l' System.out.print("y") que ce processus s'arrête et un bloc catch va réussir. Nous pouvons voir qu'en exécutant le morceau de code suivant:

public class Principal3b {

    static int a = 0;
    static int i = 0;
    static int j = 0;

    public static void main(String[] args) {
      System.out.println("X");
        doAnything();
      System.out.println("Y");
        System.out.println(i);        
        System.out.println(j);
    }

    private static void doAnything() {

        a++;
        int b = a;

        try {
            doAnything();
        } catch (final Error e) {
            i++;
            System.out.println(a);
            System.out.println(b);
            j++;
        }
    }
}

Notez qu'un println(a) est utilisé au lieu d'un print(a); par conséquent, une nouvelle ligne doit être imprimé après chaque valeur de a si tout fonctionne OK.

Cependant, quand je le lance, j'obtiens le résultat suivant:

X
62066206620662066206620662066206
6190
Y
17
1

Cela signifie qu'il y a eu 17 tentatives ro exécuter le bloc catch. De ces bloc catch exécutions, 9 sont incapables d'imprimer quoi que ce soit avant de générer eux-mêmes un StackOverflowError; 7 sont en mesure d'imprimer la valeur de 6190, mais sont incapables de l'impression d'un retour à la ligne après l'avoir avant se levant de nouveau une erreur et, enfin, il en est un qui est capable à la fois d'imprimer la valeur de 6190 et le retour à la ligne après; c'est pourquoi, finalement, permettant à son bloc catch pour effectuer sans aucune nouvelle StackOverflowError et retour gracieusement jusqu'à la pile des appels.

Comme nous avons affaire à StackOverflowError, ces chiffres ne sont qu'un exemple et peut varier considérablement, non seulement entre les machines, mais aussi entre les exécutions et le simple fait de l'ajout ou de la suppression de tout type d'instructions doit également modifier ces valeurs. Cependant, le modèle vu ici reste le même.

1voto

Evgeniy Dorofeev Points 52031

Une chose est claire: System.out.print ("y"); en capture crée ce puzzle. Si nous changeons le code comme

 static int n;

public static void main(final String[] args) throws Exception {
    System.out.println("1");
    doAnything();
    System.out.println(n);
}

private static void doAnything() {
    try {
        doAnything();
    } catch (Error e) {
        n++;
    }
}
 

il imprime

 1
1
 

0voto

Aniket Points 15250

Bien le pas. de fois que l'erreur de dépassement de pile est frappé est pas défini. Cependant, la JVM vous permet de récupérer d' StackOverflowError d'erreur et continuer l'exécution du système normalement.

Il est prouvé par le code suivant:

public class Really {
   public static void main(final String[] args) throws Exception {
     System.out.print("1");
     doAnything();
     System.out.println("2");
   }
   private static void doAnything() {
    try {
           throw new StackOverflowError();
       //doAnything();
    }
    catch(final Error e){
        System.out.print("y");
    }
   }
}

Notez cependant que @Javier a dit, l' StackOverflowError est jeté par la JVM de manière synchrone ou asynchrone(ce qui signifie qu'il peut être levée par un autre thread, peut-être un natif des threads) c'est pourquoi il n'est pas possible d'obtenir la trace de la pile de l'erreur. Le pas de. de fois que le thread(s) a frappé catch() bloc n'est pas défini.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X