Perché è possibile eseguire il ripristino da StackOverflowError?


100

Sono sorpreso di come sia possibile continuare l'esecuzione anche dopo che si StackOverflowErrorè verificato un errore in Java.

So che StackOverflowErrorè una sottoclasse della classe Error. La classe Error viene dichiarata "una sottoclasse di Throwable che indica problemi seri che un'applicazione ragionevole non dovrebbe tentare di rilevare".

Questo suona più come una raccomandazione che come una regola, sottintendendo che la cattura di un errore come StackOverflowError è di fatto consentita e spetta alla ragionevolezza del programmatore non farlo. E vedi, ho testato questo codice e termina normalmente.

public class Test
{
    public static void main(String[] args)
    {
        try {
            foo();
        } catch (StackOverflowError e) {
            bar();
        }
        System.out.println("normal termination");
    }

    private static void foo() {
        System.out.println("foo");
        foo();
    }

    private static void bar() {
        System.out.println("bar");
    }
}

Come può essere questo? Penso che quando viene lanciato StackOverflowError, lo stack dovrebbe essere così pieno che non c'è spazio per chiamare un'altra funzione. Il blocco di gestione degli errori è in esecuzione in uno stack diverso o cosa sta succedendo qui?



57
Faccio sempre errori su StackOverflow. Ma non mi impedisce di tornare indietro.

10
Yo dawg ... Ho sentito che ti sono piaciuti gli stack overflow, quindi abbiamo inserito uno stack overflow nel tuo stackoverflow.com!
Pierre Henry

Perché le architetture moderne utilizzano i Frame Pointer per facilitare lo svolgimento di pile, anche parziali. Finché il codice e il contesto per farlo non devono essere allocati dinamicamente fuori dallo stack, non dovrebbero esserci problemi.
RBarryYoung

Risposte:


119

Quando lo stack va in overflow e StackOverflowErrorviene lanciato, la solita gestione delle eccezioni lo sblocca. Svolgere la pila significa:

  • interrompe l'esecuzione della funzione attualmente attiva
  • cancella il suo stack frame, procedi con la funzione chiamante
  • interrompe l'esecuzione del chiamante
  • cancella il suo stack frame, procedi con la funzione chiamante
  • e così via...

... finché non viene rilevata l'eccezione. Ciò è normale (in effetti, necessario) e indipendente da quale eccezione viene generata e perché. Poiché si cattura l'eccezione al di fuori della prima chiamata a foo(), le migliaia di foostack frame che hanno riempito lo stack sono state tutte svolte e la maggior parte dello stack è libera di essere riutilizzata.


1
@fge Sentiti libero di modificare, ho considerato un'interruzione di paragrafo ma non sono riuscito a trovare un posto in cui sembrasse buono.

1
Potresti usare gli
elenchi

1
Il punto è che il più interno footermina con uno stato indefinito, quindi si deve presumere che qualsiasi oggetto che potrebbe aver toccato sia rotto. Poiché non sai in quale funzione si è verificato l'overflow dello stack, solo che deve essere un discendente del tryblocco che lo ha catturato, qualsiasi oggetto che può essere modificato con qualsiasi metodo raggiungibile da lì è ora sospetto. Di solito non vale la pena scoprire cosa è successo e provare a risolverlo.
Simon Richter

2
@delnan, penso che la risposta sia incompleta senza entrare anche nei dettagli perché questa è una cattiva idea. La differenza rispetto a un'eccezione generata in modo esplicito è che Errornon possono essere anticipati anche durante la scrittura di codice sicuro rispetto alle eccezioni.
Simon Richter

1
@SimonRichter No, la domanda è piuttosto specifica. Non si tratta di gestire Errors. L'OP chiede solo circa StackOverflowError, ed è chiedere una cosa specifica del trattamento di questo errore: come può una chiamata di metodo non riuscito quando e 'colto in questo errore.
Bakuriu

23

Quando viene generato StackOverflowError, lo stack è pieno. Tuttavia, quando viene catturato , tutte quelle foochiamate sono state estratte dallo stack. barpuò essere eseguito normalmente perché lo stack non è più traboccante di foos. (Nota che non penso che JLS ti garantisca di poter recuperare da uno stack overflow come questo.)


12

Quando si verifica StackOverFlow, la JVM si aprirà fino al punto di cattura, liberando lo stack.

Nel tuo esempio, elimina tutti i foo impilati.


