Java Bellek Sızıntılarını Avlamak

Yayınlanan: 2022-03-11

Deneyimsiz programcılar genellikle Java'nın otomatik çöp toplama özelliğinin onları bellek yönetimi konusunda endişelenmekten tamamen kurtardığını düşünür. Bu yaygın bir yanlış algılamadır: çöp toplayıcı elinden gelenin en iyisini yaparken, en iyi programcının bile sakatlayıcı bellek sızıntılarına yakalanması tamamen mümkündür. Açıklamama izin ver.

Artık gerekmeyen nesne başvuruları gereksiz yere korunduğunda bir bellek sızıntısı oluşur. Bu sızıntılar kötü. Birincisi, programlarınız giderek daha fazla kaynak tükettiği için makineniz üzerinde gereksiz bir baskı oluştururlar. İşleri daha da kötüleştirmek için, bu sızıntıları tespit etmek zor olabilir: statik analiz genellikle bu gereksiz referansları tam olarak belirlemekte zorlanır ve mevcut sızıntı tespit araçları, tek tek nesneler hakkında ayrıntılı bilgileri izleyip raporlayarak, yorumlanması zor ve kesinliği olmayan sonuçlar üretir.

Başka bir deyişle, sızıntıları tespit etmek ya çok zordur ya da yararlı olamayacak kadar spesifik terimlerle tanımlanır.

Aslında benzer ve örtüşen belirtilere sahip ancak çeşitli nedenleri ve çözümleri olan dört bellek sorunu kategorisi vardır:

  • Performans : genellikle aşırı nesne oluşturma ve silme, çöp toplamada uzun gecikmeler, aşırı işletim sistemi sayfası değiştirme ve daha fazlasıyla ilişkilidir.

  • Kaynak kısıtlamaları : çok az kullanılabilir bellek olduğunda veya belleğiniz büyük bir nesneyi ayırmak için çok parçalanmış olduğunda oluşur - bu yerel olabilir veya daha yaygın olarak Java yığınıyla ilgili olabilir.

  • Java yığın sızıntıları : Java nesnelerinin serbest bırakılmadan sürekli olarak oluşturulduğu klasik bellek sızıntısı. Bu genellikle gizli nesne referanslarından kaynaklanır.

  • Yerel bellek sızıntıları : JNI kodu, sürücüler ve hatta JVM ayırmaları tarafından yapılan ayırmalar gibi Java yığınının dışında kalan sürekli büyüyen bellek kullanımıyla ilişkilidir.

Bu bellek yönetimi eğitiminde, Java yığın sızıntılarına odaklanacağım ve Java VisualVM raporlarına dayalı olarak bu tür sızıntıları algılamak için bir yaklaşımın ana hatlarını çizeceğim ve Java teknolojisi tabanlı uygulamaları çalışırken analiz etmek için görsel bir arayüz kullanacağım.

Ancak bellek sızıntılarını önleyip bulmadan önce, bunların nasıl ve neden meydana geldiğini anlamalısınız. ( Not: Bellek sızıntılarının inceliklerini iyi biliyorsanız, atlayabilirsiniz. )

Bellek Sızıntıları: Bir Astar

Yeni başlayanlar için, bellek sızıntısını bir hastalık ve Java'nın OutOfMemoryError (kısalık için OOM) bir semptom olarak düşünün. Ancak herhangi bir hastalıkta olduğu gibi, tüm OOM'lar mutlaka bellek sızıntıları anlamına gelmez : çok sayıda yerel değişken veya benzeri diğer olayların oluşturulması nedeniyle bir OOM oluşabilir. Öte yandan, tüm bellek sızıntıları, özellikle masaüstü uygulamaları veya istemci uygulamaları (yeniden başlatma olmadan çok uzun süre çalıştırılmayan) söz konusu olduğunda, OOM'lar olarak kendini göstermez .

Bellek sızıntısını bir hastalık ve OutOfMemoryError'ı bir semptom olarak düşünün. Ancak tüm OutOfMemoryErrors, bellek sızıntıları anlamına gelmez ve tüm bellek sızıntıları kendilerini OutOfMemoryErrors olarak göstermez.

