StackTrace içermeyen Java'da NullPointerException


333

Bizim Java kodu yakalamak bir örnek vardı NullPointerException, ama StackTrace (ki temelde arama sona erer Throwable.printStackTrace()) oturum açmaya çalıştığınızda , tüm olsun:

java.lang.NullPointerException

Başkası buna rastladı mı? "Java null pointer boş yığın izleme" için googling denedim ama böyle bir şey rastlamak yoktu.


Bağlam nedir? Birden fazla iş parçacığı var mı? Bir SwingWorker'da bir istisnanın yığın izini almaya çalışırken sorunlar yaşadım.
Michael Myers

Burada iş parçacığı yok, sadece eski Java.
Edward Shtern

1
@Bozho - hayır - henüz NullPointer'ın nasıl çoğaltılacağından emin değilim.
Edward Shtern


-XX:-OmitStackTraceInFastThrow
Dup

Yanıtlar:


407

Muhtemelen çok fazla optimizasyon gerçekleştiren HotSpot JVM'yi (aslen Sun Microsystems tarafından, daha sonra Oracle tarafından OpenJDK'nın bir parçası olarak satın alınmıştır) kullanıyorsunuz. Yığın izlerini geri almak için, seçeneği -XX:-OmitStackTraceInFastThrowJVM'ye geçirmeniz gerekir.

Optimizasyon, bir istisna (genellikle bir NullPointerException) ilk kez oluştuğunda, tam yığın izinin yazdırılması ve JVM'nin yığın izini (veya belki de kodun konumunu) hatırlamasıdır. Bu istisna yeterince sık meydana geldiğinde, hem daha iyi performans elde etmek hem de günlüğü aynı yığın izleriyle doldurmamak için yığın izlemesi artık yazdırılmaz.

Bunun HotSpot JVM'de nasıl uygulandığını görmek için bir kopyasını alın ve global değişkeni arayın OmitStackTraceInFastThrow. En son koda baktığımda (2019'da), graphKit.cpp dosyasındaydı .


1
Bahşiş için teşekkürler. Bu seçeneği geçmek için herhangi bir gizli gotchas varsa herhangi bir fikir (benim uygulama bir ton istisna atmadığı sürece oldukça zararsız görünüyor)?
Edward Shtern

Bildiğim gizli bir yaka yok. Hotspot kaynak koduna baktığınızda, bu seçeneğin yalnızca bir yerde (graphKit.cpp) kullanıldığını görebilirsiniz. Ve bu bana iyi geliyor.
Roland Illig

34
Yığın izi optimize edildiğinde, en azından bir kez tamamen ele alındığı için ek bilgi ekleyeceğimi düşündüm: jawspeak.com/2010/05/26/…
sharakan

1
Bir OpenJDK JVM, sürüm 1.8.0u171 (Debian 9) çalıştırıyorum ve -XX:-OmitStackTraceInFastThrowbayrağı da kabul ediyor gibi görünüyor . Ben de yığın izleri (örneğin, kullanarak e.printStackTrace) yazdırmak için başarısız oldu bu yüzden henüz onaylamak için değil, ama büyük olasılıkla görünüyor. Cevabı bu keşfi yansıtacak şekilde genişlettim.
Chris

Bizim durumumuzda ilk 125 istisna bir yığın izlemesine sahipti ve daha sonra günlük dosyaların 3 rotasyonu boyunca kalanlar hiçbiri yoktu. Bu cevap suçluyu bulmakta çok yardımcı oldu.
sukhmel

61

Bir yorumda belirttiğiniz gibi log4j kullanıyorsunuz. (Yanlışlıkla) yazdığım bir yer keşfettim

LOG.error(exc);

tipik yerine

LOG.error("Some informative message", e);

