2012-06-30 19 views
5

Java'da classLoaders ile oynamaktaydım ve garip bir şey farkettim. Bir classLoader bir kavanozdan bir sınıf yüklerse, classLoader'ınızı ilgisizseniz bile, bu jar süresiz olarak kilitlenir.Java sınıfı Kilitli kavanozlara sahipLoader ikilemi

Aşağıdaki örnekte, kavanoz HelloWorld adında bir sınıf içerir. Yaptığım şey, kavanozda bulunan sınıfı dinamik olarak jar'e ekleyen classLoader aracılığıyla yüklemeyi denemektir. skip'u true olarak ayarlarsanız ve Class.forName'u çağırmazsanız, jar'i silebilirsiniz, ancak atlamıyorsanız ve classLoader (classLoader = null) ile ilgisizseniz bile, kavanoz JVM çıkana kadar silinemez.

Neden?

PS: Ben java 6 kullanıyorum ve kod test amaçlı Java 7'de

package loader; 

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLClassLoader; 

public class TestClassLoader { 

    private URLClassLoader classLoader; 

    public TestClassLoader() throws MalformedURLException, IOException { 
     System.out.println("Copying jar"); 
     if (copyJar()) { 
      System.out.println("Copying SUCCESS"); 
      performFirstCheck(); 
     } else { 
      System.out.println("Copying FAILED"); 
     } 
    } 

    public static void main(String[] args) throws IOException { 
     System.out.println("Test started"); 
     TestClassLoader testClassLoader = new TestClassLoader(); 
     System.out.println("Bye!"); 
    } 

    public void performFirstCheck() throws IOException { 
     System.out.println("Checking class HelloWorld does not exist"); 
     if (!checkClassFound(TestClassLoader.class.getClassLoader(), false)) { 
      System.out.println("Deleting jar"); 
      deleteJar(); 
      System.out.println("First Check SUCCESS"); 
      performSecondCheck(); 
     } else { 
      System.out.println("First Check FAILED"); 
     } 
    } 

    private void performSecondCheck() throws IOException { 
     System.out.println("Copying jar"); 
     if (copyJar()) { 
      System.out.println("Copying SUCCESS"); 
      createClassLoaderAndCheck(); 
     } else { 
      System.out.println("Copying FAILED"); 
     } 
    } 

    private void createClassLoaderAndCheck() throws MalformedURLException { 
     System.out.println("Creating classLoader"); 
     createClassLoader(); 
     System.out.println("Checking class HelloWorld exist"); 
     if (checkClassFound(classLoader, true)) { 
      System.out.println("Second Check SUCCESS"); 
        classLoader = null; 
      System.out.println("Deleting jar"); 
      if (deleteJar()) { 
       System.out.println("Deleting SUCCESS"); 
      } else { 
       System.out.println("Deleting FAILED"); 
      } 
     } else { 
      System.out.println("Second Check FAILED"); 
     } 
    } 

    public void createClassLoader() throws MalformedURLException { 
     URL[] urls = new URL[1]; 
     File classFile = new File("C:\\Users\\Adel\\Desktop\\classes.jar"); 
     urls[0] = classFile.toURI().toURL(); 
     classLoader = new URLClassLoader(urls); 
    } 

    public boolean checkClassFound(ClassLoader classLoader, boolean skip) { 
     if (skip) { 
      System.out.println("Skiping class loading"); 
      return true; 
     } else { 
      try { 
       Class.forName("HelloWorld", true, classLoader); 
       return true; 
      } catch (ClassNotFoundException e) { 
       return false; 
      } 
     } 
    } 

    public URLClassLoader getClassLoader() { 
     return classLoader; 
    } 

