54 Soru: Java ile hafıza sızıntısı oluşturma

tarafından oluşturulan soru Mon, Jan 21, 2019 12:00 AM

Az önce bir röportaj yaptım ve Java ile bir bellek sızıntısı oluşturmam istendi
Söylemeye gerek yok, nasıl bir tane oluşturmaya başlayacağımı bile bilmiyordum.

Bir örnek ne olurdu?

    
2971
  1. Onlara Java'nın bir çöp toplayıcı kullandığını ve onlardan "bellek sızıntısı" tanımları hakkında biraz daha spesifik olmalarını söyleyerek şunu açıkladım; hatalar - Java, oldukça içinde C /C ++ ile aynı şekilde bellek sızdırmaz. Bir yere nesnesine bir referans olması gerekir.
    2011-07-07 19: 00: 05Z
  2. Çoğu cevapta insanların bu uç davaları ve püf noktaları aradıklarını ve noktayı tamamen kaçırdıklarını (IMO) komik buluyorum. Sadece bir daha asla kullanmayacak nesnelere yararsız referanslar tutan ve aynı zamanda bu referansları asla bırakmayan kodlar gösterebildiler; Biri bu vakaların "gerçek" bellek sızıntısı olmadığını söyleyebilir çünkü hala bu nesnelere referanslar var, ancak program bu referansları bir daha asla kullanmaz ve aynı zamanda asla bırakmazsa, bu tamamen "(" kadar kötü) gerçek bellek sızıntısı ".
    2011-07-22 06: 14: 24Z
  3. Dürüst olmak gerekirse, "Git" hakkında sorduğum benzer sorunun -1'e düşürüldüğüne inanamıyorum. Burada: stackoverflow.com/questions/4400311/… Temel olarak bahsettiğim bellek sızıntıları OP’e +200 oy vermiş ve henüz saldırıya uğradım ve hakarette bulundum "Go" nun da aynı sorunu olup olmadığını sormak. Her nasılsa tüm wiki şeylerinin bu kadar iyi çalıştığından emin değilim.
    2011-07-22 11: 37: 14Z
  4. @ SyntaxT3rr0r - darien'in cevabı fanboyizm değildir. belirli JVM'lerin, hafızanın sızdırıldığı anlamına gelen hatalara sahip olabileceğini açıkça itiraf etti. bu, bellek sızıntılarına izin veren dil spesifikasyonunun kendisinden farklıdır.
    2011-07-22 15: 24: 59Z
  5. @ ehabkost: Hayır, eşdeğer değiller. (1) Belleği geri alma yeteneğine sahipsiniz, oysaki "gerçek bir sızıntı" durumunda, C /C ++ programınız tahsis edilen aralığı unutur, kurtarmanın güvenli bir yolu yoktur. (2) Profil oluşturmadaki sorunu kolayca tespit edebilirsiniz, çünkü "şişkinliğin" hangi nesneleri içerdiğini görebilirsiniz. (3) "Gerçek bir sızıntı" kesin bir hatadır, ancak sonlandırılıncaya kadar etrafta birçok nesne tutan bir program, çalışma şeklinin kasıtlı bir parçası olabilir .
    2011-07-22 18: 37: 04Z
30 Yanıtlar                              30                         

Saf Java'da gerçek bir bellek sızıntısı (kod çalıştırarak erişilemeyen ancak yine de bellekte depolanan nesneler) oluşturmanın iyi bir yolu:

  1. Uygulama uzun süren bir iş parçacığı oluşturur (veya daha hızlı sızdırmak için bir iş parçacığı havuzu kullanın).
  2. İş parçacığı bir sınıfı (isteğe bağlı olarak özel) ClassLoader aracılığıyla yükler.
  3. Sınıf, büyük bir bellek yığını (örneğin, new byte[1000000]) ayırır, statik bir alanda kendisine güçlü bir başvuru saklar ve sonra bir ThreadLocal içinde kendisine bir başvuru saklar. Ekstra bellek tahsis etmek isteğe bağlıdır (Sınıf örneğini sızdırmak yeterlidir), ancak sızıntının daha hızlı çalışmasını sağlar.
  4. İş parçacığı, özel sınıfa veya yüklendiği ClassLoader'a yapılan tüm başvuruları siler.
  5. tekrarlayın.

Bu, ThreadLocal'ın, ClassLoader öğesinin referansını tutan Class'in başvurusunu koruyan, nesneye bir referans tutması nedeniyle çalışır. ClassLoader, sırayla, yüklediği tüm Sınıflara referans tutar.

(Özellikle birçok JVM uygulamasında daha kötüydü.Java 7'den önce, Sınıflar ve Sınıf Yükleyicileri doğrudan permgen içine tahsis edildi ve hiçbir zaman GC olmadı. Bununla birlikte, JVM'nin sınıf boşaltma yöntemini nasıl işlediğine bakılmaksızın, ThreadLocal bir Class nesnesinin geri kazanılmasını engelleyecektir.)

Bu kalıptaki bir değişiklik, ThreadLocals'ı herhangi bir şekilde kullanan uygulamaları sık sık yeniden konuşlandırıyorsanız, uygulama kaplarının (Tomcat gibi) elek gibi hafızadan sızmasına neden olabilir. (Uygulama kabı açıklandığı gibi Konular'ı kullandığından ve uygulamayı her seferinde yeniden başlattığınızda yeni bir ClassLoader kullanılır.)

Güncelleme : Pek çok insan sormaya devam ettiğinden, işte bazı örnek kod bu da bu davranışı eylem halinde gösterir .

    
2161
2017-09-06 02: 56: 18Z
  1. + 1 ClassLoader sızıntıları, genellikle verileri dönüştüren 3. taraf lib'lerin neden olduğu JEE dünyasında en sık acı veren bellek sızıntılarından bazılarıdır (BeanUtils, XML /JSON kodekleri) . Bu, lib uygulamanızın kök sınıf yükleyicisinin dışına yüklendiğinde ancak sınıflarınıza referanslar verdiğinde olabilir (örneğin, önbelleğe alarak). Uygulamanızın dağıtımını kaldırdığınızda /yeniden konuşlandırdığınızda, JVM uygulamanın sınıf yükleyicisini (ve bu nedenle onun tarafından yüklenen tüm sınıfları) toplayamaz, bu nedenle yinelenen uygulama sunucusunu en sonunda borçlandırır. Şanslıysanız ClassCastException z.x.y.Abc ile ilgili bir ipucu bulursanız, z.x.y.Abc
    dosyasına atılamaz
    2011-06-27 16: 55: 16Z
  2. tomcat, TÜM yüklü sınıflarda hileler ve nils TÜM statik değişkenleri kullanıyor, tomcat'te çok sayıda dataraces ve hatalı kodlama var (biraz zaman alıp düzeltmeniz gerekir), artı Tüm akıllara durgunluk veren ConcurrentLinkedQueue, dahili (küçük) nesneler için önbellek olarak, ConcurrentLinkedQueue.Node bile daha fazla bellek harcayacak kadar küçük.
    2011-06-30 19: 49: 07Z
  3. + 1: Sınıf yükleyici sızıntıları bir kabus. Onları anlamaya çalışarak haftalar geçirdim. Üzücü olan, @earcam'in söylediği gibi, çoğunlukla 3. parti lib'lerden kaynaklanır ve çoğu profilist bu sızıntıları tespit edemez. Bu blogda Classloader sızıntıları hakkında iyi ve net bir açıklama var. blogs.oracle.com/fkieviet/entry/…
    2011-07-08 09: 08: 02Z
  4. @ Nicolas: Emin misiniz? JRockit, GC Class nesnelerini varsayılan olarak yapar ve HotSpot yapmaz, ancak AFAIK JRockit, ThreadLocal tarafından başvurulan bir Class veya ClassLoader'ı GC olarak yapamaz.
    2011-07-11 17: 01: 49Z
  5. Tomcat sizin için bu sızıntıları tespit etmeye çalışacak ve bunlar hakkında uyarmaya çalışacak: wiki.apache.org/tomcat/MemoryLeakProtection . En yeni sürüm bazen sizin için sızıntıyı bile düzeltecektir.
    2012-03-02 10: 33: 03Z

Nesne referansını içeren statik alan [esp final field]

 
class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

Arama yapma Uzun Dize üzerinde String.intern()

 
String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();

(Kapatılmamış) açık akışlar (dosya, ağ vb ...)

 
try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

Kapatılmamış bağlantılar

 
try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

Yerel yöntemlerle ayrılan bellek gibi, JVM'nin çöp toplayıcısından erişilemeyen alanlar

Web uygulamalarında, uygulama açıkça durdurulana veya kaldırılıncaya kadar bazı nesneler uygulama kapsamında saklanır.

 
getServletContext().setAttribute("SOME_MAP", map);

Kullanılmayan sınıf çöp toplanmasını önleyen IBM JDK'daki noclassgc seçeneği gibi yanlış veya uygunsuz JVM seçenekleri ,

