JVM体系结构

JVM体系结构

ClassLoader

将Class加载到内存

ClassLoader结构

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

ClassLoader

双亲委派机制

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

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

存放实例对象和数组
分成Young、Tenured、Permanent三个不同区域,其中Young又分成Eden和两个相同大小的Survivor:From、To

Heap

为什么要分代:不同对象的生命周期是不一样的,采用不同的收集算法,可以提高回收效率

例子

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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值