Java Bellek Sızıntısı nasıl bulunur


142

Java'da bellek sızıntısını nasıl buluyorsunuz (örneğin, JHat kullanarak)? Ben temel bir görünüm almak için JHat yığın dökümü yüklemeye çalıştık. Ancak, kök referansı ( ref ) veya ne denirse onu nasıl bulabileceğimi anlamıyorum . Temel olarak, birkaç yüz megabayt karma tablo girişi olduğunu söyleyebilirim ([java.util.HashMap $ Giriş ya da bunun gibi bir şey), ancak haritalar her yerde kullanılıyor ... Büyük haritaları aramanın bir yolu var mı? ya da belki büyük nesne ağaçlarının genel köklerini bulmak?

[Düzenle] Tamam, şimdiye kadar cevapları okudum ama ben ucuz bir piç olduğumu söyleyeyim (yani JHat kullanmayı JProfiler için ödemek yerine nasıl kullanacağımı öğrenmekle daha çok ilgiliyim). Ayrıca, JDK'nın bir parçası olduğu için JHat her zaman kullanılabilir. Tabii ki JHat ile bir yolu yok ama kaba kuvvet yok, ama bunun böyle olabileceğine inanamıyorum.

Ayrıca, ( tüm harita boyutlarının günlüğünü ekleyerek) gerçekten değiştirebileceğimi ve sızıntıyı fark etmem için yeterince uzun süre çalıştırabileceğimi sanmıyorum.


Bu JProfiler için bir başka "oy". Yığın analizi için oldukça iyi çalışıyor, iyi bir kullanıcı arayüzüne sahip ve oldukça iyi çalışıyor. McKenzieG1'in dediği gibi, 500 $ aksi takdirde bu sızıntıların kaynağını ararken yakacağınız süreden daha ucuzdur. Aletlerin fiyatına göre, kötü değil.
joev

Oracle'ın burada alakalı bir sayfası var: docs.oracle.com/javase/8/docs/technotes/guides/troraseot/…
Laurel

Yanıtlar:


126

Java'da bellek sızıntılarını bulmak için aşağıdaki yaklaşımı kullanıyorum. Ben jProfiler büyük bir başarı ile kullandım, ama grafik yetenekleri (diffs grafik formda analiz etmek daha kolay) ile herhangi bir özel araç çalışacağına inanıyorum.

  1. Uygulamayı başlatın ve tüm başlatma tamamlandığında ve uygulama boşta kaldığında "kararlı" duruma gelene kadar bekleyin.
  2. Önbellek, DB ile ilgili başlatma işleminin gerçekleşmesine izin vermek için bellek sızıntısı olduğundan şüphelenilen işlemi birkaç kez çalıştırın.
  3. GC'yi çalıştırın ve bellek anlık görüntüsünü alın.
  4. İşlemi tekrarlayın. İşlemin karmaşıklığına ve işlenen verilerin boyutlarına bağlı olarak işlemin birkaç ila birçok kez çalıştırılması gerekebilir.
  5. GC'yi çalıştırın ve bellek anlık görüntüsünü alın.
  6. 2 anlık görüntü için fark çalıştırın ve analiz edin.

Temel olarak analiz, örneğin nesne türlerine göre en büyük pozitif farktan başlamalı ve bu ekstra nesnelerin belleğe yapışmasına neyin neden olduğunu bulmalıdır.

Birkaç iş parçacığı analizinde istekleri işleyen web uygulamaları için daha karmaşık hale gelir, ancak yine de genel yaklaşım geçerlidir.

Özellikle uygulamaların bellek ayak izini azaltmayı amaçlayan birçok proje yaptım ve uygulamaya özgü bazı ince ayarlar ve hile ile bu genel yaklaşım her zaman iyi çalıştı.


7
Java profillerinin çoğu (hepsi değilse de) size bir düğmeyi tıklatarak GC'yi çağırmak için bir seçenek sunar. Veya System.gc () kodunuzdaki uygun yerden çağırabilirsiniz.
Dima Malenko

3
System.gc () çağrsak bile JVM çağrıyı ihmal etmeyi seçebilir. AFAIK bu JVM'ye özgüdür. Cevaba + 1'leyin.
Aniket Thakur

4
"Bellek anlık görüntüsü" tam olarak nedir? Kodumun çalıştığı her nesne türünün numarasını söyleyecek bir şey var mı?
16'da

2
"Nesne türlerine göre en büyük pozitif farktan başla" dan "bu ekstra nesnelerin belleğe yapışmasına neden olan şeyi bulmaya" nasıl giderim? İnt [], Object [], String vb. Gibi çok genel şeyler görüyorum. Nereden geldiklerini nasıl bulabilirim?
Vituel

48

Burada soru soran, herhangi bir tıklamayı cevaplamak için 5 dakika sürmeyen bir araç almanın potansiyel hafıza sızıntılarını bulmayı çok daha kolay hale getirdiğini söylemeliyim.

İnsanlar birkaç araç önerdiğinden (JDK ve JProbe denemesinde sadece görsel wm denedim) Eclipse platformu, Memory Analyzer (bazen SAP belleği olarak da adlandırılır) analizörü) http://www.eclipse.org/mat/ adresinde bulunabilir .

Bu araç hakkında gerçekten harika olan şey, ilk açtığımda yığın dökümünü dizine eklemesi, bu da her nesne için 5 dakika beklemeden tutulan yığın gibi verileri göstermesine izin verdi (hemen hemen tüm işlemler denediğim diğer araçlardan ton daha hızlıydı) .

Dökümü açtığınızda, ilk ekran size en büyük nesneleri içeren bir pasta grafiği gösterir (tutulan yığını sayar) ve biri rahatlık için büyük olan nesnelere hızlı bir şekilde gidebilir. Ayrıca, reccon'un kullanışlı olabileceği muhtemel kaçak şüphelileri bul da var, ancak navigasyon benim için yeterli olduğundan gerçekten içine girmedim.


1
Dikkat çekmeye değer: görünüşe göre Java 5 ve üstü, HeapDumpOnCtrlBreakVM parametresi mevcut değildir . Bulduğum çözüm (şimdiye kadar hala bakıyorum), .hprofdosyayı boşaltmak için JMap kullanmak , daha sonra Eclipse'e koymak ve incelemek için MAT kullanmak.
Ben

1
Yığın dökümü edinme ile ilgili olarak, çoğu profiler (JVisualVM dahil) hem yığının hem de iş parçacıklarının bir dosyaya dökülme seçeneği içerir.
bbaja42

13

Bir araç büyük bir yardımdır.

Ancak, bir aracı kullanamayacağınız zamanlar vardır: yığın dökümü, aracı çöktüğü kadar büyüktür, yalnızca kabuk erişimine sahip olduğunuz bazı üretim ortamlarında bir makinede sorun gidermeye çalışıyorsunuz.

Bu durumda, hprof dökümü dosyası etrafında yolunuzu bilmek yardımcı olur.

SITES BAŞLADI. Bu, hangi nesnelerin en fazla belleği kullandığını gösterir. Ancak nesneler yalnızca türe göre toplanmaz: her girişte bir "izleme" kimliği de bulunur. Daha sonra, nesnenin tahsis edildiği yığının ilk birkaç karesini görmek için bu "TRACE nnnn" öğesini arayabilirsiniz. Genellikle, nesnenin nereye tahsis edildiğini gördüğümde, bir hata bulurum ve işim biter. Ayrıca, -Xrunhprof seçenekleriyle yığına kaç kare kaydedileceğini de kontrol edebileceğinizi unutmayın.

