Bir StackOverflowError'dan kurtarmak neden mümkündür?


100

StackOverflowErrorJava'da bir meydana geldikten sonra bile yürütmeye devam etmenin nasıl mümkün olduğuna şaşırıyorum .

Bunun StackOverflowErrorError sınıfının bir alt sınıfı olduğunu biliyorum . Error sınıfı, "makul bir uygulamanın yakalamaya çalışmaması gereken ciddi sorunları belirten bir Throwable alt sınıfı" olarak tanımlanır.

Bu, bir kuraldan çok bir öneriye benziyor, StackOverflowError gibi bir Hata yakalamaya aslında izin verildiğini ve bunu yapmamak programcının mantıklılığına bağlı. Ve bakın, bu kodu test ettim ve normal şekilde sona eriyor.

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");
    }
}

Bu nasıl olabilir? Bence StackOverflowError atıldığında, yığın o kadar dolu olmalıdır ki başka bir işlevi çağırmak için yer kalmaz. Hata işleme bloğu farklı bir yığında mı çalışıyor veya burada neler oluyor?



57
StackOverflow'da her zaman hata yapıyorum. Yine de geri gelmemi engellemiyor.

10
Hey dostum ... Yığın taşmalarını sevdiğini duydum, bu yüzden stackoverflow.com'a bir yığın taşması koyduk!
Pierre Henry

Çünkü modern mimariler, kısmi olanlar da dahil olmak üzere yığınların çözülmesini kolaylaştırmak için Çerçeve İşaretçileri kullanır. Bunu yapmak için kod + bağlamın dinamik olarak yığından ayrılması gerekmediği sürece bir sorun olmamalıdır.
RBarryYoung

Yanıtlar:


119

Yığın taştığında ve StackOverflowErroratıldığında, olağan özel durum işleme yığını çözer. Yığının çözülmesi şu anlama gelir:

  • şu anda aktif olan işlevin yürütülmesini iptal et
  • yığın çerçevesini silin, çağırma işlevine devam edin
  • arayanın yürütmesini iptal et
  • yığın çerçevesini silin, çağırma işlevine devam edin
  • ve bunun gibi...

... istisna yakalanana kadar. Bu normaldir (aslında gereklidir) ve hangi istisnanın neden atıldığından bağımsızdır. İlk çağrının dışındaki istisnayı yakaladığınız için foo(), fooyığını dolduran binlerce yığın çerçevesinin tümü çözüldü ve yığının çoğu tekrar kullanılmak için ücretsiz.


1
@fge Düzenleme yapmaktan çekinmeyin, bir paragraf sonunu düşündüm ama iyi göründüğü bir yer bulamadım.


1
Mesele şu ki, en içteki footanımsız durumla sonlandırıldı, bu nedenle dokunduğu herhangi bir nesnenin kırılmış olduğu varsayılmalıdır. Yığın taşmasının hangi işlevde gerçekleştiğini bilmediğiniz için, yalnızca onu tryyakalayan bloğun soyundan gelmesi gerektiğini , buradan erişilebilen herhangi bir yöntemle değiştirilebilecek herhangi bir nesne artık şüpheli. Genellikle ne olduğunu bulmaya ve düzeltmeye çalışmaya değmez.
Simon Richter

2
@delnan, bunun neden kötü bir fikir olduğunu da detaylandırmadan cevabın eksik olduğunu düşünüyorum. Açıkça atılan bir istisnanın farkı, Erroristisnai güvenli kod yazarken bile s'nin tahmin edilememesidir.
Simon Richter

1
@SimonRichter Hayır, soru oldukça spesifik. O var olmayan taşıma konusunda Errors. OP soruyor sadece yaklaşık StackOverflowErrorve işlenmesi belirli bir şey soruyor bu hata: Bir yöntem çağrısı nasıl değil bu hata yakalandığı zaman başarısız.
Bakuriu

23