Bkz. IBM jdk ayarları .

    
1154
2018-01-30 23: 32: 27Z
  1. Bağlam ve oturum niteliklerinin "sızıntı" olduğuna katılmıyorum. Onlar sadece uzun ömürlü değişkenler. Ve statik son alan az çok sabittir. Belki de büyük sabitlerden kaçınılmalıdır, ancak buna bellek sızıntısı demenin adil olmadığını düşünüyorum.
    2011-07-13 04: 08: 51Z
  2. (Kapatılmamış) açık akışlar (dosya, ağ vb ...) , sonlandırma sırasında gerçek anlamda sızıntı yapmıyor (ki bu Bir sonraki GC döngüsünden sonra close () programlanacak (blokaj işlemi olabileceğinden, sonlandırıcı ipliğine genellikle close() çağrılmaz). Kapatmamak kötü bir uygulamadır, ancak sızıntıya neden olmaz. Kapatılmamış java.sql.Connection aynıdır.
    2011-07-17 18: 38: 25Z
  3. Çoğu aklı başında JVM'lerde, String sınıfı intern karma içeriği üzerinde yalnızca zayıf bir referansa sahip görünüyor. Dolayısıyla, çöp düzgün bir şekilde toplanır ve bir sızıntı değildir. (ancak IANAJP) mindprod.com/jgloss/interned.html#GC
    2011-07-22 01: 32: 24Z
  4. Nesne referansını tutan statik alan [esp final field] bir bellek sızıntısı nasıldır?
    2012-09-10 19: 52: 57Z
  5. @ cHao Doğru. Karşılaştığım tehlike, Streams tarafından sızan hafızanın problemleri değil. Sorun, yeterli hafızanın onlar tarafından sızdırılmamasıdır. Çok fazla tutamak sızdırabilir, ancak hala çok fazla hafızaya sahip olabilirsiniz. Çöp Toplayıcı daha sonra hala çok fazla hafızası olduğundan tam bir toplama yapmamaya karar verebilir. Bu, sonlandırıcının çağrılmadığı anlamına gelir, bu nedenle tutamaklarınız tükenir. Buradaki sorun, sonlandırıcıların (genellikle), sızan akışlardan gelen bellek tükenmeden önce çalıştırılmasıdır, ancak bellekten başka bir şey tükenmeden önce aranamayabilir.
    2014-08-25 15: 37: 38Z

Yapılması gereken basit bir şey bir HashSet'i yanlış (veya olmayan) bir hashCode() veya equals() ile kullanmak ve sonra da "kopyaları" eklemeye devam etmektir. Kopyaları olması gerektiği gibi görmezden gelmek yerine, küme yalnızca büyüyecek ve bunları kaldıramayacaksınız.

Bu kötü anahtarların /öğelerin takılmasını istiyorsanız, gibi bir statik alan kullanabilirsiniz

 
class BadKey {
   // no hashCode or equals();
   public final String key;
   public BadKey(String key) { this.key = key; }
}

Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.
    
438
2011-06-24 21: 50: 56Z
  1. Aslında, element sınıfı hashCode alsa ve eşit değilse bile, elemanları bir HashSet'ten kaldırabilirsiniz; sadece set için bir yineleyici edinin ve yineleyici gerçekte öğeleri değil temel girişlerin üzerinde çalıştığından remove yöntemini kullanın. (Uygulanmamış bir hashCode /equals değerinin, bir sızıntıyı tetiklemek için yeterli değil olduğunu; varsayılanlar basit nesne kimliği uygular; böylece öğeleri alabilir ve normal şekilde kaldırabilirsiniz.)
    2011-06-25 17: 23: 47Z
  2. @ Donal demeye çalışıyorum, sanırım, bir bellek sızıntısı tanımınıza katılmıyorum. (Analojiye devam etmek için) yineleyici kaldırma tekniğinizi bir sızıntının altında bir damlama tavası olarak düşünebilirim; sızıntı damlama tavası ne olursa olsun hala mevcuttur.
    2011-06-26 20: 24: 59Z
  3. Katılıyorum, bu değil bir bellek "sızıntısı", çünkü hashset referanslarını kaldırabilir ve GC'nin başlamasını bekleyebilirsiniz içeri ve presto! hafıza geri gider.
    2011-07-02 04: 30: 30Z
  4. @ SyntaxT3rr0r, sorunuzu, dilde doğal olarak bellek sızıntılarına neden olan herhangi bir şey olup olmadığını sormak olarak yorumladım. Cevap HAYIR. Bu sorular bir durum yaratmanın mümkün olup olmadığını soruyor Bir bellek sızıntısı gibi Bu örneklerin hiçbiri bir C /C ++ programcısının anlayacağı şekilde bellek sızıntısı değildir.
    2011-07-22 12: 32: 12Z
  5. @ Peter Lawrey: ayrıca, bunun hakkında ne düşünüyorsun: "C dilinde, eğer yapmazsan, doğal olarak bellek sızıntısına sızan hiçbir şey yok. ayırdığınız hafızayı elle boşaltmayı unutun "". Entelektüel sahtekârlık için bu nasıl olurdu? Her neyse, yoruldum: son sözün olabilir.
    2011-07-22 12: 56: 46Z

Aşağıda, standart unutulmuş dinleyiciler, statik referanslar, hashmaps'deki sahte /değiştirilebilir anahtarlar veya sadece yaşam döngüsünü sonlandırma şansı olmayan sıkışmış konuların yanı sıra Java'nın sızdırdığı açık olmayan bir durum olacaktır. p>

  •  File.deleteOnExit() - her zaman dizeden sızar, dizginin bir alt dize olması durumunda, sızıntı daha da kötüdür (altta yatan karakter [] de sızdırılmış) - Java 7 alt öğesinde de char[]'u kopyalar; daha sonra uygulanmaz ; @Daniel, oy kullanma gereği yok ama.

Genelde yönetilmeyen konuların tehlikesini göstermek için konulara yoğunlaşacağım, sallanmaya bile dokunmak istemiyorum.

  • Runtime.addShutdownHook ve kaldırmayın ... ve daha sonra removeShutdownHook ile, ThreadGroup sınıfındaki toplanmamış olabileceği başlatılmamış konulara ilişkin bir hata nedeniyle, ThreadGroup'u etkin bir şekilde sızdırıyor. JGroup GossipRouter’da sızıntı var.

  • Bir Thread’un oluşturulması ancak başlamaması yukarıdaki ile aynı kategoriye giriyor.

  • Bir iş parçacığı oluşturmak, ContextClassLoader ve AccessControlContext, artı ThreadGroup ve herhangi bir InheritedThreadLocal'u devralır, tüm bu referanslar, sınıf yükleyici tarafından yüklenen tüm sınıflar ve tüm statik referanslar ve ja-ja ile birlikte potansiyel sızıntılardır. Bu etki özellikle tüm j.u.c.'de görülebilir. Süper basit bir ThreadFactory arayüze sahip olan ek çerçeve, ancak çoğu geliştiricinin gizlenme tehlikesiyle ilgili hiçbir fikri yok. Ayrıca birçok kütüphane istek üzerine iş parçacığı başlatır (çok fazla endüstride popüler kütüphane).

  • ThreadLocal önbellek; Bunlar birçok durumda kötüdür. Herkesin ThreadLocal'a dayanan bir miktar basit önbellek gördüğüne eminim, kötü haberler: Eğer iş parçacığı ClassLoader bağlamında beklenenden fazla devam ederse, saf bir küçük sızıntıdır. Gerçekten gerekmedikçe ThreadLocal önbelleklerini kullanmayın.

  • ThreadGroup hiçbir konuya sahip olmadığında ThreadGroup.destroy()'u aramak, ancak yine de ThreadGrupları alt tutar. ThreadGroup'un ebeveyninden ayrılmasını önleyebilecek kötü bir sızıntı, ancak tüm çocuklar numaralandırılamaz hale geldi.

  • WeakHashMap öğesinin kullanılması ve (in) değeri doğrudan anahtara başvurur. Bu bir yığın dökümü olmadan bulmak zor bir tanesidir. Bu, korunan nesneye zorlu bir referans tutabilecek tüm genişletilmiş Weak/SoftReference için geçerlidir.

  • java.net.URL'u HTTP (S) protokolüyle kullanmak ve kaynağı (!) konumundan yüklemek. Bu özel olan KeepAliveCache, ThreadGroup sisteminde mevcut konu bağlam sınıf yükleyicisinden sızıntı yapan yeni bir konu yaratır. Canlı bir iplik olmadığında, iş parçacığı ilk istek üzerine oluşturulur, böylece ya şanslı ya da sadece sızıntı alabilirsiniz. Sızıntı, Java 7'de zaten düzeltilmiştir ve iş parçacığı oluşturan kod, bağlam sınıfı yükleyiciyi düzgün şekilde kaldırır. Birkaç vaka daha var (, ImageFetcher gibi , ayrıca düzeltildi ) benzer iş parçacıkları oluştururken.

  • Yapıcıda InflaterInputStream'u geçen new java.util.zip.Inflater()'u kullanma (örneğin PNGImageDecoder) ve inflater'ın end()'unu çağırmaz. Peki, sadece new ile kurucuya geçerseniz, şans yok ... Ve evet, el ile yapıcı parametresi olarak geçirilirse, yayında close()'u aramak inflater'ı kapatmaz. Bu kesin bir sızıntı değil çünkü kesinleştirici tarafından gerekli gördüğü zaman serbest bırakılacak. O ana kadar yerel belleği o kadar çok tüketirse, Linux oom_killer'in işlemi durdurmadan öldürmesine neden olabilir. Asıl mesele, Java’da sonlandırmanın çok güvenilmez olması ve G1’in 7.0.2’ye kadar daha da kötüleşmesi. Hikayenin ahlaki: mümkün olan en kısa sürede yerel kaynakları serbest bırakın; sonlandırıcı sadece çok zayıf.

  • java.util.zip.Deflater ile aynı durum. Deflater, Java'da aç olan bellek olduğundan, bu daha kötüdür, yani her zaman 15 bit (en fazla) ve 8 bellek kullanırdüzeyleri (9 maks.) birkaç yüz KB yerel bellek ayırıyor. Neyse ki, Deflater yaygın olarak kullanılmamaktadır ve benim bildiğim kadarıyla JDK yanlış kullanım içermemektedir. El ile bir end() veya Deflater oluşturuyorsanız, her zaman Inflater'u arayın. Son ikisinin en iyi kısmı: mevcut normal profil oluşturma araçlarıyla bulamıyorsunuz.

(İstek üzerine karşılaştığım daha fazla zaman kaybını ekleyebilirim.)

İyi şanslar ve güvende kalın; sızıntılar kötüdür!

    
260
2016-02-16 15: 52: 26Z
  1. Creating but not starting a Thread... Yikes, birkaç yüzyıl önce bu kadar kötü bir şekilde ısırıldım! (Java 1.3)
    2011-07-09 01: 54: 40Z
  2. @ leonbloy, konu gruba doğrudan eklendiğinden daha da kötüye gitmeden önce, çok zor bir sızıntı anlamına gelmiyordu. Bu sadece unstarted sayısını arttırmıyor, ancak iş parçacığı grubunun yok etmesini önlüyor (daha az kötülük ama yine de bir sızıntı)
    2011-07-09 06: 52: 08Z
  3. Teşekkürler! "ThreadGroup hiçbir konuya sahip olmadığında ThreadGroup.destroy()'u aramak ..." inanılmaz derecede ince bir hatadır; Bunu saatlerdir takip ediyorum, yoldan saptığım için kontrol GUI'mdeki iş parçacığını numaralandırmak hiçbir şey göstermedi, ancak iş parçacığı grubu ve muhtemelen en az bir çocuk grubu ortadan kalkmayacaktı.
    2017-11-08 02: 47: 03Z
  4. @ bestsss: Merak ediyorum, neden bir JVM kapatmada çalıştığı göz önüne alındığında bir kapatma kancasını kaldırmak isteyebilirsiniz?
    2017-11-09 21: 09: 34Z

Buradaki çoğu örnek "çok karmaşık" tır. Onlar kenar davaları. Bu örneklerle, programcı bir hata yaptı (eşittir /hashcode'u yeniden tanımlamıyor gibi) veya JVM /JAVA'nın (statik içeren sınıf yükü ...) bir köşe durumu tarafından ısırıldı. Bence bu bir görüşmecinin istediği bir örnek değil, hatta en sık karşılaşılan durumdur.

Ancak bellek sızıntıları için gerçekten daha basit durumlar vardır. Çöp toplayıcı yalnızca artık referans alınmayanı serbest bırakır. Java geliştiricileri olarak hafızayı umursamıyoruz. Gerektiğinde tahsis ediyoruz ve otomatik olarak serbest bırakılıyor. Güzel.

Ancak, uzun ömürlü herhangi bir başvuru ortak devlet olma eğilimindedir. Her şey olabilir, statik, singleton ... Genellikle önemsiz olmayan uygulamalar karmaşık nesneler grafikler yapma eğilimindedir. Sadece boş bir başvuru ayarlamayı unutmak ya da daha sık olarak bir nesneyi koleksiyondan kaldırmayı unutmak, bellek sızıntısı yapmak için yeterlidir.

Elbette her türlü dinleyici (kullanıcı arabirimi dinleyicileri gibi), önbellek veya uzun ömürlü paylaşılan bir durum, doğru şekilde kullanılmazsa bellek sızıntısı üretme eğilimindedir. Anlaşılması gereken, bunun bir Java köşe davası olmadığı veya çöp toplayıcı ile ilgili bir sorun olmadığıdır. Bu bir tasarım problemi. Uzun ömürlü bir nesneye bir dinleyici eklediğimizi tasarlıyoruz, ancak artık gerekmediğinde dinleyiciyi çıkarmıyoruz. Nesneleri önbelleğe alıyoruz, ancak önbellekten kaldırma stratejimiz yok.

Belki de bir hesaplama için gereken önceki durumu saklayan karmaşık bir grafiğe sahibiz. Ancak önceki durumun kendisi, önceki ve sonraki durumlarla kendi devletiyle bağlantılıdır.

SQL bağlantılarını veya dosyalarını kapatmamız gerekiyor. Öğeleri null toplama ve toplama için uygun referanslar belirlememiz gerekir. Uygun önbellekleme stratejilerine sahip olacağız (maksimum bellek boyutu, öğe sayısı veya zamanlayıcılar). Bir dinleyicinin bilgilendirilmesine izin veren tüm nesneler hem addListener hem de removeListener yöntemi sağlamalıdır. Ve bu bildiriciler artık kullanılmadığında, dinleyici listelerini temizlemeleri gerekir.

Bir bellek sızıntısı gerçekten gerçekten mümkün ve mükemmel bir şekilde tahmin edilebilir. Özel dil özelliklerine veya köşe kılıflarına gerek yok. Bellek sızıntısı ya bir şeyin eksik olabileceğinin ya da tasarım sorunlarının bir göstergesi olabilir.

    
184
2018-02-13 07: 27: 47Z
  1. Diğer cevaplarda insanların bu vakaları ve püf noktalarını aradıklarını ve görüştüğünü komik buluyorum.m noktasını tamamen kaçırmak. Sadece bir daha asla kullanmayacak nesnelere yararsız referanslar tutan ve bu referansları asla kaldırmayan kodlar gösterebilirlerdi; Biri bu vakaların "gerçek" bellek sızıntısı olmadığını söyleyebilir çünkü hala bu nesnelere referanslar var, ancak program bu referansları bir daha asla kullanmaz ve aynı zamanda asla bırakmazsa, bu tamamen "(" kadar kötü) gerçek bellek sızıntısı ".
    2011-07-22 06: 12: 36Z
  2. @ Nicolas Bousquet: "Bellek sızıntısı gerçekten de mümkündür" Çok teşekkür ederim. +15 olumlu oylama. Güzel. Bu dili, Git diliyle ilgili bir sorunun öncülünde olarak bağırdım: stackoverflow.com/questions/4400311 Bu sorunun hala olumsuz yönleri var :(
    2011-07-22 11: 49: 18Z
  3. Java ve .NET'teki GC, bir anlamda, diğer nesnelere referanslar taşıyan nesnelerin varsayım grafiğine dayanmaktadır. hakkında "diğer nesneler. Gerçekte, referans grafiğinde "ilgi çekici" ilişkileri temsil etmeyen kenarlar olabilir ve doğrudan veya dolaylı bir referans yolu olmasa bile bir nesnenin başka bir nesnenin varlığına önem vermesi mümkündür (WeakReference'u kullanarak bile) birinden diğerine var. Bir nesne referansının yedek biti varsa, "hedefe önem veriyor" göstergesinin bulunması yararlı olabilir.
    2013-05-09 16: 41: 42Z
  4. ... ve eğer bir nesnenin onu önemsemediği kimseye sahip olmadığı tespit edildiyse, sisteme bildirimler (PhantomReference benzeri yöntemlerle) sağlamasını sağlayın. WeakReference biraz yaklaşıyor, ancak kullanmadan önce güçlü bir referansa dönüştürülmeli; Eğer güçlü bir referans varken bir GC çevrimi meydana gelirse, hedefin faydalı olacağı varsayılır.
    2013-05-09 16: 47: 22Z
  5. Bence doğru cevap bu. Yıllar önce bir simülasyon yazdık. Her nasılsa, yanlışlıkla bir bellek sızıntısı yaratan mevcut durumu mevcut duruma bağladık. Son teslim tarihi nedeniyle, bellek sızıntısını asla çözemedik, ancak belgeleyerek bir "özellik" yaptık.
    2017-04-18 12: 16: 10Z

Cevap, tamamen görüşmecinin ne istediğini düşündüğüne bağlıdır.

Java sızıntısı yapmak pratikte mümkün mü? Elbette öyle ve diğer cevaplarda da pek çok örnek var.

Ancak, sorulmuş olabilecek birden fazla meta soru var mı?

  • Teorik olarak "mükemmel" bir Java uygulaması sızıntılara karşı savunmasız mıdır?
  • Aday teori ile gerçeklik arasındaki farkı anlıyor mu?
  • Aday çöp toplamanın nasıl çalıştığını anladı mı?
  • Veya çöp toplama işleminin ideal bir durumda nasıl çalışması gerekir?
  • Diğer dilleri yerel arabirimlerden arayabildiklerini biliyorlar mı?
  • Diğer dillerde hafıza sızdırmayı biliyorlar mı?
  • Aday, bellek yönetiminin ne olduğunu ve Java'daki sahnenin arkasında neler olduğunu biliyor mu?

Meta sorunuzu "Bu röportajda kullanabileceğim bir cevap nedir" olarak okuyorum. Ve böylece, Java yerine röportaj becerilerine odaklanacağım. Bir röportajda bir sorunun cevabını bilmeme durumunu tekrarlama olasılığınızın, Java sızıntısını nasıl yapacağınızı bilmeye ihtiyaç duyacağınıza inanıyorum. Yani, umarım, bu yardımcı olacaktır.

Görüşme yapmak için geliştirebileceğiniz en önemli becerilerden biri, soruları aktif olarak dinlemeyi öğrenmek ve görüşmeciyle niyetlerini çıkarmak için çalışmaktır. Bu, onların sorusunu istedikleri gibi cevaplamanıza izin vermekle kalmaz, aynı zamanda bazı hayati iletişim becerilerine sahip olduğunuzu gösterir. Pek çok eşit yetenekli geliştirici arasında bir seçim yapıldığında, her seferinde yanıt vermeden önce dinleyen, düşünen ve anlayan birini işe alacağım.

    
153
2015-01-27 12: 52: 33Z
  1. Bu soruyu her sorduğumda oldukça basit bir cevap arıyorum - sıraya girmeye devam et, nihayet db vb. gc'nin sizin için neler yapabileceğini ve yapamayacağını anlıyorlar. Görüşme yaptığınız işe bağlı olarak sanırım.
    2011-07-03 17: 59: 17Z
  2. Lütfen soruma bir bakın, teşekkürler stackoverflow.com/questions/31108772/…
    2015-06-29 06: 39: 44Z

JDBC 'yi anlamıyorsanız, aşağıdakiler oldukça anlamsız bir örnektir. Ya da en azından JDBC bir geliştiricinin, atmadan ya da Connection'u uygulamak yerine referanslarını kaybetmeden önce Statement, ResultSet ve finalize örneklerini kapatmasını beklemektedir.

 
void doWork()
{
   try
   {
       Connection conn = ConnectionFactory.getConnection();
       PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query
       ResultSet rs = stmt.executeQuery();
       while(rs.hasNext())
       {
          ... process the result set
       }
   }
   catch(SQLException sqlEx)
   {
       log(sqlEx);
   }
}

Yukarıdaki sorun, Connection nesnesinin kapalı olmaması ve dolayısıyla çöp toplayıcı gelip ulaşılamaz olduğunu görene kadar fiziksel bağlantının açık kalmasıdır. GC, finalize yöntemini çağırır, ancak finalize'u uygulamayan, en azından Connection.close'un uyguladığı gibi JDBC sürücüleri vardır. Sonuçta ortaya çıkan davranış, toplanan erişilemeyen nesneler nedeniyle bellek yeniden kazanılırken, Connection nesnesiyle ilişkilendirilmiş kaynakların (bellek dahil) basitçe geri kazanılmaması olabilir.

Connection’un finalize yönteminin her şeyi temizlemediği bir durumda, aslında veritabanı sunucusuyla fiziksel bağlantının birkaç çöp toplama döngüsü süreceğini, veritabanı sunucusu bağlantısının sonunda bağlantı kurulduğunu belirleyene kadar bulabilir. hayatta değil (eğer öyleyse) ve kapatılmalı.

JDBC sürücüsü finalize'u uygulasa bile, sonlandırma sırasında istisnalar atılabilir. Sonuçta ortaya çıkan davranış, şu anda "uyuyan" nesne ile ilişkili herhangi bir hafızanın yeniden kazanılmamasıdır, çünkü finalize'un yalnızca bir kez çağrılması garanti edilir.

Nesne sonlandırması sırasında istisnalar ile karşılaşan yukarıdaki senaryo, bellek sızıntısına neden olabilecek başka bir senaryo ile ilgilidir - nesne yeniden dirilişi. Nesne dirilişi, genellikle başka bir nesneden nesneye sonlandırılmasından dolayı güçlü bir referans oluşturularak kasıtlı olarak yapılır. Nesne dirilişi yanlış kullanıldığında, diğer bellek sızıntı kaynakları ile birlikte bir bellek sızıntısına neden olur.

Bir araya getirebileceğiniz çok sayıda örnek var - like

  • Yalnızca listeye eklediğiniz ve listeden silmediğiniz List örneğini yönetme (artık gerek duymadığınız öğelerden kurtulmanız gerekmesine rağmen) veya
  • Socket s veya File s’yi açma, ancak artık gerekmediğinde bunları kapatma (Connection sınıfını içeren yukarıdaki örneğe benzer şekilde).
  • Bir Java EE uygulamasını indirirken Singletons'ı boşaltmamak. Görünüşe göre, singleton sınıfını yükleyen Classloader, sınıfa referansı koruyacak ve dolayısıyla singleton örneği asla toplanmayacak. Uygulamanın yeni bir örneği dağıtıldığında, genellikle yeni bir sınıf yükleyici oluşturulur ve singleton nedeniyle eski sınıf yükleyici varlığını sürdürmeye devam eder.
127
2016-02-16 15: 38: 36Z
  1. Genellikle bellek sınırlarına ulaşmadan önce maksimum açık bağlantı sınırına erişirsiniz. Bana neden bildiğimi sorma ...
    2011-07-21 16: 23: 36Z
  2. Oracle JDBC sürücüsü bunu yapmakla ünlüdür.
    2013-08-20 16: 06: 38Z
  3. @ Hardwareguy Tüm SQL çağrılarımın son satırına Connection.close koyana kadar SQL veritabanlarının bağlantı sınırlarına çok çarptım. Ekstra eğlence için uzun süredir devam eden Orac'ı aradım.Veritabanına çok fazla çağrı yapılmasını önlemek için Java tarafındaki kilitleri gerektiren prosedürleri saklayın.
    2014-04-22 14: 46: 58Z
  4. @ Hardwareguy Bu ilginç, ancak tüm ortamlar için gerçek bağlantı sınırlarının aşılması gerekmiyor. Örneğin, weblogic uygulama sunucusu 11g'de konuşlandırılmış bir uygulama için, bağlantı kaçaklarını büyük ölçüde gördüm. Ancak, sızıntı yapan bağlantıları toplama seçeneği nedeniyle, bellek sızıntıları tanıtılırken veri tabanı bağlantıları mevcut kaldı. Tüm ortamlardan emin değilim.
    2014-09-22 18: 21: 03Z
  5. Deneyimlerime göre, bağlantıyı kapatsanız bile bir bellek sızıntısı oluyor. Önce ResultSet ve PreparedStatement'ı kapatmanız gerekir. OutOfMemoryErrors nedeniyle saatlerce, hatta günlerce iyi çalıştıktan sonra, bunu yapmaya başlayana kadar sürekli olarak çöken bir sunucum vardı.
    2017-08-31 08: 07: 33Z

Muhtemelen olası bir bellek sızıntısının en basit örneklerinden biri ve bunun nasıl önlenebileceği ArrayList.remove (int) 'nin uygulanmasıdır:

 
public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);
    elementData[--size] = null; // (!) Let gc do its work

    return oldValue;
}

Kendiniz uygularsanız, artık kullanılmayan dizi elemanını temizlemeyi düşünür müsünüz (elementData[--size] = null)? Bu referans büyük bir nesneyi canlı tutabilir ...

    
114
2015-09-01 04: 50: 05Z
  1. 'elementData [- size] = null;' temizliyor mu sanırım ...
    2011-07-21 18: 13: 39Z
  2. Burada bellek sızıntısı nerede?
    2011-07-22 16: 26: 40Z
  3. @ maniek: Bu kodun bellek sızıntısı gösterdiğini ima etmek istemedim. Yanlışlıkla nesnenin yanlışlıkla tutulmasını önlemek için bazen açık olmayan kodların gerekli olduğunu göstermek için alıntı yaptım.
    2011-07-23 13: 26: 32Z
  4. RangeCheck nedir (index); ?
    2014-06-09 18: 36: 22Z
  5. Joshua Bloch, bu örneğe Etkili Java'da Stacks'ın basit bir uygulamasını gösteren verdi. Çok iyi bir cevap.
    2015-06-27 02: 22: 14Z

Artık ihtiyaç duymadığınız nesnelere yapılan referansları sakladığınız zaman bir bellek sızıntısı yaşarsınız. Bellek sızıntılarının kendilerini nasıl gösterdiğine dair örnekler için Java programlarında bellek sızıntılarını kullanma bölümüne bakın. Java ve bu konuda yapabilecekleriniz.

    
66
2011-06-24 16: 18: 07Z
  1. Bunun bir "sızıntı" olduğuna inanmıyorum. Bu bir hata ve programın ve dilin tasarımından kaynaklanıyor. Sızıntı, etrafına sarkan bir nesne olabilir, kendisine referans vermez.
    2011-07-02 04: 31: 43Z
  2. @ Mehrdad: Bu, tüm dillere tam olarak uygulanmayan tek bir dar tanımdır. Herhangi bir bellek sızıntısının programın zayıf tasarımından kaynaklanan bir hata olduğunu iddia ediyorum.
    2011-07-10 02: 30: 30Z
  3. @ Mehrdad: ...then the question of "how do you create a memory leak in X?" becomes meaningless, since it's possible in any language. Bu sonucu nasıl çizdiğinizi anlamadım. Java'da herhangi bir tanımla bellek sızıntısı oluşturmanın daha az yolu vardır. Kesinlikle hala geçerli bir question.
    2011-07-10 14: 09: 59Z
  4. @ 31eee384: Programınız, asla kullanamayacağı nesneleri bellekte tutarsa, teknik olarak bellekte sızdırılmış bir bellektir. Daha büyük sorunların olması, bunu gerçekten değiştirmiyor.
    2011-07-21 18: 30: 08Z
  5. @ 31eee384: Olmayacağının bir gerçeğini biliyorsanız, o zaman olamaz. Program, yazıldığı gibi verilere hiçbir zaman erişmeyecek.
    2011-07-21 19: 13: 46Z

