jvm(3)类加载器

1.读书笔记(java疯狂讲义18章)

(1)JVM和类

运行某个java程序,命令就会启动一个java虚拟机进程。不管多少线程,都处于该java虚拟机进程中。多个线程、所有变量都处于同一个进程里,都使用该JVM进程内存区。
java程序结束时,JVM结束。内存中的状态丢失。如下:

public class A{
    public static int a = 6;
}

即使class ATest1中的main( )方法修改了a,(运行主函数),class ATest2中的main( )再次运行时,还是会初始化a。
两个JVM之间并不会共享想数据!


(2)类的加载、连接、初始化

加载:将类的class文件读入内存,并为之且总会创建一个java.lang.Class对象。(通过加载器完成,后边讲)。

连接:将类的二进制数据合并到jre中。

初始化:过程:(a)如没有加载和连接,先加载和连接;(b)直接父类若没有初始化,先初始化父类。
所以,JVM最先初始化的总是java.lang.Object!

(静态代码块和static效果一样,他们顺序执行)
当创建一个类,或使用静态方法时、访问变量等会初始化一个类。

注意有final修饰时,会不会初始化分两种情况:

  • final型变量编译时确定,在出现的地方替换。不会初始化。如下,再次出现TestClass. strConstant时不会初始化:
static final String strConstant="编译时确定";
  • final型编译时不能确定,会初始化,如下,运行时才能更确定系统时间:
static final String strConstant=System.currentTimeMillis()+"";

(3)类加载器

类加载器的作用:负责将.class文件(可能在磁盘,可能在网络上)加载到内存中,并为之生成对应的java.lang.Class对象。

在java中,一个类用全限定名标识;
在JVM中,一个类用全限定名和其类加载器作为唯一标识。如类加载器不是同一个实例,则不同。

类的加载机制:

  • 全盘负责
  • 父类委托
  • 缓存机制
    说明:机制是全部思想,并不是并列的。
根加载器
  |
  |
扩展类加载器(ExtClassLoader的实例)
  |
  |
系统类加载器(AppClassLoader的实例)
  |
  |
用户类加载器

这里写图片描述

Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader三者的关系如下:
Bootstrap ClassLoader是Extension ClassLoader的parent,Extension ClassLoader是App ClassLoader的parent。

但是这并不是继承关系,只是语义上的定义,基本上,每一个ClassLoader实现,都有一个Parent ClassLoader。
可以通过ClassLoader的getParent方法得到当前ClassLoader的parent。Bootstrap ClassLoader比较特殊,因为它不是java class所以Extension ClassLoader的getParent方法返回的是NULL。

自定义类加载器

继承ClassLoader类,重写关键的两个方法:

  • loadClass( )
  • findClass( )
    通常写findClass( )即可。重写findClass( )可以避免覆盖父类委托、缓存机制两种策略。

注:java为ClassLoader提供了URLclassLoader类,是扩展类、系统类的父类(真实的父类)。


其他整理

1.classloader的理解

    java程序(class文件)并不是本地可以执行的程序。当运行java程序时,首先运行JVM,然后再把java class加载到JVM中,负责加载class的部分就是classloader。

Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader.

JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现的,它负责加载核心JavaClass(即所有java.*开头的类)。另外JVM还会提供两个ClassLoader,它们都是用Java语言编写的,由BootstrapClassLoader加载;其中Extension ClassLoader负责加载扩展的Javaclass(例如所有javax.*开头的类和存放在JRE的ext目录下的类),ApplicationClassLoader负责加载应用程序自身的类。   当运行一个程序的时候,JVM启动,运行bootstrapclassloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。

学习概述:本模块深入讲解了Java类加载方面的知识,Java类加载器和类加载机制以及类加载原理

  学习目标:掌握类加载机制和原理,能够独立开发自己的类加载器。
  
  1.类的加载
  
  什么是类加载? 类加载是指将类的class文件读入内存,并为之创建一个Java.lang.Class对象,也就是说当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

  思考问题:怎么样才算同一个类?
  
  当JVM启动时,会形成三个类加载器组成的原始类加载器层次结构:
  【BootStrap ClassLoader】根类加载器 这是一个特殊的加载器,他并不是有Java编写,而是JVM自身实现的
  【Extension Classloader】扩展类加载器
  【System Classloader】系统类加载器
  
  类加载器的父子关系:
  
  实验获得类加载器以及了解类加载器的层次结构:
  
  

public  class ClassloaderDemo{
    public static  void main(String[] args){
        System.out.println(
            ClassLoaderDemo.class.getClassLoader().getName());
        System.out.println(System.class.getClassloader());
        ClassLoader classloader = ClassLoaderDemo.class.getClassLoader());
        while(loader!=null){
            System.out.println(loader.getClass().getName());
            loader=loader.getParent();
        }
    }
}

  注意:程序会抛出异常,因为JVM根类加载器不是Java类。(C++写的,为null)
  
  

