Java/Android类加载器(ClassLoader)及加载Class字节码

> Java 中的类加载器
  Java自带加载器:Bootstrap ClassLoader,Extention ClassLoader,system class loader;自定义Java 中的类加载器 ClassLoader一般覆盖findClass()方法。
 -- Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:
  1.引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
   2.扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
   3.系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

 -- Java ClassLoader,Java语言系统自带有三个类加载器+自定义ClassLoader
 1.Bootstrap ClassLoader 是Java类加载层次中最顶层的加载类,主要加载JDK核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。属于启动类加载器。

 2.Extention ClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar。 

 3.Appclass Loader称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。
 4.自定义ClassLoader一般覆盖findClass()方法。

 注意: 除了Java默认提供的三个ClassLoader之外,用户还可以根据需要定义自已的ClassLoader,而这些自定义的ClassLoader都必须继承自java.lang.ClassLoader类,也包括Java提供的另外二个ClassLoader(Extension ClassLoader和App ClassLoader)在内,但是Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。  当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没加载到,则把任务转交给Extension ClassLoader试图加载,如果也没加载到,则转交给App ClassLoader 进行加载,如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常。(责任链模式)
 深入分析Java ClassLoader原理- http://blog.csdn.net/xyang81/article/details/7292380
 Java加载Class文件的原理机制-http://blog.csdn.net/a1259109679/article/details/48085473
 JavaJVM加载class文件的原理机制(提高篇)-http://www.cnblogs.com/Qian123/p/5707562.html
 深入探讨 Java 类加载器-- https://www.ibm.com/developerworks/cn/java/j-lo-classloader/

  ClassLoader 只管加载,只要符合文件结构就加载,至于说能不能运行,则不是它负责的,那是由Execution Engine 负责的。
  Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。

 - 类装载方式,有两种:
 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中,
 2.显式装载, 通过class.forname()等方法,显式加载需要的类 

 - Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。
  Java的类加载器有三个,对应Java的三种类:(java中的类大致分为三种:   1.系统类   2.扩展类 3.由程序员自定义的类 )
     Bootstrap Loader  // 负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类)
            | 
          - - ExtClassLoader   // 负责加载扩展类(就是继承类和实现类)
                          | 
                      - - AppClassLoader   // 负责加载应用类(程序员自定义的类)

 -- Java采用了委托模型机制
  委托模型机制的工作原理很简单:当类加载器需要加载类的时候,先请示其Parent(即上一层加载器)在其搜索路径载入,如果找不到,才在自己的搜索路径搜索该类。这样的顺序其实就是加载器层次上自顶而下的搜索,因为加载器必须保证基础类的加载。之所以是这种机制,还有一个安全上的考虑:如果某人将一个恶意的基础类加载到jvm,委托模型机制会搜索其父类加载器,显然是不可能找到的,自然就不会将该类加载进来。
  - jvm原理机制
  1.装载:查找和导入class文件;
  2.连接:
      (1) 检查:检查载入的class文件数据的正确性;
      (2) 准备:为类的静态变量分配存储空间;
      (3) 解析:将符号引用转换成直接引用(这一步是可选的)
  3.初始化:初始化静态变量,静态代码块。
      这样的过程在程序调用类的静态成员的时候开始执行,所以静态方法main()才会成为一般程序的入口方法。类的构造器也会引发该动作。

> Android ClassLoader类加载器
  Android中类加载器有BootClassLoader,URLClassLoader,(PathClassLoader,DexClassLoader),BaseDexClassLoader等都最终继承自java.lang.ClassLoader(基类)
  和java虚拟机中不同的是BootClassLoader是ClassLoader内部类,由java代码实现而不是c++实现, BootClassLoader是Android平台上所有ClassLoader的最终parent,这个内部类是包内可见,所以我们没法使用。
  PathClassLoader和DexClassLoader都继承自BaseDexClassLoader,其中的主要逻辑都是在BaseDexClassLoader完成的。

  DexClassLoader 和 PathClassLoader, BaseDexClassLoader(基于类装载器设计“插件”框架)。在Android中,ClassLoader是一个抽象类,实际开发过程中,我们一般是使用其具体的子类DexClassLoader、PathClassLoader这些类加载器来加载类的,它们的不同之处是:
 1.DexClassLoader可以加载jar/apk/dex,可以从SD卡中加载未安装的apk;
 2.PathClassLoader可以加载/data/app目录下的apk,这也意味着,它只能加载已经安装的apk,只能加载dex文件
 3.URLClassLoader :可以加载java中的jar,但是由于dalvik不能直接识别jar,所以此方法在android中无法使用,尽管还有这个类。

 PathClassLoader classLoader = (PathClassLoader) getApplicationContext().getClassLoader();
 Log.d("mytest", "classLoader : " + classLoader + "\n" +
            "parent : " + classLoader.getParent() + "\n" +
            "grandParent : " + classLoader.getParent().getParent() + "\n" +
            "system classloader : " + ClassLoader.getSystemClassLoader() + "\n" +
            "system parent : " + ClassLoader.getSystemClassLoader().getParent());
结果:classLoader : dalvik.system.PathClassLoader[dexPath=/data/app/com.gavin.demo2application-1.apk,libraryPath=/data/app-lib/com.gavin.demo2application-1]
  parent : java.lang.BootClassLoader@41099128
  grandParent : null
  system classloader : dalvik.system.PathClassLoader[dexPath=.,libraryPath=null]
  system parent : java.lang.BootClassLoader@41099128

-- Android中类加载器有BootClassLoader,URLClassLoader,
    其实在BaseDexClassLoader里对".jar",".zip",".apk",".dex"后缀的文件最后都会生成一个对应的dex文件,所以最终处理的还是dex文件,而URLClassLoader并没有做类似的处理。
    Android Runtime(缩写为ART),在Android 5.0及后续Android版本中作为正式的运行时库取代了以往的Dalvik虚拟机。ART能够把应用程序的字节码转换为机器码,是Android所使用的一种新的虚拟机。

-- Dalvik与ART虚拟机的不同:

  Dalvik采用的是JIT技术,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率;

  ART采用Ahead-of-time(AOT)技术,应用在第一次安装的时候,字节码就会预先编译成机器码,这个过程叫做预编译。ART同时也改善了性能、垃圾回收(Garbage Collection)、应用程序除错以及性能分析。但是请注意,运行时内存占用空间较少同样意味着编译二进制需要更高的存储。

-- Android 热补丁动态修复框架小结- http://blog.csdn.net/lmj623565791/article/details/49883661/
热修复入门:Android 中的 ClassLoader。ClassLoader 是个抽象类,其具体实现的子类有 BaseDexClassLoader 和SecureClassLoader。
 SecureClassLoader 的子类是 URLClassLoader,其只能用来加载 jar 文件,这在 Android 的 Dalvik/ART 上没法使用的。
 BaseDexClassLoader 的子类是 PathClassLoader和 DexClassLoader。

-- 类加载器的代理模式  
public void testClassIdentity() { 
    String classDataRootPath = "C:\\workspace\\Classloader\\classData"; 
    FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath); 
    FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath); 
    String className = "com.example.Sample"; 
    try { 
        Class<?> class1 = fscl1.loadClass(className); 
        Object obj1 = class1.newInstance(); 
        Class<?> class2 = fscl2.loadClass(className); 
        Object obj2 = class2.newInstance(); 
        Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class); 
        setSampleMethod.invoke(obj1, obj2); 
    } catch (Exception e) { 
        e.printStackTrace(); 
    } 
 }
--  2种加载类的方法:Class.forName。
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance()用来加载 Apache Derby 数据库的驱动。
该方法有两种形式:Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)。

-- 自定义类加载器, 文件系统类加载器
public class FileSystemClassLoader extends ClassLoader { 
    private String rootDir; 
    public FileSystemClassLoader(String rootDir) { 
        this.rootDir = rootDir; 
    } 

    protected Class<?> findClass(String name) throws ClassNotFoundException { 
        byte[] classData = getClassData(name); 
        if (classData == null) { 
            throw new ClassNotFoundException(); 
        } 
        else { 
            return defineClass(name, classData, 0, classData.length); 
        } 
    } 

    private byte[] getClassData(String className) { 
        String path = classNameToPath(className); 
        try { 
            InputStream ins = new FileInputStream(path); 
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
            int bufferSize = 4096; 
            byte[] buffer = new byte[bufferSize]; 
            int bytesNumRead = 0; 
            while ((bytesNumRead = ins.read(buffer)) != -1) { 
                baos.write(buffer, 0, bytesNumRead); 
            } 
            return baos.toByteArray(); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
        return null; 
    } 

    private String classNameToPath(String className) { 
        return rootDir + File.separatorChar 
                + className.replace('.', File.separatorChar) + ".class"; 
    } 
 }

  OSGi 中的每个模块(bundle)都包含 Java 包和类。模块可以声明它所依赖的需要导入(import)的其它模块的 Java 包和类(通过 Import-Package),也可以声明导出(export)自己的包和类,供其它模块使用(通过 Export-Package)。也就是说需要能够隐藏和共享一个模块中的某些 Java 包和类。这是通过 OSGi 特有的类加载器机制来实现的。
   遇到 ClassNotFoundException和 NoClassDefFoundError等异常的时候,应该检查抛出异常的类的类加载器和当前线程的上下文类加载器,从中可以发现问题的所在。在开发自己的类加载器的时候,需要注意与已有的类加载器组织结构的协调。
--  Java字节码存放在哪 如何加载?
 JVM把文件读入内存,然后用ClassLoader来加载吗,或者应该ClassLoader本身就负责从文件加载到内存
public class PathClassLoader extends ClassLoader {
    private File dir;
     
    public PathClassLoader(String path) {
        dir = new File(path);
    }
     
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if(dir != null) {
            File clazzFile = new File(dir, name + ".class");
            if(clazzFile.exists()) {
                FileInputStream input = null;
                try {
                    input = new FileInputStream(clazzFile);
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while((len = input.read(buffer)) != -1) {
                        baos.write(buffer, 0, len);
                    }
                     
                    return defineClass(name, baos.toByteArray(), 0, baos.size());
                } catch(Exception e) {
                    throw new ClassNotFoundException(name, e);
                } finally {
                    if(input != null) {
                        try {
                            input.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return super.findClass(name);
    }
}

 Java虚拟机上Java代码是如何编译成字节码并执行的,了解JVM内部架构和在字节码执行期间不同内存区域之间的差异.
 Java虚拟机是基于栈的架构。当一个方法包括初始化main方法执行,在栈上就会创建一个栈帧(frame),栈帧中存放着方法中的局部变量。局部变量数组(local veriable array)包含在方法执行期间用到的所有变量包括一个引用变量this,所有的方法参数和在方法体内定义的变量。对于类方法(比如:static方法)方法参数从0开始,然而,对于实例方法,第0个slot用来存放this。

 内存从申请分配、到使用、再到最后的释放。
 Java 的几乎所有内存对象都是在堆内存上分配(基本数据类型除外),然后由 GC ( garbage collection)负责自动回收不再使用的内存。 GC 在什么时候回收内存对象,什么样的内存对象会被 GC 认为是“不再使用”的?
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值