Pour décrire cela, commençons par comprendre comment les variables et objets locaux sont stockés.
Les variables locales sont stockées dans la pile :
Si vous avez regardé l'image, vous devriez pouvoir comprendre comment les choses fonctionnent.
Lorsqu'un appel de fonction est appelé par une application Java, une trame de pile est allouée sur la pile d'appels. Le cadre de pile contient les paramètres de la méthode invoquée, ses paramètres locaux et l'adresse de retour de la méthode. L'adresse de retour indique le point d'exécution à partir duquel l'exécution du programme doit se poursuivre après le retour de la méthode invoquée. S'il n'y a pas d'espace pour un nouveau cadre de pile, alors, il StackOverflowError
est lancé par la machine virtuelle Java (JVM).
Le cas le plus courant qui peut éventuellement épuiser la pile d'une application Java est la récursivité. En récursivité, une méthode s'invoque lors de son exécution. La récursivité est considérée comme une technique de programmation polyvalente puissante mais doit être utilisée avec prudence pour éviter StackOverflowError
.
Un exemple de lancement d'un StackOverflowError
est illustré ci-dessous:
StackOverflowErrorExample.java:
public class StackOverflowErrorExample {
public static void recursivePrint(int num) {
System.out.println("Number: " + num);
if (num == 0)
return;
else
recursivePrint(++num);
}
public static void main(String[] args) {
StackOverflowErrorExample.recursivePrint(1);
}
}
Dans cet exemple, nous définissons une méthode récursive, appelée recursivePrint
qui imprime un entier puis, s'appelle elle-même, avec le prochain entier successif comme argument. La récursivité se termine jusqu'à ce que nous passions en 0
paramètre. Cependant, dans notre exemple, nous avons passé le paramètre de 1 et ses suiveurs croissants, par conséquent, la récursivité ne se terminera jamais.
Un exemple d'exécution, utilisant l' -Xss1M
indicateur qui spécifie la taille de la pile de threads à 1 Mo, est illustré ci-dessous:
Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
at java.io.PrintStream.write(PrintStream.java:480)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:527)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
...
En fonction de la configuration initiale de la JVM, les résultats peuvent différer, mais finalement ils StackOverflowError
doivent être levés. Cet exemple est un très bon exemple de la façon dont la récursivité peut causer des problèmes, si elle n'est pas implémentée avec prudence.
Comment gérer la StackOverflowError
La solution la plus simple consiste à inspecter soigneusement la trace de la pile et à détecter le motif répétitif des numéros de ligne. Ces numéros de ligne indiquent que le code est appelé de manière récursive. Une fois que vous avez détecté ces lignes, vous devez inspecter soigneusement votre code et comprendre pourquoi la récursivité ne se termine jamais.
Si vous avez vérifié que la récursivité est correctement implémentée, vous pouvez augmenter la taille de la pile, afin d'autoriser un plus grand nombre d'appels. En fonction de la machine virtuelle (JVM) Java installée, la taille de la pile de threads par défaut peut être égal soit 512Ko ou 1Mo . Vous pouvez augmenter la taille de la pile de threads à l'aide de l' -Xss
indicateur. Cet indicateur peut être spécifié soit via la configuration du projet, soit via la ligne de commande. Le format de l'
-Xss
argument est:
-Xss<size>[g|G|m|M|k|K]