目录
类加载器子系统
类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识。
类的加载过程其中的阶段
- 加载阶段(Loading)
- 链接阶段(Linking)
- 初始化阶段(Initialization)
存放
加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量。
阶段1:Loading
- 通过一个类的全限定名获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
阶段2:Linking
验证
确保class文件的字节流中包含的信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。
准备
为类变量(静态变量)分配内存并且设置该类变量的默认初始值,即零值。
这里不包含用final修饰的static,因为final在编译的时候就会分配了。
解析
将常量池内的符号引用转换为直接引用的过程。
阶段3:Initialization
初始化阶段就是执行类构造器方法< clinit>()的过程。此方法用来收集类中所有类变量(即静态变量)的赋值动作和静态代码块中的语句合并而来。
如果没有静态代码块和静态变量,该方法便不会调用。
类加载器分类
- Bootstrap ClassLoader(引导类加载器,c和c++实现)
- Extension ClassLoader(扩展类加载器,继承于ClassLoader,故为自定义类加载器,java实现)
- System ClassLoader(继承于ClassLoader,故为自定义类加载器,java实现)
代码
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取其上层:扩展类加载器
ClassLoader extCLassLoader = systemClassLoader.getParent();
System.out.println(extCLassLoader);
//获取其上层 打印出null
ClassLoader bootstrapClassLoader = extCLassLoader.getParent();
System.out.println(bootstrapClassLoader);
//获取用户自定义类加载器 为AppClassLoader 和 systemClassLoader是一样的
ClassLoader classLoader = test.class.getClassLoader();
//打印为null 说明用的是bootstrapClassLoader 即核心类用的是此类加载器
ClassLoader classLoader1 = String.class.getClassLoader();
启动类加载器
Bootstrap ClassLoader,使用C/C++语言实现,没有父加载器
应用程序类加载器
AppClassLoader,父类加载器为扩展类加载器,负责加载环境变量classpath或系统属性,是程序中默认的类加载器。
ClassLoader类
ClassLoader类是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)
API
获取ClassLoader:
- 获取当前类的ClassLoader:getClassLoader()
- 获取当前线程上下文的ClassLoader:Thread.currentThread().getContextClassLoader()
- 获取当前系统的ClassLoader:ClassLoader.getSystemClassLoader()
- 获取调用者的classLoader:DriverManager.getCallerClassLoader()
双亲委派机制
Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。
而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。
工作原理
- 如果一个类加载器收到了类加载请求,并不会自己先去加载,而是把这个请求委托给父类的加载器去执行。
- 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,最终将到达顶层的启动类加载器。
- 如果父类加载器可以完成类加载任务,就成功返回,如果无法完成此加载任务,子加载器才会尝试自己去加载。
优势
- 避免类的重复加载
- 保护程序安全,防止核心API被随意篡改
沙箱安全机制
Java安全模式的核心就是Java沙箱(sandbox),沙箱是一个限制程序运行的环境。沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统支援访问,通过这样的措施,来保证对代码的有效隔离和防止本地系统造成破坏。
沙箱主要限制系统资源访问,包括CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问也可以不一样。
沙箱基本组件
- 字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节校验,比如核心类。
- 类装载器(class loader):其中类装载器在3个方面对Java沙箱起作用
1.它防止恶意代码去干涉善意的代码;//双亲委派机制,对java核心源代码进行保护
2.他守护了被信任的类库边界;
3.他守护了被信任的类库边界;
4.他将代码归入保护域,确定了代码可以进行那些操作。
其它
判断class对象是否相同
在JVM中表示两个class对象是否为同一个类存在两个必要条件:
1.类的完整类名必须一致,包括包名。
2.加载这个类的ClassLoader(指ClassLoader实例对象)必须相同。
对类加载器的引用
JVM必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。
如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。
当解析一个类型到另一个类型的引用的时候,JVM需要保证两个类型的类加载器是相同的。
类的主动使用和被动使用
类的主动使用会导致类的初始化,而类的被动使用则不会。
主动使用的情况
- 创建类实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射
- 初始化一个类的子类。
- JVM启动时被标明为启动类的类。