Java sınıfları boşaltma?


174

Bir masaüstü uygulamasının dinamik olarak konuşmam gereken bir AppServer'dan sınıf yüklemeye başlayabilmesi için özel bir sınıf yükleyicim var. Bunu yapmak için gereken kavanoz miktarı saçma olduğundan (onları göndermek istiyorsak) yaptık. Ayrıca AppServer kitaplığından çalışma zamanında sınıfları dinamik olarak yüklemezsek sürüm sorunlarımız da vardır.

Şimdi, iki farklı AppServer'la konuşmam gereken bir soruna çarptım ve ilk olarak yüklediğim sınıflara bağlı olarak kötü bir şekilde kırılabileceğimi buldum ... JVM'yi gerçekten öldürmeden sınıfın boşaltılmasını zorlamanın herhangi bir yolu var mı?

Umarım bu mantıklıdır


Her kavanoz için bir sınıf yükleyiciniz var mı? OSGI, yükleyiciyi boşaltmak için arşivleri nasıl kaplar? Classloader sınıfında boşaltma API'sı yok mu?
hetaoblog

Yanıtlar:


190

Bir Sınıfın kaldırılabilmesinin tek yolu, kullanılan Sınıf Yükleyicinin çöp toplanmasıdır. Bu, her bir sınıfa ve sınıf yükleyiciye yapılan göndermelerin dodonun yoluna gitmesi gerektiği anlamına gelir.

Sorununuzun olası çözümlerinden biri, her jar dosyası için bir Classloader ve sınıfların gerçek yüklemesini belirli Jar sınıf yükleyicilerine devreten her AppServers için bir Classloader'a sahip olmaktır. Bu şekilde, her Uygulama sunucusu için jar dosyasının farklı sürümlerini gösterebilirsiniz.

Bu önemsiz değil. Her paket farklı bir sınıf yükleyiciye sahip olduğundan ve bağımlılıklar platform tarafından çözüldüğünden, OSGi platformu bunu yapmaya çalışır. Belki de iyi bir çözüm ona bir göz atmak olabilir.

OSGI kullanmak istemiyorsanız, olası bir uygulama her JAR dosyası için bir JarClassloader sınıfı örneği kullanmak olabilir .

Ve Classloader'ı genişleten yeni bir MultiClassloader sınıfı oluşturun. Bu sınıf dahili olarak bir JarClassloader dizisine (veya List) sahip olur ve defineClass () yönteminde bir tanım bulunana veya bir NoClassDefFoundException atılana kadar tüm dahili sınıf yükleyiciler arasında yineleme yapılır. Sınıfa yeni JarClassloader eklemek için birkaç erişimci yöntemi sağlanabilir. Bir MultiClassLoader için internette birkaç olası uygulama vardır, bu yüzden kendi yazmanız bile gerekmeyebilir.

Sunucuya her bağlantı için bir MultiClassloader başlatırsanız, prensipte her sunucunun aynı sınıfın farklı bir sürümünü kullanması mümkündür.

MultiClassloader fikrini, kullanıcı tanımlı komut dosyaları içeren sınıfların bellekten yüklenmesi ve kaldırılması gereken bir projede kullandım ve oldukça iyi çalıştı.


31
Ayrıca, java.sun.com/docs/books/jls/second_edition/html/… 'a göre sınıfların boşaltılmasının bir optimizasyon olduğunu ve JVM uygulamasına bağlı olarak gerçekte gerçekleşebileceğini veya olmayabileceğini unutmayın.

5
OSGi'ye daha kolay ve hafif bir alternatif olarak, modül başına sınıf yükleyici (bir grup kavanoz) ile modüler sınıf yükleme JBoss Modüllerini deneyin .
Ondra Žižka

42

Evet, sınıfları yüklemenin ve daha sonra “boşaltmanın” yolları vardır. İşin püf noktası, üst düzey sınıf yükleyici (Sistem sınıfı yükleyici) ile uygulama sunucularının sınıf yükleyicileri arasında bulunan kendi sınıf yükleyicinizi uygulamak ve uygulama sunucusunun sınıf yükleyicilerinin sınıf yüklemesini üst yükleyicilere devretmesini ummaktır. .

Bir sınıf, paketi, adı ve başlangıçta yüklediği sınıf yükleyicisi tarafından tanımlanır. JVM başlatılırken ilk yüklenen "proxy" sınıf yükleyiciyi programlayın. İş Akışı:

  • Program başlar ve gerçek "ana" sınıfı bu proxy sınıf yükleyicisi tarafından yüklenir.
  • Daha sonra normal olarak yüklenen her sınıf (yani, hiyerarşiyi kırabilecek başka bir sınıf yükleyici uygulaması aracılığıyla değil) bu sınıf yükleyiciye devredilecektir.
  • Proxy classloader delegeler java.xve sun.xsistem classloader için (bunlar olmamalıdır sistem classloader dışında herhangi classloader yoluyla yüklenebilir).
  • Değiştirilebilen her sınıf için bir sınıf yükleyiciyi (sınıfı gerçekten yükleyen ve ana sınıf yükleyiciye devretmeyen) başlatır ve bunu yükler.
  • Sınıfların paketini / adını anahtarlar ve sınıf yükleyiciyi veri yapısında (yani Hashmap) değerler olarak saklayın.
  • Proxy sınıf yükleyicisi daha önce yüklenmiş bir sınıf için her istek aldığında, sınıfı daha önce depolanan sınıf yükleyiciden döndürür.
  • Sınıf yükleyiciniz tarafından bir sınıfın bayt dizisini bulmak (veya anahtar / değer çiftini veri yapınızdan "silmek") ve değiştirmek istediğiniz takdirde sınıfı yeniden yüklemek yeterli olacaktır.