tembellik yoluyla ya da sadece düşünmeyerek. Bunun talihsiz kısmı, beklediğiniz gibi davranmamasıdır. Logger API aslında Object'i bir dize olarak değil, ilk argüman olarak alır - ve sonra argümanda toString () öğesini çağırır. Böylece güzel hoş yığın izini almak yerine, sadece toString'i yazdırır - NPE durumunda oldukça işe yaramaz.

Belki de bunu deneyimliyorsunuz?


+1: Bu tarif edilen davranışı açıklar ve bunu keşfeden tek kişi sen değilsin :)
Peter Lang

4
Aslında yukarıdaki ilk formu asla kullanmamaya ilişkin standart bir politikamız var (LOG.error (exc);) - her zaman 2 parametre imzasını kullanıyoruz, böylece günlüklere yalnızca ham bir yığın izlemesi yerine bazı açıklayıcı ifadeler ekliyoruz.
Edward Shtern

5
Elbette, ancak politika her zaman doğru bir şekilde yürütüldüğü anlamına gelmez! En azından bahsetmeye değer olduğunu düşündüm.
Steven Schlansker

Doğru, ama bu durumda ;-)
Edward Shtern

28

Aynı davranışı geçmişte gördük. Bazı çılgın nedenlerden ötürü, kodda aynı yerde birden çok kez bir NullPointerException oluşması durumunda, bir süre sonra Log.error(String, Throwable)tam yığın izleri de dahil olmak üzere duracağı ortaya çıktı.

Günlüğünüzde daha geriye bakmayı deneyin. Suçluyu bulabilirsiniz.

EDIT: Bu hata ilgili geliyor, ancak çok uzun zaman önce düzeltildi muhtemelen nedeni değil.


2
Hata kapalı, ancak performans optimizasyonunu geçici olarak çözmek için -XX: -OmitStackTraceInFastThrow bayrağı hala gerekli.
Joshua Goldberg

Bunu son zamanlarda çok görüyorum. Buna neyin sebep olabileceği veya nasıl düzeltileceği hakkında herhangi bir ipucu? Kayıt sistemi günlerce çalışıyor olabilir ve asıl neden, sıkıcı aramaya
nevindind

5
Pawel, -XX:-OmitStackTraceInFastThrowJoshua'nın önerdiği JVM bayrağını denedin mi? Ayrıca bkz . Stackoverflow.com/a/2070568/6198 .
Matt Solnit

1
Bu bizim içindi. Teşekkürler.
Andrew Cheong

20

İşte bir açıklama: Hotspot, üretimde yığın izlerini kaybetmek için istisnalara neden oldu - ve düzeltme