sun.misc.Unsafe sınıfı ile bellek sızıntısı yapabilirsiniz. Aslında bu hizmet sınıfı farklı standart sınıflarda kullanılır (örneğin, java.nio sınıflarında). Bu sınıfın örneğini doğrudan oluşturamazsınız , ancak bunu yapmak için yansıması kullanabilirsiniz .

Kod Eclipse IDE'de derlenmiyor - javac komutunu kullanarak derleyin (derleme sırasında uyarılar alırsınız)

 
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import sun.misc.Unsafe;


public class TestUnsafe {

    public static void main(String[] args) throws Exception{
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field f = unsafeClass.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        System.out.print("4..3..2..1...");
        try
        {
            for(;;)
                unsafe.allocateMemory(1024*1024);
        } catch(Error e) {
            System.out.println("Boom :)");
            e.printStackTrace();
        }
    }

}
    
50
2016-01-05 08: 37: 35Z
  1. Tahsis edilen bellek çöp toplayıcı için görünmez
    2011-07-11 15: 32: 15Z
  2. Ayrılan bellek ya da Java'ya ait değil.
    2011-07-17 18: 35: 11Z
  3. Bu güneş /oracle jvm belirli mi? Örneğin. bu IBM’de işe yarayacak mı?
    2011-07-21 16: 40: 44Z
  4. Hafıza kesinlikle "Java'ya" aittir, en azından i) başkası tarafından kullanılamayacağı, ii) Java uygulaması çıktığında sisteme geri döndü. JVM'nin hemen dışında.
    2016-06-02 03: 19: 19Z
  5. Bu, eclipse'de (en azından son sürümlerde) oluşturulacak, ancak derleyici ayarlarını değiştirmeniz gerekecek: in Window > Tercihler > Java > Derleyici > Hatalar /Uyarı > Kullanımdan kaldırılmış ve kısıtlanmış API, Yasaklı referansı (erişim kuralları) "Uyarı" olarak ayarladı.
    2016-06-02 03: 21: 13Z