2.类的加载机制,如图所示:

  <1>全盘负责:所谓全盘负责,就是说当一个类加载器负责加载某个Class的时候,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个 类加载器来实现载入。
  <2>父类委托:意思是先让父类加载器试图加载该Class,只有父类加载器无法加载该类是才尝试从自己的路径中加载该类。
  <3>缓存机制:缓存机制将会保证所有被加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存中搜索该Class,只有当缓存中不存在该Class对象 时,系统才重新读取该类对应的二进制数据。这就是为什么我们修改Class后,JVM必须重新启动,修改才生效的原因。
  
  类加载器的父子关系:用户类加载器—>系统类加载器—>扩展类加载器—>根类加载器

类加载机制:

  <1>当JVM需要加载一个类是,到底指派哪个类加载器去加载呢?
  
  首先当前线程的类加载器去加载线程中的第一个类,如果A类中引用了B类,JVM将使用加载A类的加载器来加载B类,最后还可以调用ClassLoader。loadeClass方法指定 某个类加载器去加载某个类。
  
  <2>每个类加载器在加载类时,先委托给其上级加载器。
  注意两点:
  当所有的祖宗类加载器都没有加载到类,回到发起类加载器,还加载不了,那么程序将抛出ClassNotFoundExcetpion,而不是去找发起类加载器的儿子,因为没有 getChild ()方法,即使有,那么选择哪一个儿子加载器呢?
  
  面试题:能不能自己写一个类叫Java.lang.System?
 
  答案:可以写,但是因为JVM委托机制的存在,会先找到JVM根类加载器,我自己写也可以,那么我要抛开委托加载机制,我自己指定一个ClassLoader。

 3.自定义类加载器
 
  JVM中除了根类加载器之外的所有类加载器都是classloader的子类实例,我们完全可以通过扩展ClassLoader的子类,并重写ClassLoader所包含的的方法来实现自定义类 加载器,ClassLoader有两个关键的方法:loadClass(),findClass()。

不过我们一般推荐重写findClass()方法,而不是loadClass()方法,因为重写findClass()可以避免覆盖默认类加载器的父类委托,缓存机制两种策略。

  下面是我自己编写的一个类加载器:

package snippet;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
*
* @author Administrator
*自定义类加载器
*/
public class MyClassLoader extends ClassLoader {
    // 获取java源文件的二进制码
    public byte[] getBytes(String filename){
    File file = new File(filename);
    InputStream ips=null;
    byte[] b = new byte[(int) file.length()];
    try {
        ips = new FileInputStream(file);
        int raw =ips.read(b);
        if(raw!=file.length()){
            throw new IOException("无法完整读取文件");
        }
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    finally{
        if(ips!=null){
    try {
        ips.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    }
}
return b;
}

public boolean compile(String javaFile){
    System.out.println("正在编译");
    Process p=null;
    try {
        //调用系统javac命令
        p=Runtime.getRuntime().exec("javac" + javaFile);
        try {
            p.waitFor();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    int ret = p.exitValue();
    return ret==0;
}

@Override
protected Class<?> findClass(String name) {
    Class<?> clazz=null;
    String fileStub = name.replace(".", "/");
    String javaFileName = fileStub + ".java";
    String classFileName = fileStub + ".class";
    File javaFile = new File(javaFileName);
    File classFile = new File(classFileName);
    //如果java源文件存在并且class文件不存在,或者java源文件比class文件修改的时间晚
    if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){
    if(!compile(javaFileName)||!classFile.exists()){
    try {
        throw new ClassNotFoundException("未发现class文件");
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

//如果class文件已经存在,那么直接生成字节码
if(classFile.exists()){
    byte[] b = getBytes(classFileName);
    clazz = defineClass(name, b, 0, b.length);
}

//如果为空,标明加载失败
if(clazz==null){
    try {
        throw new ClassNotFoundException(name);
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
}
return clazz;
}
}

  上面代码重写了findClass方法,通过重写该方法就可以实现自定义的类加载机制。
  学习总结:1.了解了JVM三种类加载器(根类加载器,系统类加载器,扩展类加载器),明白了三种类加载器的作用和范围
  2.学习了JVM三种类加载机制(父类委托,缓存,全盘负责)
  3.学习了如何自定义类加载器,通过继承ClassLoader类,特别要注意两个关键方法:loadClass()和findClass()两种方法的机制和不同。


其他整理2

http://m.blog.youkuaiyun.com/wangyang1354/article/details/49448007

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值