8

Perché lo stack in realtà non trabocca. Un nome migliore potrebbe essere AttemptToOverflowStack. Fondamentalmente ciò che significa è che l'ultimo tentativo di regolare lo stack frame sbaglia perché non c'è abbastanza spazio libero rimasto sullo stack. Lo stack potrebbe effettivamente avere molto spazio rimasto, ma non abbastanza. Quindi, qualunque operazione sarebbe dipesa dal successo della chiamata (tipicamente un'invocazione di un metodo), non viene mai eseguita e tutto ciò che resta è che il programma si occupi di questo fatto. Il che significa che non è davvero diverso da qualsiasi altra eccezione. In effetti, potresti catturare l'eccezione nella funzione che sta effettuando la chiamata.


1
Fai solo attenzione se lo fai che il tuo gestore delle eccezioni non richieda più spazio nello stack di quanto sia disponibile!
Vince

2

Come è già stato risposto , è possibile eseguire codice, ed in particolare chiamare funzioni, dopo aver catturato un StackOverflowErrorperché la normale procedura di gestione delle eccezioni della JVM srotola lo stack tra throwi catchpunti e, liberando spazio per lo stack da utilizzare. E il tuo esperimento conferma che è così.

Tuttavia, ciò non equivale a dire che, in generale, è possibile recuperare da a StackOverflowError.

UN StackOverflowError È-A VirtualMachineError, che È-AN Error. Come fai notare, Java fornisce alcuni vaghi consigli per Error:

indica problemi seri che un'applicazione ragionevole non dovrebbe cercare di rilevare

e tu, ragionevolmente, concludi che dovrebbe sembrare catturare un Errorpotrebbe essere OK in alcune circostanze. Nota che l'esecuzione di un esperimento non dimostra che qualcosa sia, in generale, sicuro da fare. Solo le regole del linguaggio Java e le specifiche delle classi che utilizzi possono farlo. A VirtualMachineErrorè una classe speciale di eccezione, perché la specifica del linguaggio Java e il file Java Virtual Machine forniscono informazioni sulla semantica di questa eccezione. In particolare, quest'ultimo dice :

Un'implementazione di Java Virtual Machine genera un oggetto che è un'istanza di una sottoclasse della classe VirtualMethodErrorquando un errore interno o una limitazione delle risorse impedisce di implementare la semantica descritta in questo capitolo. Questa specifica non può prevedere dove si possono incontrare errori interni o limitazioni di risorse e non impone esattamente quando possono essere segnalati. Pertanto, una qualsiasi delle VirtualMethodErrorsottoclassi definite di seguito può essere generata in qualsiasi momento durante il funzionamento della Java Virtual Machine:

...

  • StackOverflowError: L'implementazione della Java Virtual Machine ha esaurito lo spazio dello stack per un thread, in genere perché il thread sta eseguendo un numero illimitato di chiamate ricorsive a causa di un errore nel programma in esecuzione.

Il problema cruciale è che "non puoi prevedere" dove o quando StackOverflowErrorverrà lanciato un testamento. Non ci sono garanzie su dove non verrà lanciato. Non puoi fare affidamento sul fatto che venga lanciato all'ingresso di un metodo, ad esempio. Potrebbe essere lanciato in un punto all'interno di un metodo.

Questa imprevedibilità è potenzialmente disastrosa. Poiché può essere lanciato all'interno di un metodo, potrebbe essere lanciato a metà di una sequenza di operazioni che la classe considera un'operazione "atomica", lasciando l'oggetto in uno stato parzialmente modificato, incoerente. Con l'oggetto in uno stato incoerente, qualsiasi tentativo di utilizzare quell'oggetto potrebbe provocare un comportamento errato. In tutti i casi pratici non puoi sapere quale oggetto si trova in uno stato incoerente, quindi devi presumere che nessun oggetto sia affidabile. Qualsiasi operazione di ripristino o tentativo di continuare dopo che l'eccezione è stata rilevata potrebbe quindi avere un comportamento errato. L'unica cosa sicura da fare è quindi non prendere un fileStackOverflowError, ma piuttosto per consentire la chiusura del programma. (In pratica potresti tentare di eseguire una registrazione degli errori per aiutare la risoluzione dei problemi, ma non puoi fare affidamento sul fatto che la registrazione funzioni correttamente). Cioè, non puoi recuperare in modo affidabile da un fileStackOverflowError .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.