    public boolean copyJar() throws IOException { 
     File sourceJar = new File("C:\\Users\\Adel\\Desktop\\Folder\\classes.jar"); 
     File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar"); 
     if (destJar.exists()) { 
      return false; 
     } else { 
      FileInputStream finput = new FileInputStream(sourceJar); 
      FileOutputStream foutput = new FileOutputStream(destJar); 
      byte[] buf = new byte[1024]; 
      int len; 
      while ((len = finput.read(buf)) > 0) { 
       foutput.write(buf, 0, len); 
      } 
      finput.close(); 
      foutput.close(); 
      return true; 
     } 
    } 

    public boolean deleteJar() { 
     File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar"); 
     return destJar.delete(); 
    } 

} 
+0

için sunduğunu bildiren dinamik sınıf yüklemesini seçtik. Bir geçici çözüm veya açıklama ister misiniz? ? – esej

+1

@esej Zaten ikisini de buldum, cevabımı kontrol etmeye ve size fikirlerini paylaşmaya özen gösterdim. –

cevap

8

Bir yanıt ve bir çözüm buldum. Bu article ve bu şaşırtıcı ilgili article dayanarak

, bunun süresiz hafızasında sınıf önbelleğe tutar çünkü Class.forName(className, true, classLoader) kullanmak kötü bir alışkanlıktır.

classLoader = null; 
System.gc(); 

Umut bu başkalarına yardım:

çözüm classLoader unreference classLoader.loadClass(clasName) yerine, o zamanlar, bitmiş kullanmak ve kullanan çöp toplayıcı aramak oldu! :)

Arkaplan Bilgisi:

Projem bir kompleksleri biriydi: Biz başka bir sunucuya bir RMI istemcisi olarak hareket eden bir GWT sunucu vardı. Örnekler oluşturmak için GWT'nin sınıfları sunucudan indirmesi ve yüklemesi gerekiyordu. Daha sonra GWT, hazırda kullanarak onları veritabanında tutmak için sunucuya yeniden gönderir. Sıcak dağıtımı desteklemek için, kullanıcının bir kavanoz yükleyeceği ve sınıfları yükleyecek olan sunucuyu bilgilendirdiğini ve bunları GWT sunucusu

+0

Not: Bu sadece bu sınıf yükleyici tarafından yüklenen * tüm * sınıfları ve bu sınıfların herhangi birinin * tüm * örneklerinin de ilgisiz olduğunu gösterir. Böyle bir sınıfın bile bir örneği olduğu sürece, sınıf yükleyicisi belleğe alınacaktır (çünkü instance.getClass(). GetClassLoader() ') diyebilirsiniz. –

+0

Mükemmel, tüm örneklerin ve sınıfların derefernced olması gerekiyor - ve onlar PermGen içinde olabilir. Bence, Java 7'de 'URLClassLoader # close() '- kullanılmalı. Sınıfları geçiştirmek ve sınıfları yüklemek için dosyaları belleğe yüklemek için muğlak kabus anılarım var ... sorunları gerçekten çözmedim. Sınıf yükleyiciler garip hayvanlar. – esej

+0

@esej Büyük olasılıkla ClassLoader'ın bir alt sınıfını oluşturmak, dosyayı elle açmak ve içeriğini bir bayt dizisine okumak ve sonra 'ClassLoader # defineClass (String name, byte [] b, int off, int len) 'yi çağırmak demek istiyorsunuz? Bu gerçekten işe yaramalı. –

2

URLClassLoader bu gideren bir #close() yöntemi vardır için çok ayrıntılı olduğunu.

+0

Ben java 6 kullanıyorum ve bu şimdi değiştirilemez bahsetti: D –

+0

Java 6 EoL çok yakında. Sınıf yükleyiciye tüm referansları kaldırmayı ve tam bir GC _and_ çalıştıran sonlandırmayı zorlamayı deneyebilirsiniz. Ama bu korkunç. Ya da yakın bir yöntemi olan kendi jar sınıf yükleyicinizi yazabilirsiniz. –

+1

Ben zaten java 6 kullanarak bir çözüm buldum, cevabımı kontrol etmek ve görüşlerini paylaşmaya dikkat et? –