1 class装载验证流程
a) 加载:
a.1) 装载类的第一个阶段
a.2) 取得类的二进制流
a.3) 转为方法区数据结构
a.4) 在Java堆中生成对应的java.lang.Class对象
b) 链接
验证:
准备:
分配内存,并为类设置初始值 (方法区中) public static int v=1; 在准备阶段中,v会被设置为0 在初始化的<clinit>中才会被设置为1 对于static final类型,在准备阶段就会被赋上正确的值 public static final int v=1;
解析:
符号引用(字符串)替换为直接引用(指针/地址)
c) 初始化 : 执行父类构造器,执行子类构造器
2 什么是类装载器ClassLoader
a) ClassLoader是一个抽象类
b) ClassLoader的实例将读入Java字节码将类装载到JVM中
c) ClassLoader可以定制,满足不同的字节码流获取方式
d) ClassLoader负责类装载过程中的加载阶段
ClassLoader的几个方法:
public Class<?> loadClass(String name) throws ClassNotFoundException 载入并返回一个Class protected final Class<?> defineClass(byte[] b, int off, int len) 定义一个类,不公开调用 protected Class<?> findClass(String name) throws ClassNotFoundException loadClass回调该方法,自定义ClassLoader的推荐做法 protected final Class<?> findLoadedClass(String name) 寻找已经加载的类
ClassLoader的分类:
a) BootStrap ClassLoader (启动ClassLoader)
b) Extension ClassLoader (扩展ClassLoader)
c) App ClassLoader (应用ClassLoader/系统ClassLoader)
d) Custom ClassLoader(自定义ClassLoader)
这四个分类对应关系和对应JAR:
上图中,查找类是从底向上查找,如果找到就不会继续向上找。
3 java沙箱简介:
java沙箱是你可以接受来自任何来源的代码,沙箱限制了可能进行系统破坏的任何动作,所以沙箱更像一个针对具有破坏能力代码的监狱。
java沙箱基本组件:
a) 类装载器 (可以由用户定制)
b) class文件校验器
c) 内置的java虚拟机
d) 安全管理器 (可以由用户定制)
e) java核心API
4 java类装载器体系结构(从三方面对java沙箱起作用):
a) 防止恶意代码干扰善意代码:(确保类之间相互独立)
不同类装载器装入同样类会产生一个唯一的命名空间,不同命名空间之间的类如同各自有自己的防护罩,
隔绝并感知不到彼此存在,jvm维护这些命名空间,看下图理解:
b) 守护了被信任的类库边界(确保包的安全)
jvm通过 双亲委托模式 和 运行时包 来界定类库的边界。
b.1) 双亲委托模式:
先看如下代码(java.lang.ClassLoader的loadClass)
<SPAN style="FONT-SIZE: 14px">protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }</SPAN>
虚拟机去装载一个类时, 调用loadClass()方法,在方法内,
使用findLoadedClass来判断要装载的类字节码是否已经转入了内存,
如果没有的话,它会找到它的parent(parent是指装载自己的类加载器,而应用程序的类加载器是AppClassLoader),然后调用parent的loadClass,重复自己loadClass的过程。
如果parent没有装载过着这个类,就调用findBootstrapClass(这里是指bootStrap,启动装载器)
来尝试装载这个类的字节码,
如果bootStrap也没有办法装载这个类,则调用自己的findClass来尝试装载这个类,
如果还是没办法装载则抛出异常。
说白了,双亲委托模式就是:
自底向上检查类是否已经加载
自顶向下尝试加载类。
那么双亲委托模式的优点:
自定义一个String类,
public class String { public static void main(String[] args) { System.out.println("111"); } } 运行报错如下: java.lang.NoSuchMethodError: main Exception in thread "main"
行这个我们自己定义的类的java.lang.String的双亲委托模式加载过程如下AppClassLoader -> ExtClassLoader -> BootstrapLoader, 按照上述代码加载流程, AppClassLoader 根据name去classpath下找这个类,找不到,然后在BootstrapLoader下找,BootstrapLoader去加载核心API里的类,它匹配到核心API(JAVA_HOME\jre\lib)里的String类,所以调用核心API里的String类里的main函数,所以就抛出异常了
总结下 优点就是: 防止了我们自己写的类对java核心代码的破坏。
b.2) 运行时包:
jvm只允许装载同一包中的类互相访问,
自定义下的 java.lang.xxx 和 java.lang.String 是不同加载器加载,因此不能相互访问,达到隔离效果
c) 将代码归入到某保护区,确保类内代码的作用域
类装载器会把代码放在保护区中一限定这些代码能够执行的操作的权限,类比于在监狱指定范围活动。
5 类装载器体现结构脑图: