JVM 虚拟机&&类加载(一)
虚拟机
虚拟机简介
Java 虚拟机(JVM)是运行java程序的抽象计算机,它是计算机设备的规范,可以采用不同方式进行实现,java 程序通过运行在JVM中实现跨平台,一次编译到处运行,不同的操作系统有不同的JDK版本,通过调用JNI方法去实现调用不同操作系统的方法
A[java源文件]
B[class二进制字节码]
C[JVM]
D((Java native接口))
F[windows本地方法]
H[linux本地方法]
I[macOS本地方法]
A-.编译.->B
B-.加载.->C
C-.打开文件.->D
D-.windows-JDK.->F
D-.linux-JDK.->H
D-.macOS-JDK.->I
Java虚拟机不和包括java的语言绑定,它只和Class文件这种特定的二进制文件格式绑定,class文件中包含了虚拟机指令集和符号表以及若干其他辅信息。
A[java程序]
B[JRuby程序]
C[Groovy程序]
D[java编译器]
E[JRuby编译器]
F[Groovy编译器]
H[字节码]
I[Java虚拟机]
A-.*.java文件.->D
B-.*.rb文件.->E
C-.*.groovy文件.->F
D-->H
E-->H
F-->H
H-.*.class文件.->I
虚拟机产品
- Sun HotSpot(JVM)
- BEA JRocket
- IBM J9
- Microsoft JVM
- Google Android Dalvik
Class 文件
Class文件是以8位字节基础单位的二进制流,各项数据严格按照顺序排列在class文件中,中间没有任何分隔符,如果超过8位,是按照高位在前的方式存储的。
Class文件的组成
- java虚拟机指令集
- 符号表
- 其他辅助信息
常量池
java代码在编译后,Class文件并表不会保存各个方法,字段在内存里面的最终布局,因为在编译期间并不知道引用类的实际地址,因此只能使用符号描述来代替,当虚拟机真正的运行的时候,需要从class文件的常量池获得对应的符号引用,然后在类的创建或者运行时解析称具体的内存地址
也就是说在类被加载的时候,一定是在内存中分配了具体的地址,这样在解析的时候就可以替换符号引用成真正的地址。
Class文件的常量池主要存放2大类型:
- 字面量 字面量是接近java语言层面的常量概念,如文本字符串,声明final的常量值等
- 符号引用 主要包括类和接口的全称限定名,字段的名称和描述符,方法的名称和描述符
虚拟机类加载
类加载到JVM中生命空间
A[加载Loading]
B[验证Verification]
C[准备Preparation]
D[解析Resolution]
E[初始化Initialization]
F[使用Using]
G[卸载Unloading]
A-->B
D-->E
E-->F
F-->G
subgraph 连接
B-->C
C-->D
end
加载
加载的过程做的事情是:
- 获取二进制字节流
- 静态存储结构转化为方法区的运行时结数据结构
- 在java堆对象里面生成一个类对象,作为方法区的入口
Java中获取二进制字节流的途径有
- 从Zip,Jar,War等格式文件中获取
- 从网络中获取 Applet应用
- 运行时计算生成,动态代理技术
- JSP应用生成对应的Class类
验证
验证是连接的第一步,要确保Class文件中的字节流包含的信息符合JVM的要求,验证阶段大体分为如下几个阶段
- 文件格式验证 验证文件标识是否正确,版本号是否能匹配当前JVM,常量池中的常量是否有不支持的类型等 。经过这个阶段的验证后,字节流才会进入的方法区中进行存储,后面的验证只是基于方法区的存储结构验证,不会在验证字节流
- 元数据验证 主要是对字节码描述的语义进行分析,保证符号java语言的规范,类的继承,抽象类的实现等等
- 字节码验证
- 符号引用验证 该阶段发生在符号引用替换成直接引用时,验证符号引用的匹配校验等等
准备
准备阶段是为了给类分配内存并设置类变量的初始化Clint,这些变量使用的内存,都在方法区中进行分配,只是对类变量进行内存分配(Static 修饰的)不包括实例变量,实例变量是随着类实例化后一起在堆中分配的
:类的初始化 父静态变量 父类静态块 子类静态变量 子类的静态块
: 对象的初始化 父类的变量初始化,父类的构造函数。。。。。。
解析
解析的目的是将常量池中的符号引用替换为直接引用
字段的解析
class A extends B implements C{
private String str; //字段的解析
}
A{A是否能匹配}
B{B是否能匹配}
C{C是否能匹配}
A-->|是|E[结束]
A-->|否|C
subgraph 接口
C-->|是|F(结束)
end
subgraph 父类
C-->|否|B
B-->|是|I(结束)
B-->|否|J((抛出异常))
end
类方法解析
- 先查找本类
- 然后父类中递归查找
- 在类实现的接口列表及它们的父接口
接口方法解析
- 先查找本接口
- 在接口的父接口中递归查找
类加载器
通过类的全限定名来获取描述类的二进制字节流,把类加载这个阶段中的动作放到虚拟机外部去实现,以便应用程序能够自己决定如果去获取自己需要的类,实现这个动作加做类加载器。
Java中有abstract class ClassLoader 类。
ClassLoader.loadClass类的核心总结是:
- 同一个时间只能允许一个线程去加载类
- 在加载类之前会检查下是否已经加载过,只有没有被加载的才能去被加载
- 能父加载器加载的绝不会交给子加载器去加载,为什么要这样,是为了保证安全,比如你自己写个相同名字的java底层类比如java.util.ArrayList通过子加载器到内存中,那不是乱套了么,到时候植入个病毒代码,用的人 不GG了
- 父加载器加载不到的才会交给子加载器加载
加载器类型
从JVM的角度去看 只存在2中类加载器,一种是启动类加载器(Bootstrap ClassLoader)是由C++语言实现的。还有一种是另外的类加载器,是由java语言实现的,独立于JVM,全部是继承了java.lang.ClassLoader
如果从我们开发角度去看,分为3中类加载器:
- Bootstrap ClassLoader启动类加载器,负责Java_Home/lib 下面的类库加载到内存中,由于这边涉及到虚拟机本地的实现细节,所有我们开发者无法获取到类加载器的引用。
- Extension ClassLoader 扩展类加载器 负责加载Java_Home/lib/ext或者由系统变量java.ext.dirs指定位置的类加载到内存中
//Launcher.class文件中
static class ExtClassLoader extends URLClassLoader {
private static volatile Launcher.ExtClassLoader instance;
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
if (instance == null) {
Class var0 = Launcher.ExtClassLoader.class;
synchronized(Launcher.ExtClassLoader.class) {
if (instance == null) {
instance = createExtClassLoader();
}
}
}
return instance;
}
....
}
- Application ClassLoader 应用程序类鸡加载器,它负责系统路径ClassPath中指定的类库加载到内存中,这个类加载器是
ClassLoader类中的getSystemClassLoader静态方法的返回值
//java/lang/ClassLoader.java 文件中
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
A[Bootstrap加载器]
B[ExtClassLoader]
C[AppClassLoader]
D[UseClassLoader1]
F[UseClassLoader2]
B --> A
subgraph 非JVM
C --> B
D --> C
F --> C
end
有兴趣的可以自己执行看下
String appClassLoaderPath = System.getProperty("java.class.path");
System.out.println(appClassLoaderPath);
String extClassLoaderPath = System.getProperty("java.ext.dirs");
System.out.println(extClassLoaderPath);
String bootClassLoaderPath = System.getProperty("sun.boot.class.path");
System.out.println(bootClassLoaderPath);
双亲委派
什么是双亲委派
- 如果一个类加载器收到了某个类的加载请求,则该加载器不会加载,而是把这个请求抛给父类加载器,因此所有的类加载请求都会到达顶端的类加载器
- 只有当父类加载器在其范围内没法找到所需要类,才会把结果反馈给子加载器,子加载器在尝试自己加载
如何打断
自己写个类加载器 并重写loadClass方法 就可以了!
为什么要使用双亲委派
- 对任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。判断两个类是否"相等",必须是在这两个类被同一个类加载器加载的前提下。
- 基于双亲委派模型设计,那么Java中基础类,如Object类重复多次的问题就不会存在了,因为经过层层传递,加载请求最终都会被BootstrapClassLoader所响应。加载的Object类也会只有一个,否则如果用户自己编写了一个java.lang.Object类,并把它放到了ClassPath中,会出现很多个Object类,这样Java类型体系中最最基础的行为都无法保证,应用程序也将一片混乱。
JVM 虚拟机&&类加载(一)的更多相关文章
- 深入理解JVM - 虚拟机类加载机制 - 第七章
类加载的时机类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括了:加载/验证/准备/解析/初始化/使用/卸载七个阶段.其中验证/准备和解析统称为连接(Linking). 加载.验证.准 ...
- JVM虚拟机 类加载过程与类加载器
目录 前言 类的生命周期 类加载过程 加载 连接 验证 准备 解析 初始化 类加载器 三大类加载器 双亲委派模型 概念 为什么要使用双亲委派模型 源码分析 反双亲委派模型 参考 前言 类装载器子系统是 ...
- JVM虚拟机-类加载器子系统
转自博客:http://www.cnblogs.com/muffe/p/3541189.html 还有一些自己补充的知识点 一.类加载器基本概念 顾名思义,类加载器(class loader)用来 ...
- java jvm虚拟机类加载器
在Java中任意一个类都是由这个类本身和加载这个类的类加载器来确定这个类在JVM中的唯一性. 类加载器 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到J ...
- java jvm虚拟机类加载过程
加载 在加载阶段, 虚拟机需要完成以下3件事情:1) 通过一个类的全限定名来获取定义此类的二进制字节流.2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构.3) 在内存中生成一个代表这 ...
- JVM虚拟机类加载机制(一)
类从被加载到虚拟机内存中开始,到卸载出内存截止,整个生命周期包括:加载.验证.准备.解析,初始化.使用.卸载七个阶段.其中验证.准备.解析三个部分统称为连接. 类初始化情况: 遇到new.getsta ...
- java内功 ---- jvm虚拟机原理总结,侧重于虚拟机类加载执行系统
参考书籍:<深入理解java虚拟机>,三天时间用了八个小时看完,像读一本武侠小说,挺爽. 另外需声明:图片都是从我自己的csdn博客转载,所以虽然有csdn标识,但都是我自己画的图片. j ...
- JVM虚拟机深入理解+GC回收+类加载
旭日Follow_24 的CSDN 博客 ,全文地址请点击: https://blog.csdn.net/xuri24/article/details/81455449 一,前言 本文章是读了“深入理 ...
- 【JVM.6】虚拟机类加载机制
一.概述 虚拟机类加载机制:虚拟机把描述类的数据从Class文件中加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型. 与那些在编译时需要进行连接工作的语言不同 ...
随机推荐
- stand up meeting 1/15/2016 && work of weekend 1/16/2016~1/17/2016
part 组员 工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云 组内对生词卡片又重新进行了讨论:准备最后的发布和整个开发的整理 ...
- J - Recommendations CodeForces - 1315D
https://blog.csdn.net/w_udixixi/article/details/104479288 大意:n个数,每个数只能向上加,a[i]+1需要的时间是t[i],求使这n个数无重复 ...
- [PHP] 调用微博API 发微博OAuth2.0
在实际测试中出现很多问题, 第一就是按照文档调用ACCESS_TOKEN的时候费老劲啦,因为是编辑线上的,有好多中文空格,没有看出来!整了好久! 第二个就是在调用api发微博的时候出现乱码!必须把发送 ...
- asp.net core identity学习1
ASP.NET Identity 学习 创建一个Asp.net core mvc项目 添加Nuget包: Microsoft.EntityFrameworkCore.SqlServer 3.1.3 M ...
- Java中的匿名对象代码实例
/* 匿名对象:就是没有名字的对象. 匿名对象的应用场景: A:调用场景,仅仅只调用一次的时候. 注意:调用多次的时候,不合适. 那么,这种匿名调用有什么好处吗? 有,匿名对象调用完毕就是垃圾.可以被 ...
- SweetAlert - 演示6种不同的提示框效果
http://www.sucaihuo.com/js/190.html http://www.cnblogs.com/beiz/p/5238124.html
- opencv-2-VS2017与QT显示图像
opencv-2-VS2017与QT显示图像 opencvqtVSC++ 目的 使用 VS 构建第一个 opencv 程序 使用 QT 构建 第一个 opencv 程序 VS 导入 QT 程序 开始 ...
- awk和sed命令
awk awk是一个强大的编辑工具,可以在无交互的情况下实现相当复杂的文本操作 awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息 a ...
- Web 之 Cookie
Cookie Cookie实际上是一小段的文本信息.客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie.客户端浏览器会把Cookie保存起来.当浏 ...
- 好程序员分享Web前端面试题汇总JS篇之跨域问题
为什么80%的码农都做不了架构师?>>> 好程序员分享Web前端面试题汇总JS篇之跨域问题,接着上一篇文章我们继续来探讨web前端面试必备面试题. 跨域解决方案 1. 通过jso ...