StackOverflowError atıldığında, yığın dolu demektir. Ancak, yakalandığında , tüm bu fooçağrılar yığından çıkarıldı. baryığın artık foos ile dolmadığından normal şekilde çalışabilir . (JLS'nin böyle bir yığın taşmasından kurtulabileceğinizi garanti ettiğini düşünmediğimi unutmayın.)


12

StackOverFlow gerçekleştiğinde, JVM, yığını serbest bırakarak yakalama noktasına kadar açılır.

Örneğinizde, tüm istiflenmiş foo'lardan kurtuluyor.


8

Çünkü yığın aslında taşmaz. AttemptToOverflowStack daha iyi bir isim olabilir. Temel olarak anlamı, yığın çerçevesini ayarlamaya yönelik son girişimin, yığında yeterli boş alan kalmadığı için hatalı olmasıdır. Yığın aslında çok fazla alana sahip olabilir, ancak yeterli alan olmayabilir. Bu nedenle, hangi işlem başarılı çağrıya bağlı olursa olsun (tipik olarak bir yöntem çağrısı), asla beklenmez ve kalan tek şey programın bu olguyla başa çıkmasıdır. Bu, diğer istisnalardan gerçekten farklı olmadığı anlamına gelir. Aslında, çağrıyı yapan işlevde istisnayı yakalayabilirsiniz.


1
Sadece bunu yaparsanız, istisna işleyicinizin mevcut olandan daha fazla yığın alanı gerektirmemesine dikkat edin!
Vince

2

Daha önce cevaplandığı gibi , bir yakaladıktan sonra kod çalıştırmak ve özellikle fonksiyonları çağırmak mümkündür, StackOverflowErrorçünkü JVM'nin normal istisna işleme prosedürü, yığın alanını kullanmak için serbest bırakarak throw, catchnoktalar ve noktalar arasındaki yığını çözer . Ve deneyiniz durumun böyle olduğunu doğruluyor.

Ancak, bu kadar karşı, genel olarak, mümkün olduğu şeklindeki oldukça aynı değildir kurtarmak bir mesafede StackOverflowError.

BİR StackOverflowErrorIS-A VirtualMachineError, ki bu AN Error. Sizin de belirttiğiniz gibi, Java şu konularda bazı belirsiz tavsiyeler verir Error:

makul bir uygulamanın yakalamaya çalışmaması gereken ciddi sorunları belirtir

ve makul bir şekilde, bunun bazı durumlarda iyi olabileceği gibi görünmesi gerektiği sonucuna Errorvarıyorsunuz. Bir deney yapmanın, genellikle bir şeyin yapılmasının güvenli olduğunu göstermediğini unutmayın. Bunu yalnızca Java dilinin kuralları ve kullandığınız sınıfların özellikleri yapabilir. A VirtualMachineError, özel bir istisna sınıfıdır, çünkü Java Dil Spesifikasyonu ve Java Sanal Makine Spesifikasyonu bu istisnanın anlambilimiyle ilgili bilgi sağlar. Özellikle, ikincisi şöyle diyor :

Bir Java Sanal Makinesi uygulaması VirtualMethodError, bir iç hata veya kaynak sınırlaması, bu bölümde açıklanan semantiği uygulamasını engellediğinde , sınıfın bir alt sınıfının bir örneği olan bir nesneyi atar . Bu belirtim, dahili hataların veya kaynak sınırlamalarının nerede karşılaşılabileceğini tahmin edemez ve tam olarak ne zaman rapor edilebileceklerini belirlemez. Bu nedenle, VirtualMethodErroraşağıda tanımlanan alt sınıflardan herhangi biri, Java Sanal Makinesi'nin çalışması sırasında herhangi bir zamanda atılabilir:

...

  • StackOverflowError: Java Sanal Makinesi uygulamasında, bir iş parçacığı için yığın alanı tükendi, bunun nedeni tipik olarak iş parçacığının, çalıştırılan programdaki bir hatanın bir sonucu olarak sınırsız sayıda özyinelemeli çağrı yapmasıdır.

Can alıcı sorun, a'nın nereye veya ne zaman StackOverflowErroratılacağını "tahmin edememenizdir" . Nereye atılmayacağına dair hiçbir garanti yok . Örneğin, bir yönteme girişte atılacağına güvenemezsiniz . Bir yöntem içinde bir noktaya fırlatılabilir .

Bu öngörülemezlik potansiyel olarak felakettir. Bir yöntemin içine atılabileceği için, sınıfın tek bir "atomik" işlem olarak gördüğü bir işlem dizisi boyunca kısmen atılabilir ve nesneyi kısmen değiştirilmiş, tutarsız bir durumda bırakabilir. Nesne tutarsız bir durumdayken, o nesneyi kullanmaya yönelik herhangi bir girişim hatalı davranışa neden olabilir. Tüm pratik durumlarda, hangi nesnenin tutarsız durumda olduğunu bilemezsiniz , bu nedenle hiçbir nesnenin güvenilir olmadığını varsaymanız gerekir . Herhangi bir kurtarma işlemi veya istisna yakalandıktan sonra devam etme girişimi bu nedenle hatalı davranışlara neden olabilir. Yapılacak tek güvenli şey etmektir değil bir yakalamakStackOverflowErrorbunun yerine programın sonlandırılmasına izin vermek için. (Pratikte, sorun gidermeye yardımcı olmak için bazı hata günlüğü oluşturma girişiminde bulunabilirsiniz, ancak bu günlük kaydının düzgün çalışmasına güvenemezsiniz ). Yani, güvenilir bir şekilde birStackOverflowError .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.