Cevabımı buradan kopyalayabilirim: Java'da bellek sızıntısına neden olmanın en kolay yolu?

"Bilgisayar biliminde bir bellek sızıntısı (veya bu bağlamda sızıntı), bir bilgisayar programı belleği tükettiğinde ancak işletim sistemine geri veremediğinde ortaya çıkar." (Ara)

Kolay cevap: Yapamazsınız. Java otomatik bellek yönetimini yapar ve sizin için gerekmeyen kaynakları serbest bırakır. Bunun olmasını engelleyemezsin. HER ZAMAN kaynakları serbest bırakabilecek. Manuel bellek yönetimi olan programlarda bu farklıdır. Malloc () kullanarak C de bir miktar bellek bulamıyorsunuz. Belleği boşaltmak için, malloc'un döndürdüğü işaretçiye ve bunun üzerine free () çağrısı yapmanız gerekir. Ancak işaretçiyi artık kullanmıyorsanız (üzerine yazılmış veya ömür boyu aşılmış), o zaman ne yazık ki bu belleği serbest bırakamazsınız ve bu nedenle de bellek sızıntısı olur.

Şimdiye kadarki diğer cevaplar benim tanımımda gerçekten bellek sızıntısı değil. Hepsi hafızayı anlamsız şeyler ile hızlı bir şekilde doldurmayı amaçlıyor. Ancak, istediğiniz zaman hala oluşturduğunuz nesneleri kaldırabilir ve böylece belleği serbest bırakabilirsiniz - > SIZINTI YOK. acconrad'ın yanıtı oldukça yaklaşıyor, çünkü çözümü zorla çöp toplayıcıyı sadece "çökertmek" için itiraf etmeliyim sonsuz bir döngüde).

Uzun cevap: JNI kullanarak, Java için bir kütüphane yazarak bellek sızıntısı alabilirsiniz;ve bu nedenle bellek sızıntısı var. Bu kütüphaneyi çağırırsanız, java işleminiz bellek sızdırıyor. Veya JVM'de hatalar olabilir, böylece JVM belleği kaybeder. JVM'de muhtemelen böcekler vardır, çöp toplama işlemi bu kadar önemsiz olmadığı için bazı bilinenler bile olabilir, ancak o zaman hala bir hata vardır. Tasarım gereği bu mümkün değildir. Böyle bir hatadan etkilenen bazı java kodları istiyor olabilirsiniz. Üzgünüm bir tanesini bilmiyorum ve bir sonraki Java sürümünde artık bir hata olmayabilir.

    
44
2017-05-23 11: 47: 29Z
  1. Bu, bellek sızıntılarının son derece sınırlı (ve çok kullanışlı olmayan) bir tanımıdır. Pratik amaçlar için mantıklı olan tek tanım, "bir bellek sızıntısı, programın, tuttuğu verilerden sonra artık gerekli olmayan bellekte tutmaya devam ettiği herhangi bir koşuldur" dır.
    2014-01-23 19: 54: 30Z
  2. Söz konusu akconrad'ın yanıtı silindi mi?
    2016-01-05 08: 36: 40Z
  3. @ TomášZato: Hayır, değil. Şimdi bağlantı kurmak için yukarıdaki referansı çevirdim, böylece kolayca bulabilirsiniz.
    2016-01-05 11: 14: 26Z
  4. Nesne dirilişi nedir? Bir yıkıcı kaç defa çağırılır? Bu sorular bu cevabı nasıl ispatlar?
    2016-12-06 17: 08: 05Z
  5. Peki, GC’ye rağmen Java’da bir bellek sızıntısı oluşturabildiğinizden ve hala yukarıdaki tanımları yerine getirdiğinizden emin olun. Yalnızca ek bir veri yapısına sahip olun, harici erişime karşı korunur, böylece başka hiçbir kod kaldırmaz; program kodu içermez, çünkü program kodu kaldırmaz.
    2018-11-03 23: 37: 28Z

İşte http://wiki.eclipse.org/Performance_Bloopers # String.substring.28.29 .

 
public class StringLeaker
{
    private final String muchSmallerString;

    public StringLeaker()
    {
        // Imagine the whole Declaration of Independence here
        String veryLongString = "We hold these truths to be self-evident...";

        // The substring here maintains a reference to the internal char[]
        // representation of the original string.
        this.muchSmallerString = veryLongString.substring(0, 1);
    }
}

Alt dize, orijinalin çok daha uzun dizenin dahili temsilini ifade ettiği için, orijinal bellekte kalır. Bu nedenle, oyun sırasında bir StringLeaker'ınız olduğu sürece, sadece bir karakterli bir dizgede tuttuğunuzu düşünebilseniz bile, tüm orijinal dizgiyi bellekte saklayabilirsiniz.

İstenmeyen bir referansı orijinal dizeye kaydetmekten kaçınmanın yolu, şunun gibi bir şey yapmaktır:

 
...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...

Ekstra kötülük için, alt dizgiyi de .intern() olabilir:

 
...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...

Bunu yapmak, hem orijinal uzun dizeyi hem de türetilmiş alt dizeyi, StringLeaker örneği atıldıktan sonra bile bellekte tutar.

    
37
2011-07-21 19: 00: 35Z
  1. Buna bir bellek sızıntısı demem, per . muchSmallerString serbest bırakıldığında (StringLeaker nesnesi imha edildiğinden), uzun dize de serbest kalacaktır. Bellek sızıntısı dediğim şey, bu JVM örneğinde asla serbest bırakılamayan bellek. Bununla birlikte, kendinize belleği nasıl boşaltacağınızı gösterdiniz: this.muchSmallerString=new String(this.muchSmallerString). Gerçek bir bellek sızıntısı ile yapabileceğiniz hiçbir şey yoktur.
    2011-07-22 16: 45: 08Z
  2. @ rds, bu adil bir nokta. intern dışı durum, "bellek sızıntısı" ndan daha fazla "hafıza sürprizi" olabilir. .intern(), alt dizeyi yerleştirirken, kesinlikle, daha uzun olan dizeye yapılan referansın korunduğu ve serbest bırakılamadığı bir durum yaratır.
    2011-07-22 18: 42: 43Z
  3. substring () java7'de yeni bir dize oluşturur (yeni bir davranıştır)
    2013-03-22 12: 43: 46Z
  4. "Yorum"
    Substring'i () kendiniz yapmanız gerekmez: büyük bir girdinin küçük bir bölümünü eşleştirmek için bir regex eşleştiricisi kullanın ve "çıkarılan" dizesini taşıyın uzun süredir buralarda. Büyük girdi Java 6'ya kadar canlı kalıyor.
    2017-10-04 16: 46: 16Z

Herhangi bir sunucu kabında çalışan herhangi bir web uygulamasını alın (Tomcat, Jetty, Glassfish, her neyse ...). Uygulamayı art arda 10 veya 20 kez yeniden konuşlandırın (sunucunun otomatik dağıtım dizinindeki WAR'ye dokunmanız yeterli olabilir.

Hiç kimse bunu gerçekten test etmediyse, birkaç yeniden dağıtımdan sonra bir OutOfMemoryError alma şansınız yüksektir, çünkü uygulama kendisinden sonra temizlemeye özen göstermedi. Bu testte sunucunuzda bir hata bile bulabilirsiniz.

Sorun şu ki, kabın kullanım ömrü, uygulamanızın kullanım ömründen daha uzun. Konteynırın uygulamanızdaki nesnelere veya sınıflara sahip olabileceği tüm referansların çöp toplanabileceğinden emin olmanız gerekir.

Web uygulamanızın konuşlandırılmasından kurtulan tek bir referans varsa, ilgili sınıf yükleyici ve bunun sonucunda web uygulamanızın tüm sınıfları çöp toplanamaz.

Uygulamanız tarafından başlatılan başlıklar, ThreadLocal değişkenleri, günlük kaydı ekleri, sınıf yükleyici sızıntılarına neden olan olağan şüphelilerden bazılarıdır.

    
36
2011-07-03 22: 41: 17Z
  1. Bu, bellek sızıntısı nedeniyle değil, ancak sınıf yükleyici önceki sınıf kümesini kaldırmadığındandır. Bu nedenle, sunucuyu yeniden başlatmadan bir uygulama sunucusunu yeniden dağıtmanız önerilmez (fiziksel makine değil, uygulama sunucusu). Aynı sorunu WebSphere ile de gördüm.
    2018-05-22 05: 23: 52Z

Bunun GUI kodundaki yaygın bir örneği, bir widget /bileşen oluşturup bazı statik /uygulama kapsamındaki bir nesneye dinleyici eklerken ve ardından widget yok olduğunda dinleyiciyi çıkarmamaktır. Sadece bir bellek sızıntısı almakla kalmaz, aynı zamanda ne olayları dinlerseniz dinlersiniz, tüm eski dinleyicileriniz de denir.     

36
2016-02-16 15: 40: 24Z
  1. 2011-07-22 16: 34: 12Z

Belki JNI aracılığıyla harici yerel kod kullanarak?

Saf Java ile neredeyse imkansızdır.

Ancak, artık belleğe artık erişemediğiniz halde "standart" bir bellek sızıntısı söz konusudur, ancak yine de uygulamaya aittir. Bunun yerine kullanılmayan nesnelere referanslar saklayabilir veya daha sonra kapatmadan akışları açabilirsiniz.

    
34
2011-06-24 21: 48: 48Z
  1. "Bellek sızıntısı" tanımına bağlıdır. "Tutulan, ancak artık gerekli olmayan bellek" ise, Java’da yapılması kolaydır. "Tahsis edilmiş ancak kod tarafından erişilebilir değil" ise, biraz zorlaşır.
    2011-06-24 16: 15: 11Z
  2. @ Joachim Sauer - İkinci tip demek istedim. İlki yapmak oldukça kolaydır :)
    2011-06-2416: 17: 00Z
  3. "Saf Java ile neredeyse imkansız." Eh, benim deneyimim, özellikle buradaki tuzakların farkında olmayan insanlar tarafından önbellek uygulanması söz konusu olduğunda, bir başka şeydir.
    2011-06-24 16: 28: 12Z
  4. @ Rogach: her iki durumda da Joachim Sauer 'in yorum yaptığını gösteren +10 000 temsilcisi olan çeşitli yanıtlar üzerinde temel olarak +400 artış var bu çok mümkün. Bu yüzden "neredeyse imkansız" un hiç bir anlamı yok.
    2011-07-22 11: 51: 25Z

