深入探究jvm之类装载器
一、class装载验证流程
1、加载
1)、取得类的二进制流。
2)、转为方法区数据结构。
3)、在Java堆中生成对应的java.lang.Class对象。
2、链接--验证(目的:保证Class流的格式是正确的)
1)、文件格式的验证:是否是0xCAFEBASE开头、版本号是否正确等。
2)、元数据验证:是否有父类、是否继承了final类、非抽象类是否实现了所有的抽象方法等。
3)、字节码验证(最复杂):运行检查、栈数据类型和操作码数据参数是否吻合、跳转指令是否指定到合理的位置。
4)、符号引用验证:常量池中描述类是否存在、访问的字段和方法是否存在且有足够的权限。
3、链接--准备
分配内存,并为类设置初始值(在方法区中),举个栗子:
①public static int a = 100;在链接--准备阶段中,a会被设置为0(int 类型的默认值),在初始化的<clinit>中才会被设置为1。
②public static final int a = 100;对于static final 类型,在链接--准备阶段就会被赋上正确的值,a被设置为100。
4、链接--解析
符号引用(字符串引用对象不一定被加载)替换为直接引用(指针或者偏移量,引用对象一定存在于内存中)。
5、初始化
1)、执行类构造器<clinit>,包括static变量赋值语句和static块。、
2)、子类的<clinit>调用前保证父类的<clinit>被调用。
3)、<clinit>方法是线程安全的,同步执行。
二、什么是ClassLoader?
ClassLoader是一个抽象类,它的实例将读入的Java字节码装载到jvm中,ClassLoader可以实现定制,以满足不同的字节码流的获取方式,主要负责类装载过程中的加载。
1)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)
- 寻找已经加载的类
2)JDK中ClassLoader默认的设计模式分类:
①BootStrap ClassLoader(启动ClassLoader)
②Extension ClassLoader (扩展ClassLoader)
③App ClassLoader(应用ClassLoader/系统ClassLoader)
④自定义ClassLoader
上图展现了类查找和加载的次序,这样我们很容易就会想到存在一个问题:顶层的ClassLoader是无法加载底层ClassLoader的类,只就是“双亲问题”。那么Java框架(也就是rt.jar)如何加载Classpath下应用的类呢?
举个栗子进一步说明问题:javax.xml.parsers包中定义了xml解析的类接口,这些类接口(Service Provider Interface)都位于rt.jar,即接口的定义(以及类的工厂方法)都在Bootstrap ClassLoader中,其实这个SPI的实现(非抽象类)都在AppLoader之中,JDK要求Bootstrap ClassLoader能够加载Classpath下的类,这显然是不满足上图的要求的。
JDK为了解决这个问题在Thread类中定义了一个静态方法,Thread.setContextClassLoader()。这是一个上下文加载器,是一个“角色”,并不是一个真正的ClassLoader,它只是承担了特殊的任务,用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题。基本思想是在顶层ClassLoader中传入一个底层的ClassLoader实例。下面的代码来源于rt.jar中javax.xml.parsers.FactoryFinder展示如何在启动类加载器中加载AppLoader的类突破“双亲模式”问题。
- static private Class getProviderClass(String className, ClassLoader cl,
- boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
- {
- try {
- if (cl == null) {
- if (useBSClsLoader) {
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
- } else {
- cl = ss.getContextClassLoader();
- if (cl == null) {
- throw new ClassNotFoundException();
- }
- else {
- return cl.loadClass(className); //使用上下文ClassLoader
- }
- }
- }
- else {
- return cl.loadClass(className);
- }
- }
- catch (ClassNotFoundException e1) {
- if (doFallback) {
- // Use current class loader - should always be bootstrap CL
- return Class.forName(className, true, FactoryFinder.class.getClassLoader());
- }
- …..
双亲模式是默认的模式,但不是必须要这么做,比如Tomcat的WebappClassLoader就会先加载自己的class,找不到再委托parent,再如,OSGi(模块化,热加载)的ClassLoader形成网状结构,根据需要自由加载Class。
破坏双亲模式的例子,先从底层的ClassLoader加载。OrderClassLoader的部分实现:
- protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- // First, check if the class has already been loaded
- Class re=findClass(name);
- if(re==null){
- System.out.println(“无法载入类:”+name+“ 需要请求父加载器");
- return super.loadClass(name,resolve);
- }
- return re;
- }
findClass(String name )实现如下:
- protected Class<?> findClass(String className) throws ClassNotFoundException {
- Class clazz = this.findLoadedClass(className);
//每个类只加载一次,会查找、定义、加载- if (null == clazz) {
- try {
- String classFile = getClassFile(className);
- FileInputStream fis = new FileInputStream(classFile);
- FileChannel fileC = fis.getChannel();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- WritableByteChannel outC = Channels.newChannel(baos);
- ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
- //-----------------省略部分代码-------------------//
- fis.close();
- byte[] bytes = baos.toByteArray();
- clazz = defineClass(className, bytes, 0, bytes.length);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return clazz;
- }
深入探究jvm之类装载器的更多相关文章
- 探究JVM——垃圾回收
垃圾回收主要考虑三件事情:哪些内存需要回收?什么时候回收?如何回收? 一.哪些内存需要回收? 堆内存:对于JVM 来说,垃圾回收主要是针对堆内存中的对象实例. 方法区:垃圾收集行为在方法区是比较少出现 ...
- 探究JVM和GC
1.Java堆中各代分布: 图1:Java堆中各代分布 Young:主要是用来存放新生的对象. Old:主要存放应用程序中生命周期长的内存对象. Permanent:是指内存的永久保存区域,主要存放C ...
- 深入探究jvm之GC的算法及种类
一.GC基本概念 GC(Garbage Collection)垃圾收集,1960年最早在List中使用.在Java中GC回收的对象是堆空间和永久区,可以有效避免程序员人为造成内存泄漏问题.将堆空间和永 ...
- 深入探究jvm之GC的参数调优
在上一篇博客记录了GC的算法及种类,这篇博客主要记录一下GC的参数如何调整以提高jvm的性能. 一.堆的回顾: 堆的内存空间总体分为新生代和老年代,老年代存放的老年对象,新构造的对象分配在eden区中 ...
- 深入探究JVM(1) - Java的内存区域解析
http://blog.csdn.net/sczyh22/article/details/46652901<br>Java 虚拟机在执行Java程序的时候会把它管理的内存区域划为几部分,这 ...
- 深入探究JVM(2) - 探秘Metaspace
Java 8彻底将永久代移除出了HotSpot JVM,将其原有的数据迁移至Java Heap或Metaspace.这一篇文章我们来总结一下Metaspace(元空间)的特性.如有错误,敬请指出,谢谢 ...
- 深入探究JVM之内存结构及字符串常量池
前言 Java作为一种平台无关性的语言,其主要依靠于Java虚拟机--JVM,我们写好的代码会被编译成class文件,再由JVM进行加载.解析.执行,而JVM有统一的规范,所以我们不需要像C++那样需 ...
- 深入探究JVM之对象创建及分配策略
@ 目录 前言 正文 一.对象的创建方式 二.对象的创建过程 对象在哪里创建 分配内存 对象的内存布局 三.对象的访问定位 四.判断对象的存活 对象生死 回收方法区 引用 对象的自我拯救 五.对象的分 ...
- 深入探究JVM之垃圾回收器
@ 目录 前言 正文 一.垃圾收集算法 标记-复制 标记-清除 标记-整理 分代回收 二.常用的垃圾回收器 Serial/SerialOld ParNew Parallel Scavenge/Para ...
随机推荐
- 浅谈ES6新特性
ES6的了解 新增模板字符串(为JavaScript提供了简单的字符串插值功能).箭头函数(操作符左边为输入的参数,而右边则是进行的操作以及返回的值Inputs=>outputs.).for-o ...
- Python 三元条件判断表达式(and or/if else)
参考: http://wangye.org/blog/archives/690/
- nginx unit 安装试用
1. yum 源 nano /etc/yum.repos.d/unit.repo 内容 [unit] name=unit repo baseurl=https://packages.nginx.org ...
- wordpress域名解析到了网站,但是点击其他页面会出现ip而不是域名
1.前提域名可以访问你的网站证明解析没问题 2.那就是wp后台的设置问题,将url和站点url改为你的域名http://www.eovision.cc清理缓存即可 亲测可用,如果改了出现页面 ...
- Python笔试面试题_牛客(待完善)
中文,免费,零起点,完整示例,基于最新的Python 3版本.https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42 ...
- 安装MySQL-python 的问题
安装MySQL-python 的问题 1.CentOS下载mysql-devel安装 yum install mysql-devel 2.Ubuntu下不叫mysql-devel,而是叫libmysq ...
- Clustershell集群管理
在运维实战中,如果有若干台数据库服务器,想对这些服务器进行同等动作,比如查看它们当前的即时负载情况,查看它们的主机名,分发文件等等,这个时候该怎么办?一个个登陆服务器去操作,太傻帽了!写个shell去 ...
- postman参数化的方法
1.准备csv格式的文件(注意第一行是是引用参数的名称) 2.编写请求,应用变量参数,并且设置断言 引用变量参数 3.把这个请求的文件夹runner一下批量执行 4.把第一步变量的csv文件在runn ...
- 杂项-DB:内存数据库
ylbtech-杂项-DB:内存数据库 内存数据库,顾名思义就是将数据放在内存中直接操作的数据库.相对于磁盘,内存的数据读写速度要高出几个数量级,将数据保存在内存中相比从磁盘上访问能够极大地提高应用的 ...
- each函数遍历select标签下的所有option选项
如下: <select id="asd" name="sweet1"> <option value=1>--四川--</optio ...