Mac OS X'te test ettim

  • Java sürümü "1.6.0_26"
  • Java (TM) SE Çalışma Zamanı Ortamı (derleme 1.6.0_26-b03-383-11A511)
  • Java HotSpot (TM) 64 Bit Sunucu VM (yapı 20.1-b02-383, karışık mod)

    Object string = "abcd";
    int i = 0;
    while (i < 12289) {
        i++;
        try {
            Integer a = (Integer) string;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Bu özel kod parçası için, 12288 yinelemeleri (+ frekans?), JVM'nin önceden belirlenmiş istisna kullanmaya karar verdiği sınır gibi görünüyor ...


10

exception.toString size StackTrace vermez, sadece geri döner

bu atılabilir kısa bir açıklama. Sonuç şunun birleşimidir:

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

exception.printStackTraceBunun yerine StackTrace çıktısını almak için kullanın .


Üzgünüm, orijinal yazımda yanlış yazılmış. Bunları printStackTrace () kullanan Log4J aracılığıyla günlüğe kaydediyorum.
Edward Shtern

1
getStackTrace()Sorunun günlükçünüzde olmadığından emin olmak için kullanmayı denediniz mi?
Peter Lang

1
Log4j kullanıyorsanız, istisnayı argümanın bir parçası olarak log yöntemine gönderdiğinizden emin olun. Bununla bir cevap göndereceğim.
Ravi Wallau

@raviaw geçerli nokta! @Edward Shtern: log4j yönteminin 2-arg biçimini kullandığınızı doğrulayabilir misiniz? Bir cevapta, bunun şirket politikasının bundan bahsettiğini biliyorum, ancak bu durumda politikayı izlediğinizden kesinlikle emin misiniz?
KarstenF

Uzun bir atış olabilir, ancak istisnanın bazı 3. taraf kodlarından kaynaklanması mümkün mü? Belki de (zayıf yazılmış) bir istisna sarıcıdır, toString () basitçe sarılmış istisnanın sınıf adını döndürür ve temel yığın izini sağlamada başarısız olur. Catchger bloğunuza logger.info ("Exception class =" + exc.class.getCanonicalName ()) gibi bir şey koymayı deneyin ve ne elde ettiğinizi görün.
KarstenF

4

Alternatif öneri - Eclipse kullanıyorsanız, NullPointerException'ın kendisinde bir kesme noktası ayarlayabilirsiniz (Hata Ayıklama perspektifinde, "Kesme Noktaları" sekmesine gidin ve içinde! Bulunan küçük simgeye tıklayın)

Hem "yakalandı" hem de "yakalanmadı" seçeneklerini kontrol edin - şimdi NPE'yi tetiklediğinizde hemen kırılır ve daha sonra tam olarak nasıl ele alındığını ve neden yığın izini almadığınızı görebilirsiniz.


1

toString()yalnızca istisna adını ve isteğe bağlı mesajı döndürür. Aramayı öneririm

exception.printStackTrace()

mesajı dökmek için veya kanlı ayrıntılara ihtiyacınız varsa:

 StackTraceElement[] trace = exception.getStackTrace()

Yukarıya bakınız - yanlış yazıyorum - printStackTrace () kullanıyorum.
Edward Shtern

1

(Kodunuzun çağrılıp çağrılmadığı printStackTrace()veya bunun bir günlük işleyici tarafından yapılıp yapılmadığı konusunda sorunuz hala net değil .)

İşte neler olabileceğine dair bazı olası açıklamalar:

  • Kullanılan günlükçü / işleyici, tam bir yığın izlemesi değil, yalnızca kural dışı durumun ileti dizesini çıkaracak şekilde yapılandırılmıştır.

  • Uygulamanız (veya bazı üçüncü taraf kitaplıkları), LOG.error(ex);(örneğin) log4j Logger yönteminin 2 bağımsız değişken biçimini kullanarak istisnayı günlüğe kaydediyor.

  • Mesaj, düşündüğünüz yerden farklı bir yerden geliyor; örneğin, bazı üçüncü taraf kütüphane yöntemlerine veya daha önceki hata ayıklama girişimlerinden kalan bazı rastgele şeylere geliyor.

  • Günlüğe kaydedilen özel durum, yığın izini gizlemek için bazı yöntemleri aşırı yükledi. Bu durumda, istisna gerçek bir NullPointerException olmaz, ancak bazı özel NPE alt türleri veya hatta bazı bağlantısız istisnalar olacaktır.

Son olası açıklamanın pek olası olmadığını düşünüyorum, ancak insanlar en azından tersine mühendisliği "önlemek" için bu tür bir şey yapmayı düşünüyorlar. Tabii ki sadece gerçekten dürüst geliştiriciler için hayatı zorlaştırmayı başarıyor.


1

Projenizde AspectJ kullandığınızda, bazı yönler yığın izinin bölümünü gizliyor olabilir. Örneğin, bugün:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

Bu yığın izlemesi Maven'in emin ateşi ile test çalıştırılırken yazdırıldı.

Öte yandan, testi IntelliJ'de çalıştırırken farklı bir yığın izi yazdırıldı:

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)

0

Bu, İstisna çıktısını alır, yalnızca istisnaları daha iyi ele almanız için hata ayıklamak için kullanın.

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }
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.