Ayırma sitesini kontrol ederseniz ve yanlış bir şey görmüyorsanız, beklenmeyen referans zincirini bulmak için bu canlı nesnelerden bazılarını kök nesnelere geriye doğru zincirleme başlatmanız gerekir. Bu, bir aracın gerçekten yardımcı olduğu yerdir, ancak aynı şeyi elle de yapabilirsiniz (grep ile). Sadece bir kök nesne yoktur (yani, çöp toplama işlemine tabi olmayan nesne). Konular, sınıflar ve yığın kareleri kök nesneler olarak işlev görür ve güçlü bir şekilde başvurdukları herhangi bir şey toplanmaz.

Zincirleme yapmak için, kötü izleme kimliğine sahip girişler için HEAP DUMP bölümüne bakın. Bu sizi onaltılık olarak benzersiz bir nesne tanımlayıcısı gösteren bir OBJ veya ARR girişine götürür. Kimin nesneye güçlü bir referansı olduğunu bulmak için o kimliğin tüm tekrarlarını arayın. Sızıntıların nerede olduğunu bulana kadar dallandıkça bu yolların her birini geriye doğru izleyin. Bir aracın neden bu kadar kullanışlı olduğunu görüyor musunuz?

Statik üyeler bellek sızıntıları için tekrarlayan bir suçludur. Aslında, bir araç olmadan bile, statik Harita üyeleri için kodunuzu inceleyerek birkaç dakika harcamaya değer. Bir harita büyüyebilir mi? Girişlerini temizleyen bir şey var mı?


“Yığın dökümü o kadar büyük ki aracı çökertiyor” —sonu kontrol ettim jhatve MATgörünüşe göre tüm yığın dökümü belleğe yüklemeye çalışıyorum ve bu yüzden tipik OutOfMemoryErrorolarak büyük dökümlerde (yani, yığın analizine en çok ihtiyaç duyulan uygulamalardan! ). NetBeans Profiler referansları indekslemek için büyük dökümlerde yavaşlayabilen farklı bir algoritma kullanıyor gibi görünüyor, ancak en azından araçta sınırsız bellek tüketmiyor ve çöküyor.
Jesse Glick

10

Çoğu zaman, kurumsal uygulamalarda verilen Java yığını maksimum 12 ila 16 GB'lik ideal boyuttan daha büyüktür. NetBeans profillerinin doğrudan bu büyük java uygulamalarında çalışmasını zor buldum.

Ancak genellikle buna gerek yoktur. "Canlı" yığın dökümü almak için jdk ile gelen jmap yardımcı programını kullanabilirsiniz, yani jmap GC çalıştırdıktan sonra yığını dökecek. Uygulamada biraz işlem yapın, işlem tamamlanana kadar bekleyin, ardından bir başka "canlı" yığın dökümü alın. Yığın dökümlerini yüklemek, histogram üzerinde sıralamak, hangi nesnelerin arttığını veya hangisinin en yüksek olduğunu görmek için Eclipse MAT gibi araçları kullanın, Bu bir ipucu verecektir.

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

Bu yaklaşımla ilgili tek bir sorun var; Devasa yığınlar, canlı seçenekle bile, geliştirme turuna aktarılamayacak kadar büyük olabilir ve açmak için yeterli belleğe / RAM'e sahip bir makineye ihtiyaç duyabilir.

Sınıf histogramı bu noktada devreye girer. Canlı sınıf histogramını jmap aracıyla dökebilirsiniz. Bu sadece bellek kullanımının sınıf histogramını verecektir. Örneğin char dizisini en üste koyabilir. Ve aşağıda bir yerde String sınıfı. Bağlantıyı kendiniz çizmelisiniz.

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

İki yığın dökümü almak yerine, yukarıda açıklandığı gibi iki sınıf histogram alın; Sonra sınıf histogramlarını karşılaştırın ve artan sınıfları görün. Java sınıflarını uygulama sınıflarınızla ilişkilendirip ilişkilendiremeyeceğinize bakın. Bu oldukça iyi bir ipucu verecektir. İki jmap histogram dökümünü karşılaştırmanıza yardımcı olabilecek bir piton komut dosyası. histogramparser.py

