JVM体系结构

作者: PennyWong | 来源:发表于2016-05-20 10:18 被阅读152次
    JVM体系结构

    ClassLoader

    将Class加载到内存

    结构

    • BootstrapClassLoader:加载Java核心库(JAVA_HOME/jre/lib),唯一一个使用本地代码编写的加载器
    • ExtensionClassLoader:加载扩展库(JAVA_HOME/jre/lib/ext和系统参数java.ext.dirs指定的目录),它的父加载器是null(因为BootstrapClassLoader是使用C++实现的,没有对应的java类)
    • SystemClassLoader:加载应用类的类文件(Classpath下的类文件)
    • UserDefineClassLoader:加载用户定义的类文件,可以从网络或者数据库中加载类文件,实现类文件加密解密,动态地创建符合应用特殊需要的定制化类等
    ClassLoader.png

    双亲委派机制

    加载器在接收到加载类的请求时,首先检查自己的缓存,确认类是否已被加载,如果没有加载,则将请求委托给父加载器,依次递归,如果父加载器完成加载,则成功返回,否则才自己去加载

    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
    {
      // 检查类是否已被加载
      Class c = findLoadedClass(name);
      if (c == null) {                
        try {
          if (parent != null) {
            // 委托父类去加载
            c = parent.loadClass(name, false);
          } else {
            // parent为空,代表父类是BootstrapClassLoader
            c = findBootstrapClassOrNull(name);
          }
        } catch (ClassNotFoundException e) {
          // ClassNotFoundException thrown if class not found
          // from the non-null parent class loader
        }
    
        if (c == null) {
          // 父类没有加载,才自己去加载
          c = findClass(name);
        }
      }
    
      if (resolve) {
        resolveClass(c);
      }
      return c;
    }
    

    意义:避免重复加载,避免安全因素(如果不采用这种机制,那么系统核心的类就可以被随意替换)

    ** 不同类加载器加载的类不是相同类型:在Java中,一个类的全名(包名+类名)作为其标识,但在JVM中,一个类用其全名+类加载器作为唯一标识,不同类加载器加载的类置于不同的命名空间中**

    线程上下文类加载器

    双亲委派模式不能解决全部的加载问题。
    Java提供了很多服务提供者接口(Service Provider Interface,SPI),常见的有JDBC、JNDI、JAXP等。这些SPI接口由Java核心库提供(通过BootstrapClassLoader加载),而它们的实现由第三方库提供(通过SystemClassLoader加载)。但很多时候,SPI接口需要加载具体的实现类,如 JAXP 中的javax.xml.parsers.DocumentBuilderFactory类中的newInstance()方法用来生成一个新的DocumentBuilderFactory的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。问题是BootstrapClassLoader是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给SystemClassLoader,因为它是SystemClassLoader的祖先类。

    而线程上下文类加载器就是解决这个问题,如果不做任何的设置,Java的线程的上下文类加载器默认就是SystemClassLoader。在SPI接口的代码中使用线程上下文类加载器,就可以成功的加载到SPI实现的类。

    使用线程上下文加载器,要注意保证多个需要通信的线程间的类加载器应该是同一个,防止因为不同的类加载器导致类型转换异常(ClassCastException)。

    运行时区域

    运行时区域

    Method Area

    已加载的类信息(类结构、方法、字段、静态变量)
    常量池:字符串、整形(-127-128)
    一般称为Permanent Generation

    线程执行

    每个线程都有一个PC Register、JVM Stack、Native Method Stack
    PC Register:下一条要执行的指令的地址
    JVM Stack:包含一系列的Stack Frame,每次方法调用都会创建一个Frame并压栈,每个栈帧都对应一个被调用的方法(使用递归容易让栈溢出,通过-Xss设置栈大小)。同时那些方法内的局部变量,也是在这里创建


    Stack Frame

    Heap

    Heap
    存放实例对象和数组
    分成Young、Tenured、Permanent三个不同区域,其中Young又分成Eden和两个相同大小的Survivor:From、To
    为什么要分代:不同对象的生命周期是不一样的,采用不同的收集算法,可以提高回收效率

    例子

    public class Test {
      public static void main(String[] args) {
        public Test2 t2 = new Test2(); 
        //JVM将Test2类信息加载到方法区,new Test2()实例保存在堆区,Test2引用保存在栈区
      }
    }
    

    参考

    http://docs.oracle.com/javase/specs/jvms/se7/jvms7.pdf
    http://blog.csdn.net/zhoudaxia/article/details/35897057
    http://www.ibm.com/developerworks/cn/java/j-lo-classloader/

    相关文章

      网友评论

        本文标题:JVM体系结构

        本文链接:https://www.haomeiwen.com/subject/lucgrttx.html