异常将上下文初始化事件发送到类的侦听器实例_Java虚拟机从入门到入土之JVM的类加载机制
作者:六脉神剑
转载于:https://juejin.im/post/5e1aaf626fb9a0301d11ac8e
JVM总体概述
JVM总体上是由
- 类装载子系统(ClassLoader)
- 运行时数据区
- 执行引擎
- 内存回收
- 类文件结构
以上5个部分组成,每一个都是非常重要的,如果你要了解JVM,要学习JVM调优,那么只能是一个个去把他们啃了
什么是类加载机制
书上的原话:
虚拟机把描述类的数据从Class文件加载到内存,并对这些数据进行校验,转换 解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制
类加载的时机
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括
- 加载
- 验证
- 准备
- 解析
- 初始化
- 使用
- 卸载
总共是7个阶段
理解类加载三个字
首先 类 是指的.Class文件类,那么怎么生成这个文件呢?
- Java代码编译
- 原本就是.Class 文件
- 动态代理生成
等等 还有很多
那么 加载 这2个字应该怎么理解呢 大家可以看下图
本地的.Class文件通过类加载器加载到JVM内存中的方法区里面,然后通过这个对象来访问数据区的数据
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流代表的静态存储结构转化成方法区的二进制字节流
- 再内存的方法区生成这个类的Java.lang.Class对象,作为这个类各个数据访问的入口
五种必须初始化的情况
Java并没用规定生命时候进行类加载的第一阶段,但是对于初始化阶段,虚拟机有严格的规范
- 遇到new 关键字的时候
- 使用reflect包的方法的时候
- 当初始化一个类的时候发现父类还没初始化,必须先初始化父类
- 当虚拟机启动的时候,加载main方法的类
- 当使用1.7的动态语言支持的时候(这块没有接触过,有没有大佬懂的)
验证阶段
分为以下几种样装情况
- 文件格式的验证,验证当前字节流是否能被JVM识别
- 元数据的验证,验证它的父类,它的继承,是否是抽象类等
- 字节码验证,验证逻辑是否合理
- 符合引用的验证 验证是否能通过生成的Class对象找到对应的数据
准备阶段
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:
1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。
2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。
假设一个类变量的定义为:
public static int value = 3;
那么变量value在准备阶段过后的初始值为0,而不是3,因为这时候尚未开始执行任何Java方法,而把value赋值为3的putstatic指令是在程序编译后,存放于类构造器()方法之中的,所以把value赋值为3的动作将在初始化阶段才会执行。
下表列出了Java中所有基本数据类型以及reference类型的默认零值:
这里还需要注意如下几点:
- 对基本数据类型来说,对于类变量(static)和全局变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值,而- 对于局部变量来说,在使用前必须显式地为其赋值,否则编译时不通过。
- 对于同时被static和final修饰的常量,必须在声明的时候就为其显式地赋值,否则编译时不通过;而只被final修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值。
- 对于引用数据类型reference来说,如数组引用、对象引用等,如果没有对其进行显式地赋值而直接使用,系统都会为其赋予默认的零值,即null。
- 如果在数组初始化时没有对数组中的各元素赋值,那么其中的元素将根据对应的数据类型而被赋予默认的零值。
如果类字段的字段属性表中存在ConstantValue属性,即同时被final和static修饰,那么在准备阶段变量value就会被初始化为ConstValue属性所指定的值。
解析阶段
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程 ##初始化阶段 到了初始化阶段,才真正开始执行类中定义的Java代码
类加载器
站在Java虚拟机的角度来讲,只存在两种不同的类加载器:
- 启动类加载器:它使用C++实现(这里仅限于Hotspot,也就是JDK1.5之后默认的虚拟机,有很多其他的虚拟机是用Java语言实现的),是虚拟机自身的一部分。
- 所有其他的类加载器:这些类加载器都由Java语言实现,独立于虚拟机之外,并且全部继承自抽象类java.lang.ClassLoader,这些类加载器需要由启动类加载器加载到内存中之后才能去加载其他的类。
站在Java开发人员的角度来看,类加载器可以大致划分为以下四类:
- 启动类加载器 (C实现)
- 扩展类加载器 (ClassLoader)
- 应用程序加载器 (ClassLoader)
- 自定义加载器 (ClassLoader)
这几种类加载器的层次关系如下图所示:
双亲委派模型
类加载器之间的这种层次关系叫做双亲委派模型。 双亲委派模型要求除了顶层的启动类加载器(Bootstrap ClassLoader)外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不是以继承关系实现的,而是用组合实现的。
双亲委派模型的工作过程
由我来概况就是 八个字 向上检查,从下加载 如果一个类接受到类加载请求,他自己不会去加载这个请求,而是将这个类加载请求委派给父类加载器,这样一层一层传送,直到到达启动类加载器(Bootstrap ClassLoader)。 只有当父类加载器无法加载这个请求时,子加载器才会尝试自己去加载。
双亲委派模型的代码实现
双亲委派模型的代码实现集中在java.lang.ClassLoader的loadClass()方法当中。
- 首先检查类是否被加载,没有则调用父类加载器的loadClass()方法;
- 若父类加载器为空,则默认使用启动类加载器作为父加载器;
- 若父类加载失败,抛出ClassNotFoundException 异常后,再调用自己的findClass() 方法。 loadClass源代码如下:
protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException { //1 首先检查类是否被加载 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //2 没有则调用父类加载器的loadClass()方法; c = parent.loadClass(name, false); } else { //3 若父类加载器为空,则默认使用启动类加载器作为父加载器; c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { //4 若父类加载失败,抛出ClassNotFoundException 异常后,这个方法就是加载的核心代码 c = findClass(name); } } if (resolve) { //5 再调用自己的findClass() 方法。 resolveClass(c); } return c;}
自定义类加载器
class NetworkClassLoader extends ClassLoader { * String host; * int port; * * public Class findClass(String name) { * byte[] b = loadClassData(name); * return defineClass(name, b, 0, b.length); * } * private byte[] loadClassData(String name) { // load the class data from the connection . . . } }
这个就是官方的例子
破环双亲委派
双亲委派模型很好的解决了各个类加载器加载基础类的统一性问题。即越基础的类由越上层的加载器进行加载。 若加载的基础类中需要回调用户代码,而这时顶层的类加载器无法识别这些用户代码,怎么办呢?这时就需要破坏双亲委派模型了。
java默认的线程上下文类加载器是系统类加载器(AppClassLoader).
// Now create the class loader to use to launch the application try { loader = AppClassLoader.getAppClassLoader(extcl); } catch (IOException e) { throw new InternalError( "Could not create application class loader" ); } // Also set the context class loader for the primordial thread. Thread.currentThread().setContextClassLoader(loader);
以上代码摘自sun.misc.Launch的无参构造函数Launch()。
使用线程上下文类加载器,可以在执行线程中,抛弃双亲委派加载链模式,使用线程上下文里的类加载器加载类.
典型的例子有,通过线程上下文来加载第三方库jndi实现,而不依赖于双亲委派.
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
结尾
今天把类加载机制好好讲了一下,这样大家就更加的熟悉了内的加载过程,对于Java开发是有好处的
异常将上下文初始化事件发送到类的侦听器实例_Java虚拟机从入门到入土之JVM的类加载机制相关推荐
- 异常将上下文初始化事件发送到类的侦听器实例._Java CLassLoader类加载器详解,一点课堂(多岸学院)...
Java CLassLoader 类加载器(class loader)是 Java™中的一个很重要的概念.类加载器负责加载 Java 类的字节代码到 Java 虚拟机中.本文首先详细介绍了 Java ...
- 严重: 异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.co
原文 BeanFactory创建Bean实例错误,原因可能是项目的builderpath中的JDK版本莫名被调成默认的了,如javase1.5,重新移除添加系统的jdk即可. 2022.2.14 补充 ...
- 严重:异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.context.ContextLoaderListener] 以解决
严重: 异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.context.ContextLoaderListener] org.springframewor ...
- 异常将上下文初始化事件发送到类的侦听器实例_Spring的Bean实例化原理,这一次彻底搞懂了!...
前言 之前分析了Spring XML和注解的解析原理,并将其封装为BeanDefinition对象存放到IOC容器中,而这些只是refresh方法中的其中一个步骤--obtainFreshBeanFa ...
- java消息失败重发_JMS消息在JMS侦听器中重新发送异常
Javadoc为 org.springframework.jms.listener.AbstractMessageListenerContainer 表示,如果 "sessionAcknow ...
- 侦听127.0.01_Spring 4.2中由注释驱动的事件侦听器
侦听127.0.01 介绍 在应用程序内交换事件已成为许多应用程序中必不可少的部分,幸运的是,Spring为瞬态事件(*)提供了完整的基础结构. 最近对事务绑定事件的重构为我提供了一个借口,以在实践中 ...
- Spring 4.2中由注释驱动的事件侦听器
介绍 在应用程序内交换事件已成为许多应用程序中必不可少的部分,幸运的是,Spring为瞬态事件(*)提供了完整的基础结构. 最近对事务绑定事件的重构为我提供了一个借口,以在实践中检查Spring 4. ...
- vue 侦听器侦听对象属性_Spring中的异步和事务性事件侦听器
vue 侦听器侦听对象属性 内置的事件发布功能从Spring的早期版本开始存在,并且对于处理同一应用程序上下文中Spring组件之间的基本通信仍然有用. 通常,应用程序可以生成应用程序事件(可以是任意 ...
- Spring中的异步和事务性事件侦听器
内置的事件发布功能从Spring的早期版本开始存在,对于处理同一应用程序上下文中Spring组件之间的基本通信仍然有用. 通常,应用程序可以生成应用程序事件(可以是任意对象)并侦听它们. 整个机制非常 ...
- 读《Javascript高级程序设计》中的javascript事件处理程序(事件侦听器)心得
今天读了<Javascript高级程序设计>中的javascript事件处理程序(事件侦听器)部分的内容,总结一些自己的心得: 事件就是用户或者浏览器自身执行的某种动作.例如click . ...
最新文章
- Windows CE 6.0正式发布 源代码100%开放
- Kernel 社区 开发准备工作mutt 邮件使用
- C/C++笔试、面试题
- python怎么去学_Python第一步的学习,前辈告诉你怎么去学
- 【UOJ#67】新年的毒瘤 Tarjan 割点
- iOS中POST请求
- 基于wrapper的特征选择——递归特征消除RFE的python实现
- linux apache tomcat ajp,Linux下apache tomcat ajp session複製
- 金融数据分析与挖掘实战1.4.4-1.5.1
- pythontcp服务器如何关闭阻塞_python 网络编程(socketserver,阻塞,其他方法)
- vba 定义数组_excel自定义排序的三种方法
- hdu 2570 (贪心)
- python 爬取直播_Python爬取直播视频
- cmd命令查询硬盘序列号,磁盘ID,MAC地址
- 后盾网php多少钱_后盾网向军老师PHP视频教程
- 信捷plc c 语言全局变量,三菱PLC编程中,跳转指令CJ、子程序调用CALL和中断指令有什么区别?...
- fn映射 mac 键盘_如何在Mac OS X下修改Fn键的属性?
- 智能车单车组之平衡控制理论分析篇
- Hive(2)——使用总结
- 编程语言的学习路线通论
热门文章
- 不确定性原理的前世今生 · 数学篇(一)
- 阿里云张献涛:如何修炼云计算的弹指神通
- 让数据更安全可用,阿里云存储多项新功能发布
- Java画韦恩图_R绘制韦恩图 | Venn图
- 【回归预测】基于matlab麻雀算法优化LSSVM回归预测【含Matlab源码 1128期】
- 【预测模型】基于matlab模糊小波神经网络目标威胁评估【含Matlab源码 1621期】
- 毕设题目:Matlab手势识别
- 【基础处理】基于matlab Fxlms算法有源噪声控制系统【含Matlab源码 1394期】
- Spring中@Autowired注入static静态变量空指针异常
- deque插入和删除操作