PermGen ve XML ayrıştırma ile ilgili olarak bir kez güzel bir "bellek sızıntısı" oldu. Karşılaştığımızı daha hızlı yapmak için kullandığımız XML ayrıştırıcısı (hangisinin olduğunu hatırlamıyorum) bir String.intern () etiketi yapmıştır. Müşterilerimizden biri, veri değerlerini XML özniteliklerinde veya metinlerinde değil, tagnames olarak saklamak konusunda harika bir fikir edindi, bu yüzden şöyle bir belgeye sahibiz:

 
<data>
   <1>bla</1>
   <2>foo</>
   ...
</data>

Aslında, sayıları kullanmadılar ancak günde 10-15 milyon oranında gelen ve benzersiz olan daha uzun metin kimliklerini (yaklaşık 20 karakter) kullandılar. Günde 200 MB'lık bir çöp yapar, bu asla tekrar gerekli değildir ve asla GCed (PermGen'de olduğundan). Permgen 512 MB olarak ayarlandı, bu nedenle hafıza dışı istisnaların (OOME) ulaşması yaklaşık iki gün sürdü ...

    
30
2016-02-16 15: 44: 24Z
  1. Sadece örnek kodunuzu nitelemek için: Numaraların (veya sayılarla başlayan dizelerin) XML'de öğe adları olarak kabul edilmediğini düşünüyorum.
    2011-07-03 00: 18: 34Z
  2. Haklısın sanırım. Bu sadece gösteri içindi.
    2011-07-04 11: 42: 08Z
  3. Bunun artık String Interning'in öbek üzerinde gerçekleştiği JDK 7+ için doğru olmadığını unutmayın. Detaylı bir yazı için bu makaleye bakın: java-performance.info /dize stajyer-in-java-6-7-8
    2017-04-18 19: 47: 08Z

Kısa bir süre önce log4j kaynaklı bir bellek sızıntısı durumuyla karşılaştım.

Log4j, Yuvalanmış Teşhis İçeriği (NDC) adlı bu mekanizmaya sahiptir Bu, harmanlanmış log çıkışını farklı kaynaklardan ayırt etmek için bir araçtır. NDC'nin çalıştığı ayrıntı düzeyi iş parçacıklarıdır, bu nedenle günlük çıktılarını farklı iş parçacıklarından ayrı olarak ayırt eder.

İş parçacığına özgü etiketleri saklamak için log4j'nin NDC sınıfı, Thread nesnesinin (iş parçacığı kimliği demek yerine) tarafından anahtarlanan bir Hashtable'ı kullanır ve böylece NDC etiketi, takılan tüm nesneleri bellekte kalana kadar iş parçacığının nesnesi de bellekte kalır. Web uygulamamızda, günlükleri tek bir istekten ayrı olarak ayırmak için bir istek kimliğiyle çıkış çıkışlarını etiketlemek için NDC kullanıyoruz. NDC etiketini bir iş parçacığıyla ilişkilendiren kap, yanıtı bir istekten döndürürken de kaldırır. Sorun, bir istek işleme sırasında, aşağıdaki kod gibi bir alt iş parçacığı doğduğunda ortaya çıktı:

 
pubclic class RequestProcessor {
    private static final Logger logger = Logger.getLogger(RequestProcessor.class);
    public void doSomething()  {
        ....
        final List<String> hugeList = new ArrayList<String>(10000);
        new Thread() {
           public void run() {
               logger.info("Child thread spawned")
               for(String s:hugeList) {
                   ....
               }
           }
        }.start();
    }
}    

Böylece bir NDC bağlamı, ortaya konulan satır içi iple ilişkilendirildi. Bu NDC bağlamı için anahtar olan thread nesnesi, hugeList nesnesinin üzerinde asılı olduğu satır içi dizisidir. Bu nedenle, iş parçacığı ne yaptığını yaptıktan sonra bile, hugeList'e yapılan referans NDC bağlamı Hastable tarafından canlı tutuldu, böylece bir bellek sızıntısına neden oldu.

    
23
2014-03-07 21: 29: 39Z
  1. Bu çok kötü. Tahsis eden bu günlük kütüphanesini kontrol etmelisiniz.Bir dosyaya giriş yaparken Sıfır belleği s: mentalog.soliveirajr.com
    2011-09-20 23: 10: 42Z
  2. NDC'ler ThreadLocal'da depolanır.
    2014-06-02 09: 59: 14Z
  3. + 1 slc4j /logback'te (aynı yazarın ardışık ürünleri) MDC ile benzer bir sorun olup olmadığını biliyor musunuz? Kaynağa derin bir dalış yapmak üzereyim ama önce kontrol etmek istedim. Her iki durumda da, bunu gönderdiğiniz için teşekkür ederiz.
    2014-07-02 21: 40: 49Z

Bellek sızıntısı nedir:

  • Bunun nedeni bir hata veya kötü tasarım.
  • Bu bir hafıza kaybı.
  • Zamanla daha da kötüye gider.
  • Çöp toplayıcı temizleyemiyor.

Tipik bir örnek:

Nesnelerin önbelleği işleri karıştırmak için iyi bir başlangıç ​​noktasıdır.

 
private static final Map<String, Info> myCache = new HashMap<>();

public void getInfo(String key)
{
    // uses cache
    Info info = myCache.get(key);
    if (info != null) return info;

    // if it's not in cache, then fetch it from the database
    info = Database.fetch(key);
    if (info == null) return null;

    // and store it in the cache
    myCache.put(key, info);
    return info;
}

Önbellekiniz büyür ve büyür. Ve çok geçmeden tüm veritabanı hafızaya gömülüyor. Daha iyi bir tasarım LRUMap kullanır (Sadece son kullanılan nesneleri önbellekte tutar).

Elbette, işleri daha karmaşık hale getirebilirsiniz:

  • ThreadLocal yapılarını kullanma.
  • daha fazla karmaşık referans ağacı ekleyerek .
  • veya 3. taraf kütüphanelerinin neden olduğu sızıntılar .

Genelde ne olur:

Bu Bilgi nesnesinin, başka nesnelere referansları olan diğer nesnelere referansları varsa. Bir şekilde, bunun da bir çeşit bellek sızıntısı olduğunu düşünebilirsiniz, (kötü tasarım nedeniyle).

    
23
2014-10-28 13: 18: 50Z

Kimsenin iç sınıf örnekleri kullanmamasının ilginç olduğunu düşündüm. Dahili bir sınıfınız varsa; doğal olarak içeren sınıfa yapılan bir referansı korur. Elbette teknik olarak bir bellek sızıntısı değildir, çünkü Java WILL sonunda temizler; ancak bu, sınıfların beklenenden daha uzun süre dayanmasına neden olabilir.

 
public class Example1 {
  public Example2 getNewExample2() {
    return this.new Example2();
  }
  public class Example2 {
    public Example2() {}
  }
}

Şimdi Örnek1'i ararsanız ve Örnek1'i atmadan bir Örnek2 alırsanız, doğası gereği bir Örnek1 nesnesine hala bir bağlantınız olur.

 
public class Referencer {
  public static Example2 GetAnExample2() {
    Example1 ex = new Example1();
    return ex.getNewExample2();
  }

  public static void main(String[] args) {
    Example2 ex = Referencer.GetAnExample2();
    // As long as ex is reachable; Example1 will always remain in memory.
  }
}

Ayrıca, belirli bir süreden daha uzun süre var olan bir değişkeniniz varsa; Java, her zaman var olacağını ve kodda artık ulaşılamıyorsa, onu asla temizlemeye çalışmayacağını varsaymaktadır. Ancak bu tamamen doğrulanmamış.

    
22
2011-07-09 01: 23: 55Z
  1. iç sınıflar nadiren sorun olur. Bunlar basit bir durumdur ve algılanması çok kolaydır. Söylenti de sadece bir söylentidir.
    2011-07-09 06: 54: 04Z
  2. "Söylenti" kuşak GC'nin nasıl çalıştığı hakkında yarı okunan birine benziyor. Uzun ömürlü ama şimdi erişilemeyen nesneler gerçekten bir süre etrafta dolanabilir ve bir süre yer tutabilir, çünkü JVM onları genç nesillerin dışına çıkardı, böylece her geçişi kontrol etmeyi bırakabilirdi. Tasarım gereği piddly "5000 temp string'leri temizle" kartlarından kaçacaklar. Ama ölümsüz değiller. Hala toplama için uygunlar ve VM RAM için bağlanırsa, sonunda tam bir GC taraması yapacak ve bu belleği silecektir.
    2013-08-16 20: 33: 26Z

Statik bir Harita oluşturun ve ona zor referanslar eklemeye devam edin. Bunlar asla GC'd olmayacak.

 
public class Leaker {
    private static final Map<String, Object> CACHE = new HashMap<String, Object>();

    // Keep adding until failure.
    public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}
    