Bu sızıntılar neden bu kadar kötü? Diğer şeylerin yanı sıra, program yürütme sırasında bellek bloklarını sızdırmak, sistemdeki boş fiziksel bellek bittiğinde, tahsis edilen ancak kullanılmayan bellek bloklarının değiştirilmesi gerekeceğinden, zaman içinde sistem performansını düşürür. Sonunda, bir program mevcut sanal adres alanını tüketerek OOM'a yol açabilir.

OutOfMemoryError

Yukarıda bahsedildiği gibi, OOM, bir bellek sızıntısının yaygın bir göstergesidir. Esasen, yeni bir nesne tahsis etmek için yeterli alan olmadığında hata atılır. Olabildiğince deneyin, çöp toplayıcı gerekli alanı bulamıyor ve yığın daha fazla genişletilemez. Böylece yığın izleme ile birlikte bir hata ortaya çıkar.

OOM'nizi teşhis etmenin ilk adımı, hatanın gerçekte ne anlama geldiğini belirlemektir. Bu kulağa açık geliyor, ancak cevap her zaman çok net değil. Örneğin: OOM, Java yığını dolu olduğu için mi yoksa yerel yığın dolu olduğu için mi görünüyor? Bu soruyu yanıtlamanıza yardımcı olmak için olası hata mesajlarından birkaçını analiz edelim:

  • java.lang.OutOfMemoryError: Java heap space

  • java.lang.OutOfMemoryError: PermGen space

  • java.lang.OutOfMemoryError: Requested array size exceeds VM limit

  • java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space?

  • java.lang.OutOfMemoryError: <reason> <stack trace> (Native method)

"Java yığın alanı"

Bu hata mesajı mutlaka bir bellek sızıntısı anlamına gelmez. Aslında, sorun bir yapılandırma sorunu kadar basit olabilir.

Örneğin, sürekli olarak bu tür OutOfMemoryError üreten bir uygulamayı analiz etmekten sorumluydum. Biraz araştırmadan sonra, suçlunun çok fazla bellek gerektiren bir dizi başlatma olduğunu anladım; bu durumda, uygulamanın hatası değil, uygulama sunucusu çok küçük olan varsayılan yığın boyutuna güveniyordu. JVM'nin bellek parametrelerini ayarlayarak sorunu çözdüm.