Son olarak JConolse ve VisualVm gibi araçlar zaman içinde bellek büyümesini görmek ve bellek sızıntısı olup olmadığını görmek için gereklidir. Sonunda bazen probleminiz bir bellek sızıntısı değil, yüksek bellek kullanımı olabilir. Bunun için GC günlük kaydını etkinleştirin; G1GC gibi daha gelişmiş ve yeni bir sıkıştırma GC kullanın; GC davranışını canlı olarak görmek için jstat gibi jdk araçlarını kullanabilirsiniz

jstat -gccause pid <optional time interval>

-Jhat, jmap, Full GC, Humongous ayırma, G1GC için google'a diğer referanslar


1
burada daha fazla ayrıntı içeren bir blog yazısı ekledi - alexpunnen.blogspot.in/2015/06/…
Alex Punnen

5

Sızıntılarınızı bulmanıza yardımcı olacak araçlar var, örneğin JProbe, YourKit, AD4J veya JRockit Görev Kontrolü. Sonuncusu şahsen en iyi bildiğim. Herhangi bir iyi araç, hangi sızıntıları ve sızan nesnelerin nereye tahsis edildiğini kolayca belirleyebileceğiniz bir seviyeye inmenize izin vermelidir.

HashTable'ları, Hashmap'leri veya benzerini kullanmak, Java'da bellekte sızıntı yapmanın birkaç yolundan biridir. Sızıntıyı el ile bulmak zorunda kalsaydım, HashMaps'imin boyutunu kalıcı olarak yazdırırdım ve oradan öğeler eklediğim ve onları silmeyi unuttuğum birini bulurum.


4

Haritalarınızı değiştirdiğinizde, boyutlarınızın günlük kaydını eklemenin, ardından haritaların makul bir boyutun ötesinde büyüdüğü günlükleri aramanın düşük teknoloji çözümü her zaman vardır.



0

Tahsisleri izleyen bir bellek profili kullanmanız gerekir. JProfiler'e bir göz atın - "yığın yürüteç" özelliği harika ve tüm büyük Java IDE'leriyle entegrasyona sahipler. Ücretsiz değil, ama o kadar da pahalı değil (tek bir lisans için 499 $) - daha az karmaşık araçlarla bir sızıntı bulmak için çabucak çabucak 500 dolar değerinde zaman harcayacaksınız.


0

Çöp toplayıcıyı birkaç kez çağırdıktan sonra bellek kullanım boyutunu ölçerek öğrenebilirsiniz:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

Çıktı sayıları eşitse, uygulamanızda bellek sızıntısı olmaz, ancak bellek kullanım sayıları (artan sayılar) arasında fark görürseniz, projenizde bellek sızıntısı olur. Örneğin:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

Bazen akışlar ve soketler gibi bazı eylemlerle belleği serbest bırakmanın biraz zaman alabileceğini unutmayın . İlk çıktılarla yargılamamalısınız, belirli bir sürede test etmelisiniz.


0

JProfiler ile bellek sızıntılarını bulmak için bu ekrandan çıkış yapın . @Dima Malenko Cevap görsel açıklaması.

Not: JProfiler ücretsiz olmasa da, Deneme sürümü mevcut durumla başa çıkabilir.


0

Çoğumuz Eclipse'i zaten kod yazmak için kullandığımız için, neden Eclipse'de Bellek Analiz Aracı'nı (MAT) kullanmıyorsunuz? Harika çalışıyor.

Eclipse MAT analiz eden araçlar sağlar Eclipse IDE için eklentiler bir dizi heap dumpsJava uygulaması ve tanımlamak için memory problemsbaşvuruda.

Bu, geliştiricinin aşağıdaki özelliklerle bellek sızıntılarını bulmasına yardımcı olur

  1. Bellek anlık görüntüsü alma (Yığın Dökümü)
  2. Histogram
  3. Tutulan Öbek
  4. Dominator Ağacı
  5. GC Köklerine Giden Yolları Keşfetme
  6. Müfettiş
  7. Ortak Bellek Desenleri
  8. Nesne Sorgu Dili

resim açıklamasını buraya girin

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.