18
2011-07-21 17: 32: 58Z
  1. Bu nasıl bir sızıntı? Tam olarak yapmanı istediğin şeyi yapıyor. Bu bir sızıntı ise, nesnelerin herhangi bir yerinde oluşturulması ve saklanması bir sızıntıdır.
    2011-07-21 19: 10: 53Z
  2. @Falmarri ile aynı fikirdeyim. Orada bir sızıntı göremiyorum, sadece nesneler yaratıyorsunuz. Kesinlikle 'removeFromCache' adında başka bir yöntemle ayırdığınız belleği 'geri alabilirsiniz'. Bir sızıntı, hafızayı geri kazanamayacağınız zamandır.
    2012-07-17 19: 08: 54Z
  3. Demek istediğim, belki de bir önbelleğe yerleştiren nesneler oluşturmaya devam eden, dikkatli olmadıklarında OOM hatasıyla sonuçlanabileceğidir.
    2012-07-17 20: 07: 12Z
  4. @ duffymo: Ama gerçekten sorulan soru bu değildi. Tüm hafızanızı kullanmanızla hiçbir ilgisi yoktur.
    2012-07-18 22: 10: 25Z
  5. Kesinlikle geçersiz. Harita koleksiyonunda sadece bir grup nesne topluyorsunuz. Referansları, Harita onları tuttuğu için saklanacak.
    2013-11-29 10: 36: 14Z

Herkes her zaman yerel kod yolunu unutur. İşte bir sızıntı için basit bir formül:

  1. Yerel yöntemi bildir.
  2. Yerel yöntemde malloc'u arayın. free'u arama.
  3. Yerel yöntemi çağırın.

Unutmayın, yerel koddaki bellek ayırmaları JVM yığınından gelir.

    
17
2011-07-21 20: 52: 40Z
  1. Gerçek bir hikayeye dayanarak.
    2015-09-02 09: 51: 46Z

Görüşme yapan kişi muhtemelen aşağıdaki kod gibi dairesel bir referans arıyordu (bu arada, artık eskiden beri sadece referans sayımı kullanan çok eski JVM'lerde bellek sızdırıyordu). Ancak bu oldukça belirsiz bir soru, bu nedenle JVM bellek yönetimi anlayışınızı göstermek için önemli bir fırsat.

 
class A {
    B bRef;
}

class B {
    A aRef;
}

public class Main {
    public static void main(String args[]) {
        A myA = new A();
        B myB = new B();
        myA.bRef = myB;
        myB.aRef = myA;
        myA=null;
        myB=null;
        /* at this point, there is no access to the myA and myB objects, */
        /* even though both objects still have active references. */
    } /* main */
}

Ardından, referans sayımında yukarıdaki kodun hafızaya sızacağını açıklayabilirsiniz. Ancak çoğu modern JVM artık referans sayımı kullanmıyor, çoğu bu hatıraları toplayacak olan bir çöp toplayıcı kullanıyor.

Daha sonra, bunun gibi temel bir yerel kaynağı olan bir Nesne oluşturmayı açıklayabilirsiniz:

 
public class Main {
    public static void main(String args[]) {
        Socket s = new Socket(InetAddress.getByName("google.com"),80);
        s=null;
        /* at this point, because you didn't close the socket properly, */
        /* you have a leak of a native descriptor, which uses memory. */
    }
}

Daha sonra bunun teknik olarak bir bellek sızıntısı olduğunu açıklayabilirsiniz, ancak sızıntıya, JVM'deki Java kodunuz tarafından serbest bırakılmayan temel yerel kaynakları tahsis eden yerel kod neden olur.

Günün sonunda, modern bir JVM ile, JVM'nin farkındalığının normal kapsamı dışında yerel bir kaynak tahsis eden bir miktar Java kodu yazmanız gerekir.

    
17
2017-10-13 19: 09: 24Z

Bu sınıfın finalize yönteminde yeni bir sınıf örneği oluşturarak hareketli bir bellek sızıntısı oluşturabilirsiniz. Sonlandırıcı birden fazla örnek oluşturursa bonus puan. Yığın boyutunuza bağlı olarak tüm yığını birkaç saniye ile birkaç dakika arasında bir süre içinde sızdıran basit bir program:

 
class Leakee {
    public void check() {
        if (depth > 2) {
            Leaker.done();
        }
    }
    private int depth;
    public Leakee(int d) {
        depth = d;
    }
    protected void finalize() {
        new Leakee(depth + 1).check();
        new Leakee(depth + 1).check();
    }
}

public class Leaker {
    private static boolean makeMore = true;
    public static void done() {
        makeMore = false;
    }
    public static void main(String[] args) throws InterruptedException {
        // make a bunch of them until the garbage collector gets active
        while (makeMore) {
            new Leakee(0).check();
        }
        // sit back and watch the finalizers chew through memory
        while (true) {
            Thread.sleep(1000);
            System.out.println("memory=" +
                    Runtime.getRuntime().freeMemory() + " / " +
                    Runtime.getRuntime().totalMemory());
        }
    }
}
    
16
2011-07-22 08: 05: 07Z

Henüz kimsenin bunu söylemediğini sanmıyorum: sonlandırmayı geçersiz kılarak bir nesneyi diriltebilirsiniz() finalize () yöntemi, bunun bir referansını bir yerde saklar. Çöp toplayıcı nesneye yalnızca bir kez çağrılacaktır, bundan sonra nesne asla imha edilmeyecektir.

    
15
2011-07-03 17: 36: 58Z
  1. Bu doğru değil. finalize() çağrılmayacak, ancak daha fazla referans olmadığında nesne toplanacak. Çöp toplayıcıya da 'çağrılmadı'.
    2011-07-05 19: 38: 11Z
  2. Bu cevap yanıltıcıdır, finalize() yöntemi JVM tarafından yalnızca bir kez çağrılabilir, ancak bu, nesne yeniden dirilirse toplanan yeniden çöplük olamayacağı anlamına gelmez ve sonra tekrar başvurdu. finalize() yönteminde kaynak kapatma kodu varsa, bu kod tekrar çalıştırılmaz, bu bellek sızıntısına neden olabilir.
    2012-12-19 11: 36: 38Z

Son zamanlarda daha ince bir kaynak sızıntısı ile karşılaştım. Kaynakları sınıf yükleyicinin getResourceAsStream üzerinden açtık ve girdi akışı tanıtıcılarının kapatılmadığı ortaya çıktı.

Uhm, ne kadar aptal diyebilirsin.

Bunu ilginç yapan şey şu: bu yolla, JVM'nin yığından ziyade, temel sürecin yığın hafızasını sızdırabilirsiniz.

İhtiyacınız olan tek şey, içinde Java kodundan referans alınacak bir dosyanın bulunduğu bir jar dosyası. Jar dosyası büyüdükçe, daha hızlı bellek ayrılır.

Aşağıdaki sınıfla böyle bir kavanozu kolayca oluşturabilirsiniz:

 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class BigJarCreator {
    public static void main(String[] args) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar")));
        zos.putNextEntry(new ZipEntry("resource.txt"));
        zos.write("not too much in here".getBytes());
        zos.closeEntry();
        zos.putNextEntry(new ZipEntry("largeFile.out"));
        for (int i=0 ; i<10000000 ; i++) {
            zos.write((int) (Math.round(Math.random()*100)+20));
        }
        zos.closeEntry();
        zos.close();
    }
}

Sadece BigJarCreator.java adlı bir dosyaya yapıştırın, derleyin ve komut satırından çalıştırın:

 
javac BigJarCreator.java
java -cp . BigJarCreator

Et voilà: Geçerli çalışma dizininizde içinde iki dosya bulunan bir kavanoz arşivi buluyorsunuz.

İkinci bir sınıf oluşturalım:

 
public class MemLeak {
    public static void main(String[] args) throws InterruptedException {
        int ITERATIONS=100000;
        for (int i=0 ; i<ITERATIONS ; i++) {
            MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");
        }
        System.out.println("finished creation of streams, now waiting to be killed");

        Thread.sleep(Long.MAX_VALUE);
    }

}

Bu sınıf temelde hiçbir şey yapmaz, ancak referans alınamayan InputStream nesneleri oluşturur. Bu nesneler derhal toplanacak çöplerdir ve bu nedenle yığın boyutuna katkıda bulunmazlar. Örneğimiz için mevcut bir kaynağı bir jar dosyasından yüklemek önemlidir ve burada büyüklüğü önemlidir!

Şüpheniz varsa, yukarıdaki sınıfı derlemeye ve başlatmaya çalışın, ancak düzgün bir yığın boyutu (2 MB) seçtiğinizden emin olun:

 
javac MemLeak.java
java -Xmx2m -classpath .:big.jar MemLeak

Burada bir OOM hatasıyla karşılaşmazsınız, referanslar tutulmadığından, yukarıdaki örnekte İTERASYONLARI ne kadar büyük seçerseniz seçin uygulama çalışmaya devam edecektir. İşleminizdeki bellek tüketimi (üstte görünür (RES /RSS)) veya işlem gezgini), uygulama wait komutuna gelmedikçe artar. Yukarıdaki kurulumda, yaklaşık 150 MB'lık bir hafıza tahsis edecektir.

Uygulamanın güvenli şekilde oynatılmasını istiyorsanız, oluşturulduğu yerdeki giriş akışını kapatın:

 
MemLeak.class.getClassLoader().getResourceAsStream("resource.txt").close();

ve işleminiz yineleme sayısından bağımsız olarak 35 MB'ı aşmayacak.

Oldukça basit ve şaşırtıcı.

    
15
2012-09-12 20: 07: 37Z

Pek çok insanın önerdiği gibi, Kaynak Kaçakları'nın JDBC örnekleri gibi neden olduğu oldukça kolaydır. Gerçek Bellek sızıntıları biraz daha zordur - özellikle JVM'nin kırık parçalarına güvenmiyorsanız, bunu sizin için yapmak ...