Doğru bir ClassCastException veya LinkageError vb. Gelmemelidir .

Sınıf yükleyici hiyerarşileri hakkında daha fazla bilgi için (evet, tam olarak burada uyguladığınız şey; -) Ted Neward'ın "Sunucu Tabanlı Java Programlama" konusuna bakın - bu kitap, istediğinize çok benzer bir şey uygulamama yardımcı oldu.


3
Yorum bırakmadan bu cevap için -1'i işaretleyen insanları anlamıyorum. Benim için iyi görünüyor. Belki de ClassLoadersınıf başına bir tane olmak biraz fazladır, ClassLoaderher JAR için bir tane mantıklıdır. Önerilen şemada sınıf yüklemesini nasıl zorlayacağınız konusunda daha spesifik olabilir misiniz? Örneğin, ClassLoaderA tarafından yüklenen sınıf örneklerinin ClassLoaderB tarafından yüklenen örneklerle yönlendirilmediğini nasıl garanti edebilirim?
dma_k

@ dma_k tam olarak, cevap iyidir, ancak bahsettiğiniz önemli noktalara dokunmuyor.
zinking

@Georgi bunun için örnek oluşturabileceğimiz / yeniden kullanabileceğimiz mevcut bir uygulama var mı?
Kızak

1
Eğer örnek java kodu sağlayabilir eğer çok çok yararlı olacaktır.
Sriharsha grv

@ Sriharshag.rv Bunu denediniz ve bir örnek uyguladınız mı?
niaomingjian

17

Sınıf yükleyiciyi GCing yapmadan bireysel sınıfları boşaltmanın mümkün olduğu özel bir sınıf yükleyici yazdım. Kavanoz Sınıfı Yükleyici


Tıkır tıkır çalışıyor :). Bir jar dosyasının tüm sınıf dosyalarını kaldırmak için herhangi bir yöntem var mı?
Ercksen

Maalesef şu anda değil. Ama ona bakacağız. Gelecek sürümlerde olabilir.
Kamran

Bu arada, biraz çözüm buldum. JarClassLoaderYüklediğiniz her kavanoz dosyası için bir tane varsa getLoadedClasses(), onu çağırabilir , ardından her birini tekrarlayabilir ve boşaltabilirsiniz.
Ercksen

12

Sınıf doldurucular zor bir sorun olabilir. Özellikle birden fazla sınıf yükleyici kullanıyorsanız ve etkileşimleri net ve titizlikle tanımlanmadıysa sorun yaşayabilirsiniz. Aslında gidecek bir sınıf kaldırmak için gitmek için gitmek istediğiniz herhangi bir sınıf (ve onların örnekleri) tüm referansları kaldırmak zorunda düşünüyorum.

Bu tür bir şey yapması gereken çoğu insan OSGi kullanıyor . OSGi gerçekten güçlü ve şaşırtıcı derecede hafif ve kullanımı kolay,


7

Bir ClassLoader'ı kaldırabilirsiniz, ancak belirli sınıfları kaldıramazsınız. Daha spesifik olarak, kontrolünüz altında olmayan bir ClassLoader içinde oluşturulan sınıfları kaldıramazsınız.

Mümkünse, kaldırmanız için kendi ClassLoader'ınızı kullanmanızı öneririm.


4

Sınıflar, ClassLoader örneklerine örtük güçlü bir başvuruya sahiptir veya bunun tersi de geçerlidir. Java nesnelerinde olduğu gibi toplanan çöplerdir. Araçlar arabirimine veya benzerine çarpmadan, sınıfları tek tek kaldıramazsınız.

Her zamanki gibi bellek sızıntıları alabilirsiniz. Sınıflarınızdan birine veya sınıf yükleyicinize herhangi bir güçlü referans, her şeyi sızdırır. Bu, ThreadLocal, java.sql.DriverManager ve java.beans öğelerinin Sun uygulamaları ile gerçekleşir.


-1

Boşaltma dersinin JConsole'de falan çalışıp çalışmadığını canlı olarak izliyorsanız java.lang.System.gc(), sınıf boşaltma mantığınızın sonuna da eklemeyi deneyin . Çöp Toplayıcı'yı açıkça tetikler.


3
Dikkat: System.gc () gerekli değildir GC'yi arayın. Sadece jvm'den başlatmasını ister, ancak zorlamaz. Ve IME genellikle GC'yi başlatmaz: - \
Juh_
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.