JVM探索

一、JVM启动顺序

   JVM预定义的三种类型类加载器,当一个 JVM 启动的时候,Java 缺省开始使用如下三种类型类装入器:

启动(Bootstrap)类加载器:引导类装入器是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib 下面的类库加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

标准扩展(Extension)类加载器:扩展类加载器是由 Sun ExtClassLoadersun.misc.Launcher$ExtClassLoader 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。

系统(System)类加载器:系统类加载器是由 Sun AppClassLoadersun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。

  除了以上列举的三种类加载器,还有一种比较特殊的类型就是线程上下文类加载器

  需要着重说明的是,JVM在加载类时默认采用的是双亲委派机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

【疑问】为什么要使用这种双亲委托模式呢? 
  第一个原因就是因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。 
  第二个原因就是考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。 

二、类加载过程:

 1、加载:查找并加载类的二进制文件。本步骤的目的在于加载二进制的.class文件到内存中。

2、连接:包括验证、准备和解析类的二进制数据

  (1)验证:确保被加载类的正确性。存在有人修改过字节码的情况, 顺便补充一下java的字节码是有一定标准的,具体参考Java字节码的官方文档。

  (2)准备:为类的静态变量分配内存,并将其初始化为默认值。 【注意】是默认值, 默认情况下:int -> 0, short -> 0, long -> 0, float -> 0.0f, double -> 0.0, char -> ' ', byte -> 0, boolean -> false

  (3)解析:把类中的符号引用解析为直接引用

3、初始化:为类的静态变量初始化

三、类加载器的扩展

 继承自ClassLoader

  1. import java.io.ByteArrayOutputStream; 
  2. import java.io.IOException; 
  3. import java.io.InputStream; 
  4. import java.util.ArrayList; 
  5. import java.util.HashSet; 
  6. import java.util.Iterator; 
  7. import java.util.List; 
  8. import java.util.Set; 
  9. import java.util.jar.JarEntry; 
  10. import java.util.jar.JarFile; 
  11. import playground.library.functional.FunctionalUtils; 
  12. import playground.library.functional.Pair; 
  13. import playground.library.functional.iterator.FilterIterator; 
  14. import playground.library.functional.iterator.IteratorTransformer; 
  15. import playground.library.functional.Predicate; 
  16. import playground.library.functional.Transformer; 
  17. import playground.library.functional.iterator.DelegatingIterator; 
  18. import playground.library.functional.iterator.LinkingIterator; 
  19. import playground.library.jar.JarEntryIterator; 
  20. import playground.library.utils.IteratorUtils; 
  21. import playground.library.utils.IOUtils; 
  22.  
  23. /** 
  24.  * ClassLoader for loading from Jar files. 
  25.  */ 
  26. public class JarClassLoader extends ClassLoader 
  27.     public JarClassLoader(ClassLoader parent) { super(parent); }     
  28.     public JarClassLoader()                   { super(); } 
  29.      
  30.     private final List<JarFile>     files = new ArrayList<JarFile>(); 
  31.      
  32.     /** 
  33.      * Provides an iterator over all pairs of (file, entry) of JarFiles associated 
  34.      * with this class loader and entries of those jar files. 
  35.      */ 
  36.     private Iterator<Pair<JarFile, JarEntry>> getResources(){ 
  37.         return new LinkingIterator<Pair<JarFile, JarEntry>>( 
  38.             new IteratorTransformer<JarFile, Iterator<Pair<JarFile,JarEntry>>>( 
  39.                 new Transformer<JarFile, Iterator<Pair<JarFile, JarEntry>>>(){ 
  40.                 public Iterator<Pair<JarFile, JarEntry>> transform(JarFile file){ 
  41.                     return FunctionalUtils.merge(file, new JarEntryIterator(file));}}) 
  42.            .transform(JarClassLoader.this.files.iterator()));} 
  43.      
  44.     /** 
  45.      * Searches through the available jar files to try and find a class with the given name. 
  46.      */ 
  47.     @Override 
  48.     protected Class<?> findClass(String name) throws ClassNotFoundException{ 
  49.         final Iterator<Pair<JarFile,JarEntry>> resources = this.getResources(); 
  50.         while (resources.hasNext()){ 
  51.             final Pair<JarFile, JarEntry> resource = resources.next(); 
  52.                 if(resource.second.getName().equals(name)){ 
  53.                     try { return this.load(resource.first.getInputStream(resource.second));} 
  54.                     catch (IOException e) { e.printStackTrace(); }}} 
  55.         throw new ClassNotFoundException();} 
  56.      
  57.     /** 
  58.      * Loads the contents of the InputStream as a class. This doesn't validate 
  59.      * the contents, so will fail messily if you pass it something that isn't a 
  60.      * valid class file. 
  61.      */ 
  62.     private Class<?> load(final InputStream input){ 
  63.         try { 
  64.             final ByteArrayOutputStream output = new ByteArrayOutputStream(); 
  65.             final byte[] bytes = IOUtils.copy(input, new ByteArrayOutputStream()).toByteArray(); 
  66.             return this.defineClass(null, bytes, 0, bytes.length); } 
  67.         catch (IOException e){ throw new RuntimeException(e); } 
  68.         finally
  69.             try{ input.close(); } 
  70.             catch (IOException e){ throw new RuntimeException(e);}}} 
  71.          
  72.     /** 
  73.      * Provides an iterator which lazily loads the contents of the jar file as 
  74.      * classes. Will also register the jar file with the class loader's file list. 
  75.      * When the iterator is exhausted the jar file will be removed from the file list. 
  76.      */ 
  77.     public Iterator<Class<?>> load(final JarFile file) throws IOException{ 
  78.         this.files.add(file); 
  79.                  
  80.         return   
  81.             new DelegatingIterator<Class<?>>( 
  82.                 new IteratorTransformer<JarEntry, Class<?>>( 
  83.                     new Transformer<JarEntry, Class<?>>(){ 
  84.                         public Class transform(JarEntry stream){ 
  85.                             InputStream fileStream = null
  86.  
  87.                             try{return JarClassLoader.this.load(fileStream = file.getInputStream(stream)); } 
  88.                             catch(IOException e){throw new RuntimeException(e);} 
  89.                             finally{IOUtils.close(fileStream); }}}) 
  90.                 .transform( 
  91.                     new FilterIterator(  
  92.                        new Predicate<JarEntry>(){  
  93.                            public boolean satisfies(JarEntry entry){ 
  94.                                return entry.getName().endsWith(".class");}}, 
  95.                        new JarEntryIterator(file)))){ 
  96.                 @Override 
  97.                 public Class<?> next(){ 
  98.                     final Class<?> clazz = super.next(); 
  99.                     if (!this.hasNext()) JarClassLoader.this.files.remove(file);                     
  100.                     return clazz;} 
  101.                  
  102.                 @Override 
  103.                 public boolean hasNext(){ 
  104.                     final boolean hasNext = super.hasNext(); 
  105.                     if (!hasNext) JarClassLoader.this.files.remove(file); 
  106.                     return hasNext;}};} 
  107.  
  108.     /** 
  109.      * Loads all class files from the jar and returns a set of them. 
  110.      */ 
  111.     public Set<Class<?>> loadAll(final JarFile file) throws IOException 
  112.     { 
  113.         return IteratorUtils.fill(new HashSet<Class<?>>(), this.load(file)); 
  114.     }