Çok geniş bir alanı kaplayan ve daha sonra bunlara erişemeyen nesneler oluşturma fikirleri de gerçek bellek sızıntısı değildir. Eğer hiçbir şey erişemezse, o zaman çöp toplanacak ve eğer bir şey erişebilecekse o zaman bu bir sızıntı değil ...

Gerçi çalışmak için kullanmanın bir yolu - ve hala devam edip etmediğini bilmiyorum - üç derin bir dairesel zincire sahip olmaktır. A Nesnesinin, B Nesnesine bir referansı olduğu gibi, B Nesnesi, C Nesnesine bir referansa sahiptir ve C Nesnesinin, A Nesnesine bir referansı vardır. GC, iki derin zincirin - A < ; B - A ve B başka hiçbir şey tarafından erişilemiyorsa, ancak üç yollu zinciri kaldıramazsa, güvenle toplanabilir ...

    
14
2011-07-21 17: 55: 51Z
  1. Şimdi bir süre için durum. Modern GC'ler, dairesel referansların nasıl kullanılacağını bilir.
    2013-06-20 23: 01: 28Z

Hafızanın sızacağı birçok farklı durum var. Karşılaştığım bir harita, açığa çıkmaması ve başka yerlerde kullanılmaması gereken bir harita ortaya çıkardı.

 
public class ServiceFactory {

private Map<String, Service> services;

private static ServiceFactory singleton;

private ServiceFactory() {
    services = new HashMap<String, Service>();
}

public static synchronized ServiceFactory getDefault() {

    if (singleton == null) {
        singleton = new ServiceFactory();
    }
    return singleton;
}

public void addService(String name, Service serv) {
    services.put(name, serv);
}

public void removeService(String name) {
    services.remove(name);
}

public Service getService(String name, Service serv) {
    return services.get(name);
}

// the problematic api, which expose the map.
//and user can do quite a lot of thing from this api.
//for example, create service reference and forget to dispose or set it null
//in all this is a dangerous api, and should not expose 
public Map<String, Service> getAllServices() {
    return services;
}

}

// resource class is a heavy class
class Service {

}
    
11
2011-07-10 07: 46: 03Z

İplikler sonlanıncaya kadar toplanmaz. Çöp toplamanın kökleri olarak hizmet ederler. Bunlar, sadece onları unutmak veya onlara yapılan referansları silmek suretiyle geri alınmayacak olan birkaç nesneden biridir.

Düşünün: çalışan bir iş parçacığını sonlandırmak için temel desen iş parçacığı tarafından görülen bazı koşul değişkenlerini ayarlamaktır. İplik değişkeni periyodik olarak kontrol edebilir ve bunu sonlandırmak için bir sinyal olarak kullanabilir. Değişken volatile bildirilmezse, o zaman değişkende değişiklik iplik tarafından görülmeyebilir, bu yüzden sonlandırılması bilemez. Veya bazı iş parçacıklarının paylaşılan bir nesneyi güncellemek isteyip istemediğini, ancak onu kilitlemeye çalışırken kilitlenmeyeceğini hayal edin.

Yalnızca bir avuç iş parçacığınız varsa, bu hatalar muhtemelen açık olacaktır çünkü programınız düzgün çalışmayı bırakacaktır. Gerektiğinde daha fazla iş parçacığı oluşturan bir iş parçacığı havuzunuz varsa, kullanılmayan /sıkışmış iş parçacıkları fark edilmeyebilir ve süresiz olarak birikerek bellek sızıntısına neden olabilir. İplikler uygulamanızdaki diğer verileri kullanması muhtemeldir, bu nedenle doğrudan referans aldıkları herhangi bir şeyin toplanmasını da önler.

Oyuncak örneği olarak:

 
static void leakMe(final Object object) {
    new Thread() {
        public void run() {
            Object o = object;
            for (;;) {
                try {
                    sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {}
            }
        }
    }.start();
}

İstediğiniz kadar System.gc()'u arayın, ancak leakMe'a iletilen nesne asla ölmez.

(* düzenlenmiş *)

    
11
2017-05-23 12: 02: 58Z
  1. Kod sızdırılmış, kod sıkışmış ancak hala kapsamda.
    2013-09-26 22: 33: 50Z
  2. @ Spidey Hiçbir şey "sıkışmış" değil. Çağıran yöntem derhal geri döner ve iletilen nesne hiçbir zaman geri kazanılmaz. Bu kesinlikle bir sızıntı.
    2013-09-26 23: 39: 39Z
  3. Programınızın kullanım ömrü boyunca "koşuyor" (veya uyuyorsa olsun) iş parçacığına sahip olacaksınız. Bu benim için bir sızıntı olarak sayılmaz. Bir havuzun yanı sıra, tamamen kullanmasanız bile bir sızıntı sayılmaz.
    2013-09-27 01: 24: 42Z
  4. @ Spidey "Programınızın ömrü boyunca bir şey olacak. Bu bana bir sızıntı olarak sayılmaz." Kendini duyuyor musun?
    2013-09-27 01: 39: 58Z
  5. @ Spidey İşlemin sızdırılmadığını bildiği hafızayı sayarsanız, buradaki tüm cevaplar yanlıştır, çünkü işlem her zaman sanal sayfalarında hangi sayfaları izler? adres alanı eşleştirildi. İşlem sona erdiğinde, işletim sistemi, sayfaları boş sayfa yığınına geri koyarak tüm sızıntıları temizler. Bir sonraki uç noktaya geçmek için, RAM yongalarındaki veya diskteki takas alanındaki fiziksel bitlerin hiçbirinin fiziksel olarak yanlış yerleştirilmediğini veya tahrip edilmediğine dikkat çekerek herhangi bir sızıntının ölmesine neden olabilir, böylece bilgisayarı kapatabilirsiniz. ve herhangi bir sızıntıyı gidermek için tekrar açık.
    2013-09-27 18: 00: 38Z

Geçerli bir örneğin, ThreadLocal değişkenlerini thread'ların toplandığı bir ortamda kullanabileceğini düşünüyorum.

Örneğin, Servlets'teki ThreadLocal değişkenlerini kullanarak diğer web bileşenleriyle iletişim kurmak, iş parçacığı tarafından oluşturulan iş parçacıklarını kullanmak ve boştakileri iş başında tutmakbir havuz. ThreadLocal değişkenleri, eğer doğru şekilde temizlenmemişse, muhtemelen aynı web bileşeni değerlerinin üzerine yazana kadar orada yaşayacak.

Elbette, bir kere tanımlandıktan sonra, sorun kolayca çözülebilir.

    
10
2011-07-03 06: 49: 51Z

Büyük potansiyel bellek sızıntıları oluşturmanın bir başka yolu, Map.Entry<K,V>’un TreeMap’una referans tutmaktır.

Bunun neden sadece TreeMap s için geçerli olduğunu söylemek zordur, ancak uygulamaya bakıldığında bunun nedeni şudur: TreeMap.Entry, kardeşlerine referanslar saklar, eğer bir TreeMap toplanmaya hazırsa, ancak başka bir sınıf Map.Entry’undan birine atıfta bulunur, ardından tüm Harita hafızaya alınır.


Gerçek hayat senaryosu:

Büyük bir TreeMap veri yapısı döndüren bir db sorgusu olduğunu hayal edin. Öğeler genellikle ekleme öğesi siparişi korunurken TreeMap s kullanır.

 
public static Map<String, Integer> pseudoQueryDatabase();

Sorgu birçok kez çağrıldıysa ve her sorgu için (bu nedenle, her döndürülen her Map için) bir yerde Entry saklarsanız, bellek sürekli büyümeye devam eder.

Aşağıdaki sarmalayıcı sınıfını göz önünde bulundurun:

 
class EntryHolder {
    Map.Entry<String, Integer> entry;

    EntryHolder(Map.Entry<String, Integer> entry) {
        this.entry = entry;
    }
}

Uygulama:

 
public class LeakTest {

    private final List<EntryHolder> holdersCache = new ArrayList<>();
    private static final int MAP_SIZE = 100_000;

    public void run() {
        // create 500 entries each holding a reference to an Entry of a TreeMap
        IntStream.range(0, 500).forEach(value -> {
            // create map
            final Map<String, Integer> map = pseudoQueryDatabase();

            final int index = new Random().nextInt(MAP_SIZE);

            // get random entry from map
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                if (entry.getValue().equals(index)) {
                    holdersCache.add(new EntryHolder(entry));
                    break;
                }
            }
            // to observe behavior in visualvm
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

    }

    public static Map<String, Integer> pseudoQueryDatabase() {
        final Map<String, Integer> map = new TreeMap<>();
        IntStream.range(0, MAP_SIZE).forEach(i -> map.put(String.valueOf(i), i));
        return map;
    }

    public static void main(String[] args) throws Exception {
        new LeakTest().run();
    }
}

Her pseudoQueryDatabase() çağrısından sonra, map örneklerinin toplanmaya hazır olması gerekir, ancak en az bir Entry başka bir yerde depolandığı için olmayacak.

jvm ayarlarınıza bağlı olarak, uygulama OutOfMemoryError nedeniyle erken aşamada çökebilir.

Bu visualvm grafiğinden belleğin nasıl büyümeye devam ettiğini görebilirsiniz.

 Bellek dökümü - TreeMap

Aynısı karma veri yapısında da olmaz (HashMap).

Bu, HashMap kullanırken grafiktir.

 Bellek dökümü - HashMap

Çözüm? Yalnızca Map.Entry’u kaydetmek yerine, anahtarı /değeri doğrudan (doğrudan yaptığınız gibi) kaydedin.


Daha kapsamlı bir kriter yazdım burayı .

    
10
2018-06-18 07: 28: 30Z
kaynak yerleştirildi İşte