2012-06-30 14 views
5

मैं जावा में क्लासलोडर्स के साथ खेल रहा था और एक अजीब चीज देखी। यदि क्लासलोडर एक जार से कक्षा को लोड करता है, तो यह जार अनिश्चित काल तक बंद हो जाता है भले ही आप अपने क्लासलोडर को अपरिवर्तित करते हैं।लॉक किए गए जार के साथ जावा क्लासलोडर दुविधा

नीचे दिए गए उदाहरण में, जार में हैलोवर्ल्ड नामक एक कक्षा होती है। मैं जो करता हूं वह एक वर्ग लोडर के माध्यम से जार में निहित कक्षा को लोड करने का प्रयास करता है जो जार गतिशील रूप से जोड़ता है। यदि आप skip से true पर सेट करते हैं और Class.forName पर कॉल नहीं करते हैं, तो आप जार को हटा सकते हैं, लेकिन यदि आप छोड़ नहीं पाते हैं और भले ही आप classLoader (classLoader = null) को अपरिवर्तित करते हैं, तो जब तक JVM बाहर निकलता है तब तक जार हटाया नहीं जा सकता है।

वह क्यों है?

पुनश्च: मैं जावा 6 उपयोग कर रहा हूँ और कोड परीक्षण प्रयोजनों

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

पर उपलब्ध कराएगा, क्या आप एक कामकाज या व्याख्या चाहते हैं ? – esej

+1

@esej मुझे पहले से ही दोनों मिले हैं, मेरा जवाब जांचने और आपको राय साझा करने की परवाह है? –

उत्तर

8

मुझे एक उत्तर और एक कामकाज मिला है।

इस article और इस अद्भुत संबंधित article के आधार पर, यह Class.forName(className, true, classLoader) का उपयोग करने की एक बुरी आदत है क्योंकि यह कक्षा को अनिश्चित काल तक कैश किया जाता है।

classLoader = null; 
System.gc(); 

आशा इस दूसरों मदद करता है:

समाधान का उपयोग classLoader.loadClass(clasName) बजाय, तो एक बार, समाप्त हो गया classLoader unreference और का उपयोग कर कचरा कलेक्टर कॉल करने के लिए गया था! :)

पृष्ठभूमि जानकारी:

मेरे परियोजना एक Complexe एक था: हम एक GWT सर्वर अन्य सर्वर पर एक RMI ग्राहक के रूप में कार्य किया था। तो उदाहरण बनाने के लिए, जीडब्ल्यूटी को सर्वर से कक्षाओं को डाउनलोड करने और उन्हें लोड करने की आवश्यकता है। बाद में, जीडब्ल्यूटी सर्वर को उदाहरण के लिए हाइबरनेट का उपयोग करके डेटाबेस में बने रहने के लिए भेज देगा। गर्म तैनाती का समर्थन करने के लिए, हमने गतिशील रूप से कक्षा लोडिंग का चयन किया जहां उपयोगकर्ता एक जार अपलोड करेगा और उस सर्वर को सूचित करेगा जो कक्षाओं को लोड करेगा और उन्हें जीडब्ल्यूटी सर्वर

+0

ध्यान दें कि यह केवल तभी काम करता है जब * इस वर्ग लोडर द्वारा लोड किए गए सभी * वर्ग और * इनमें से किसी भी वर्ग के सभी * उदाहरण भी संदर्भित नहीं हैं। जब तक इस तरह के वर्ग का एक उदाहरण भी होता है, तब तक वर्ग लोडर को स्मृति में रखा जाएगा (क्योंकि आप 'instance.getClass() को कॉल कर सकते हैं। GetClassLoader() ')। –

+0

उत्कृष्ट, पूरी तरह से सभी उदाहरणों और कक्षाओं को अव्यवस्थित करने की आवश्यकता है - और वे पर्मजेन में समाप्त हो सकते हैं। मुझे लगता है कि किसी को ध्यान रखना चाहिए कि जावा 7 में हमारे पास 'URLClassLoader # close() 'है - जिसका उपयोग किया जाना चाहिए। कक्षाओं को लोड करने के लिए कक्षाओं को लोड करने और फ़ाइलों को स्मृति में लोड करने से मुझे अस्पष्ट नाश्ते की यादें हैं ... वास्तव में वास्तव में समस्याओं को हल नहीं किया। क्लासलोडर अजीब जानवर हैं। – esej

+0

@esej आप शायद क्लासलोडर का सबक्लास बनाते हैं, मैन्युअल रूप से फ़ाइल खोलते हैं और अपनी सामग्री को बाइट सरणी में पढ़ते हैं और फिर 'क्लासलोडर # डिफिन क्लास (स्ट्रिंग नाम, बाइट [] बी, इंट ऑफ, इंट लेन) को कॉल करते हैं? यह वास्तव में काम करना चाहिए। –

2

जावा 7 में URLClassLoader एक #close() विधि है कि यह समस्या नहीं है के लिए बहुत वर्बोज़ है।

+0

मैंने बताया कि मैं जावा 6 का उपयोग कर रहा हूं और इसे अब बदला नहीं जा सकता: डी –

+0

जावा 6 वास्तव में जल्द ही ईओएल है। आप कक्षा लोडर के सभी संदर्भों को हटाने और एक पूर्ण जीसी _and_ चलने अंतिमकरण को मजबूर करने का प्रयास कर सकते हैं। लेकिन यह भयानक है। या आप अपना खुद का जार क्लासलोडर लिख सकते हैं जिसमें एक करीबी विधि है। –

+1

मुझे पहले से ही जावा 6 का उपयोग करके समाधान मिला है, मेरा जवाब जांचने और आपको राय साझा करने की देखभाल है? –

संबंधित मुद्दे