Diğer durumlarda ve özellikle uzun ömürlü uygulamalar için mesaj, istemeden nesnelere referanslar tuttuğumuzun ve çöp toplayıcının bunları temizlemesini engellediğimizin bir göstergesi olabilir. Bu, bir bellek sızıntısının Java dilindeki karşılığıdır . ( Not: Bir uygulama tarafından çağrılan API'ler, istemeden nesne referanslarını da tutuyor olabilir. )

Bu "Java yığın alanı" OOM'lerinin bir başka potansiyel kaynağı, sonlandırıcıların kullanımıyla ortaya çıkar. Bir sınıfın bir finalize yöntemi varsa, bu tür nesnelerin alanı çöp toplama zamanında geri kazanılmaz. Bunun yerine, çöp toplama işleminden sonra nesneler daha sonra gerçekleşecek olan sonlandırma için kuyruğa alınır. Sun uygulamasında, sonlandırıcılar bir arka plan programı iş parçacığı tarafından yürütülür. Sonlandırıcı iş parçacığı, sonlandırma kuyruğuna yetişemezse, Java yığını dolabilir ve bir OOM atılabilir.

“PermGen alanı”

Bu hata mesajı, kalıcı neslin dolu olduğunu gösterir. Kalıcı nesil, sınıf ve yöntem nesnelerini depolayan yığının alanıdır. Bir uygulama çok sayıda sınıf yüklerse, kalıcı neslin boyutunun -XX:MaxPermSize seçeneği kullanılarak artırılması gerekebilir.

Dahili java.lang.String nesneleri de kalıcı nesilde saklanır. java.lang.String sınıfı, bir diziler havuzunu korur. Intern yöntemi çağrıldığında, yöntem, eşdeğer bir dize olup olmadığını görmek için havuzu kontrol eder. Eğer öyleyse, intern yöntemiyle döndürülür; değilse, dize havuza eklenir. Daha kesin bir ifadeyle, java.lang.String.intern yöntemi, bir dizenin kurallı gösterimini döndürür; sonuç, bu dize değişmez olarak göründüğünde döndürülecek olan aynı sınıf örneğine yapılan bir başvurudur. Bir uygulama çok sayıda dizeyi stajyer olarak alıyorsa, kalıcı neslin boyutunu artırmanız gerekebilir.

Not: jmap -permgen komutunu, dahilileştirilmiş String örnekleri hakkında bilgiler de dahil olmak üzere kalıcı nesille ilgili istatistikleri yazdırmak için kullanabilirsiniz.

"İstenen dizi boyutu VM sınırını aşıyor"

Bu hata, uygulamanın (veya o uygulama tarafından kullanılan API'lerin) yığın boyutundan daha büyük bir dizi ayırmaya çalıştığını gösterir. Örneğin, bir uygulama 512 MB'lık bir dizi ayırmaya çalışırsa ancak maksimum yığın boyutu 256 MB ise, bu hata mesajıyla bir OOM atılır. Çoğu durumda, sorun ya bir yapılandırma sorunudur ya da bir uygulama çok büyük bir dizi ayırmaya çalıştığında ortaya çıkan bir hatadır.

“<sebep> için <boyut> bayt isteyin. Takas alanı bitti mi?”

Bu mesaj bir OOM gibi görünüyor. Ancak, yerel yığından bir ayırma başarısız olduğunda ve yerel yığın tükenmeye yakın olduğunda, HotSpot VM bu belirgin istisnayı atar. Mesajda, başarısız olan isteğin boyutu (bayt cinsinden) ve bellek isteğinin nedeni bulunur. Çoğu durumda, <sebep> bir ayırma hatası bildiren kaynak modülün adıdır.

Bu tür bir OOM atılırsa, sorunu daha fazla tanılamak için işletim sisteminizdeki sorun giderme yardımcı programlarını kullanmanız gerekebilir. Bazı durumlarda sorun uygulamayla ilgili bile olmayabilir. Örneğin, şu durumlarda bu hatayı görebilirsiniz:

  • İşletim sistemi yetersiz takas alanıyla yapılandırılmış.

  • Sistemdeki başka bir işlem, mevcut tüm bellek kaynaklarını tüketiyor.

Uygulamanın yerel bir sızıntı nedeniyle başarısız olması da mümkündür (örneğin, bir miktar uygulama veya kitaplık kodu sürekli olarak bellek ayırıyor ancak bunu işletim sistemine bırakamıyorsa).

<sebep> <yığın izleme> (Yerel yöntem)

Bu hata mesajını görüyorsanız ve yığın izlemenizin üst çerçevesi yerel bir yöntemse, bu yerel yöntem bir ayırma hatasıyla karşılaşmıştır. Bu ileti ile önceki ileti arasındaki fark, Java bellek ayırma hatasının Java VM kodu yerine bir JNI veya yerel yöntemde algılanmasıdır.

Bu tür bir OOM atılırsa, sorunu daha fazla teşhis etmek için işletim sistemindeki yardımcı programları kullanmanız gerekebilir.

OOM Olmadan Uygulama Kilitlenmesi

Bazen, yerel yığından bir ayırma hatasından hemen sonra bir uygulama çökebilir. Bu, bellek ayırma işlevleri tarafından döndürülen hataları kontrol etmeyen yerel kod çalıştırıyorsanız oluşur.

Örneğin, kullanılabilir bellek yoksa malloc sistem çağrısı NULL döndürür. malloc dönüş kontrol edilmezse, uygulama geçersiz bir bellek konumuna erişmeye çalıştığında çökebilir. Koşullara bağlı olarak, bu tür bir sorunun bulunması zor olabilir.

Bazı durumlarda, önemli hata günlüğünden veya kilitlenme dökümünden alınan bilgiler yeterli olacaktır. Bir çökmenin nedeninin bazı bellek ayırmalarında hata işleme eksikliği olduğu belirlenirse, söz konusu ayırma hatasının nedenini bulmanız gerekir. Diğer yerel yığın sorunlarında olduğu gibi, sistem yetersiz takas alanıyla yapılandırılmış olabilir, başka bir işlem tüm kullanılabilir bellek kaynaklarını tüketiyor olabilir, vb.

Sızıntı Teşhisi

Çoğu durumda, bellek sızıntılarını teşhis etmek, söz konusu uygulama hakkında çok ayrıntılı bilgi gerektirir. Uyarı: süreç uzun ve yinelemeli olabilir.

Bellek sızıntılarını yakalama stratejimiz nispeten basit olacaktır:

  1. Belirtileri tanımlayın

  2. Ayrıntılı çöp toplamayı etkinleştir

  3. Profil oluşturmayı etkinleştir

  4. İzi analiz edin

1. Belirtileri Tanımlayın

Tartışıldığı gibi, çoğu durumda Java işlemi, bellek kaynaklarınızın tükendiğinin açık bir göstergesi olan bir OOM çalışma zamanı istisnası oluşturur. Bu durumda, normal bir bellek tükenmesi ile bir sızıntı arasında ayrım yapmanız gerekir. OOM'un mesajını analiz edin ve yukarıda verilen tartışmalara dayanarak suçluyu bulmaya çalışın.

Çoğu zaman, bir Java uygulaması çalışma zamanı yığınının sunduğundan daha fazla depolama isterse, bunun nedeni kötü tasarım olabilir. Örneğin, bir uygulama bir görüntünün birden çok kopyasını oluşturursa veya bir diziye bir dosya yüklerse, görüntü veya dosya çok büyük olduğunda depolama alanı tükenir. Bu normal bir kaynak tükenmesidir. Uygulama tasarlandığı gibi çalışıyor (bu tasarım açıkça kabadayı olmasına rağmen).

Ancak bir uygulama aynı tür verileri işlerken bellek kullanımını sürekli olarak artırırsa, bellek sızıntınız olabilir.

2. Ayrıntılı Çöp Toplama özelliğini etkinleştirin

Gerçekten bir bellek sızıntısı olduğunu söylemenin en hızlı yollarından biri, ayrıntılı çöp toplamayı etkinleştirmektir. Bellek kısıtlaması sorunları genellikle verbosegc kalıplar incelenerek tanımlanabilir.

Spesifik olarak, -verbosegc bağımsız değişkeni, çöp toplama (GC) işlemi her başlatıldığında bir izleme oluşturmanıza olanak tanır. Diğer bir deyişle, bellek çöp olarak toplanırken, standart hataya göre özet raporlar yazdırılır ve size belleğinizin nasıl yönetildiğine dair bir fikir verir.

İşte –verbosegc seçeneğiyle oluşturulan bazı tipik çıktılar:

ayrıntılı çöp toplama çıktısı

Bu GC izleme dosyasındaki her blok (veya kıta) artan sırada numaralandırılmıştır. Bu izi anlamlandırmak için, ardışık Tahsis Hatası stanzalarına bakmalı ve toplam hafıza (burada, 19725304) artarken zamanla azalan boş hafızaya (bayt ve yüzde) bakmalısınız. Bunlar tipik bellek tükenmesi belirtileridir.

3. Profil Oluşturmayı Etkinleştir

Farklı JVM'ler, tipik olarak nesnelerin türü ve boyutu hakkında ayrıntılı bilgi içeren yığın etkinliğini yansıtacak izleme dosyaları oluşturmak için farklı yollar sunar. Buna yığının profilini çıkarma denir.

4. İzi Analiz Edin

Bu gönderi, Java VisualVM tarafından oluşturulan izlemeye odaklanır. İzler, farklı Java bellek sızıntısı algılama araçları tarafından oluşturulabildiklerinden farklı biçimlerde olabilir, ancak arkasındaki fikir her zaman aynıdır: yığında orada olmaması gereken bir nesne bloğu bulun ve bu nesnelerin birikip birikmediğini belirleyin. serbest bırakmak yerine. Java uygulamasında belirli bir olay her tetiklendiğinde tahsis edildiği bilinen geçici nesneler özellikle ilgi çekicidir. Yalnızca küçük miktarlarda olması gereken birçok nesne örneğinin varlığı, genellikle bir uygulama hatasını gösterir.

Son olarak, bellek sızıntılarını çözmek için kodunuzu baştan sona gözden geçirmeniz gerekir. Nesne sızıntısının türünü öğrenmek çok yardımcı olabilir ve hata ayıklamayı önemli ölçüde hızlandırabilir.

JVM'de Çöp Toplama Nasıl Çalışır?

Bellek sızıntısı sorunu olan bir uygulamanın analizine başlamadan önce, JVM'de çöp toplamanın nasıl çalıştığına bakalım.

JVM, izleme toplayıcı adı verilen bir çöp toplayıcı biçimi kullanır; bu, esas olarak etrafındaki dünyayı duraklatarak, tüm kök nesneleri (doğrudan iş parçacıkları tarafından başvurulan nesneler) işaretleyerek ve referanslarını izleyerek yol boyunca gördüğü her nesneyi işaretleyerek çalışır.

Java , oluşturulan nesnelerin çoğunun hızlı bir şekilde atıldığını ve hızlı bir şekilde toplanmayan nesnelerin bir süre daha buralarda olacağını belirten, nesiller arası hipotez varsayımına dayalı olarak nesiller boyu çöp toplayıcı adı verilen bir şey uygular.

Bu varsayıma dayalı olarak Java, nesneleri birden çok nesle böler. İşte görsel bir yorum:

Java bölümleri birden çok nesile

  • Genç Nesil - Nesnelerin başladığı yer burasıdır. İki alt nesli vardır:

    • Eden Space - Nesneler buradan başlar. Çoğu nesne Eden Space'de yaratılır ve yok edilir. Burada GC, optimize edilmiş çöp toplamaları olan Minor GC'leri yapar. Bir Minor GC gerçekleştirildiğinde, hala ihtiyaç duyulan nesnelere yapılan referanslar, hayatta kalanlar alanlarından birine (S0 veya S1) geçirilir.

    • Survivor Space (S0 ve S1) - Eden'de hayatta kalan nesneler burada biter. Bunlardan iki tane var ve herhangi bir zamanda yalnızca biri kullanılıyor (ciddi bir bellek sızıntısı olmadığı sürece). Her GC döngüsünde dönüşümlü olarak biri boş , diğeri canlı olarak belirlenir.

  • Kiralanmış Nesil - Eski nesil olarak da bilinir (Şekil 2'deki eski alan), bu alan daha uzun ömürlü eski nesneleri tutar (yeterince uzun yaşarlarsa hayatta kalan alanlardan taşınır). Bu alan dolduğunda, GC, performans açısından daha maliyetli olan bir Tam GC yapar. Bu alan sınırsız büyürse, JVM bir OutOfMemoryError - Java heap space atar.

  • Kalıcı Nesil - Kalıcı nesil ile yakından ilişkili üçüncü nesil, kalıcı nesil özeldir çünkü sanal makinenin Java dili düzeyinde eşdeğeri olmayan nesneleri tanımlamak için ihtiyaç duyduğu verileri tutar. Örneğin, sınıfları ve yöntemleri tanımlayan nesneler kalıcı nesilde saklanır.

Java, her nesle farklı çöp toplama yöntemleri uygulayacak kadar akıllıdır. Genç nesil, Parallel New Collector adlı bir izleme, kopyalama toplayıcı kullanılarak işlenir. Bu koleksiyoncu dünyayı durdurur ama genç nesil genellikle küçük olduğu için ara kısadır.

JVM nesilleri ve nasıl çalıştıkları hakkında daha ayrıntılı bilgi için Java HotSpot Sanal Makinesi belgelerindeki Bellek Yönetimi bölümünü ziyaret edin.

Bellek Sızıntısı Tespiti

Bellek sızıntılarını bulmak ve bunları ortadan kaldırmak için uygun bellek sızıntısı araçlarına ihtiyacınız vardır. Java VisualVM'yi kullanarak böyle bir sızıntıyı tespit etme ve kaldırma zamanı.

Java VisualVM ile Yığının Uzaktan Profilini Çıkarma

VisualVM, Java teknolojisi tabanlı uygulamalar çalışırken onlar hakkında ayrıntılı bilgileri görüntülemek için görsel bir arabirim sağlayan bir araçtır.

VisualVM ile yerel uygulamalarla ve uzak ana bilgisayarlarda çalışanlarla ilgili verileri görüntüleyebilirsiniz. Ayrıca JVM yazılım örnekleriyle ilgili verileri yakalayabilir ve verileri yerel sisteminize kaydedebilirsiniz.

Java VisualVM'nin tüm özelliklerinden yararlanmak için Java Platform, Standard Edition (Java SE) sürüm 6 veya üzerini çalıştırmalısınız.

İlgili: Neden Zaten Java 8'e Yükseltmeniz Gerekiyor?

JVM için Uzak Bağlantıyı Etkinleştirme

Bir üretim ortamında, kodumuzun üzerinde çalışacağı gerçek makineye erişmek genellikle zordur. Neyse ki Java uygulamamızın profilini uzaktan yapabiliyoruz.

İlk olarak, hedef makinede kendimize JVM erişimi vermemiz gerekiyor. Bunu yapmak için, aşağıdaki içeriğe sahip jstatd.all.policy adlı bir dosya oluşturun:

 grant codebase "file:${java.home}/../lib/tools.jar" { permission java.security.AllPermission; };

Dosya oluşturulduktan sonra, aşağıdaki gibi jstatd - Virtual Machine jstat Daemon aracını kullanarak hedef VM'ye uzak bağlantıları etkinleştirmemiz gerekiyor:

 jstatd -p <PORT_NUMBER> -J-Djava.security.policy=<PATH_TO_POLICY_FILE>

Örneğin:

 jstatd -p 1234 -J-Djava.security.policy=D:\jstatd.all.policy

Hedef VM'de başlatılan jstatd ile hedef makineye bağlanabilir ve bellek sızıntısı sorunları olan uygulamanın profilini uzaktan belirleyebiliriz.

Uzak Ana Bilgisayara Bağlanma

İstemci makinede bir bilgi istemi açın ve VisualVM aracını açmak için jvisualvm yazın.

Ardından, VisualVM'de bir uzak ana bilgisayar eklemeliyiz. Hedef JVM, J2SE 6 veya üstü olan başka bir makineden uzak bağlantılara izin verecek şekilde etkinleştirildiğinden, Java VisualVM aracını başlatır ve uzak ana bilgisayara bağlanırız. Uzak ana bilgisayarla bağlantı başarılı olduysa, burada görüldüğü gibi hedef JVM'de çalışan Java uygulamalarını göreceğiz:

hedef jvm'de çalışıyor

Uygulamada bir bellek profili oluşturucuyu çalıştırmak için yan paneldeki ismine çift tıklamamız yeterlidir.

Artık hepimiz bir bellek analizörü kurduk, şimdi MemLeak olarak adlandıracağımız bellek sızıntısı sorunu olan bir uygulamayı inceleyelim.

MemLeak

Elbette, Java'da bellek sızıntıları oluşturmanın birkaç yolu vardır. Basit olması için bir sınıfı HashMap bir anahtar olarak tanımlayacağız, ancak equals() ve hashcode() yöntemlerini tanımlamayacağız.

Bir HashMap, Harita arabirimi için bir karma tablo uygulamasıdır ve bu nedenle anahtar ve değerin temel kavramlarını tanımlar: her değer benzersiz bir anahtarla ilişkilidir, bu nedenle belirli bir anahtar/değer çiftinin anahtarı zaten mevcutsa HashMap, mevcut değeri değiştirilir.

Anahtar sınıfımızın equals() ve hashcode() yöntemlerinin doğru bir şekilde uygulanmasını sağlaması zorunludur. Onlar olmadan, iyi bir anahtarın üretileceğinin garantisi yoktur.

equals() ve hashcode() yöntemlerini tanımlamayarak, aynı anahtarı tekrar tekrar HashMap'e ekliyoruz ve anahtarı gerektiği gibi değiştirmek yerine, HashMap sürekli büyüyor, bu aynı anahtarları tanımlayamıyor ve bir OutOfMemoryError atıyor .

İşte MemLeak sınıfı:

 package com.post.memory.leak; import java.util.Map; public class MemLeak { public final String key; public MemLeak(String key) { this.key =key; } public static void main(String args[]) { try { Map map = System.getProperties(); for(;;) { map.put(new MemLeak("key"), "value"); } } catch(Exception e) { e.printStackTrace(); } } }

Not: bellek sızıntısı, 14. satırdaki sonsuz döngüden kaynaklanmaz : sonsuz döngü, kaynak tükenmesine neden olabilir, ancak bellek sızıntısına neden olmaz. Equals equals() ve hashcode() yöntemlerini düzgün bir şekilde uygulamış olsaydık, HashMap içinde yalnızca bir öğeye sahip olacağımız için kod sonsuz döngüde bile sorunsuz çalışırdı.

(İlgilenenler için, burada (kasıtlı olarak) sızıntı oluşturmanın bazı alternatif yolları bulunmaktadır.)

Java VisualVM'yi Kullanma

Java VisualVM ile Java Yığınını bellek olarak izleyebilir ve davranışının bir bellek sızıntısının göstergesi olup olmadığını belirleyebiliriz.

İşte başlatmadan hemen sonra MemLeak'in Java Yığın çözümleyicisinin grafiksel bir temsili (çeşitli nesiller hakkındaki tartışmamızı hatırlayın):

java visualvm kullanarak bellek sızıntılarını izleyin

Sadece 30 saniye sonra, Eski Nesil neredeyse doluyor, bu da Tam bir GC ile bile Eski Nesil'in sürekli büyüdüğünü, bu da bir bellek sızıntısının açık bir işareti olduğunu gösteriyor.

Bu sızıntının nedenini saptamanın bir yolu, bir yığın dökümü ile Java VisualVM kullanılarak oluşturulan aşağıdaki görüntüde ( yakınlaştırmak için tıklayın ) gösterilmektedir. Burada Hashtable$Entry nesnelerinin %50'sinin heap içinde olduğunu görüyoruz, ikinci satır ise bizi MemLeak sınıfına yönlendiriyor. Bu nedenle, bellek sızıntısına MemLeak sınıfı içinde kullanılan bir karma tablo neden olur.

karma tablo bellek sızıntısı

Son olarak, Genç ve Eski nesillerin tamamen dolu olduğu OutOfMemoryError hemen sonra Java Heap'i gözlemleyin.

yetersiz bellek hatası

Çözüm

Belirtiler çeşitli olduğundan ve yeniden üretilmesi zor olduğundan, bellek sızıntıları, çözülmesi en zor Java uygulama sorunları arasındadır. Burada, bellek sızıntılarını keşfetmeye ve kaynaklarını belirlemeye yönelik adım adım bir yaklaşımı özetledik. Ancak hepsinden önemlisi, hata mesajlarınızı yakından okuyun ve yığın izlerinize dikkat edin; tüm sızıntılar göründükleri kadar basit değildir.

ek

Java VisualVM ile birlikte, bellek sızıntısı tespiti yapabilen başka araçlar da vardır. Birçok sızıntı detektörü, bellek yönetimi rutinlerine yapılan çağrıları yakalayarak kitaplık düzeyinde çalışır. Örneğin, HPROF , yığın ve CPU profili oluşturma için Java 2 Platform Standard Edition (J2SE) ile birlikte verilen basit bir komut satırı aracıdır. HPROF çıktısı doğrudan analiz edilebilir veya JHAT gibi diğer araçlar için girdi olarak kullanılabilir. Java 2 Enterprise Edition (J2EE) uygulamalarıyla çalıştığımızda, Websphere uygulama sunucuları için IBM Heapdumps gibi daha kolay olan bir dizi yığın dökümü çözümleyicisi çözümü vardır.