JVM类加载机制详解,建议看这一篇就够了,深入浅出总结的十分详细!
类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
类加载的时机
- 遇到new(比如new Student())、getstatic和putstatic(读取或设置一个类的静态字段,如下代码,读取被final修饰并已在编译器把结果放入常量池的静态字段除外)、invokestatic(调用类的静态方法)这四条指令时,如果对应的类没有初始化,则要对对应的类先进行初始化。
public class Student{
private static int age;
public static void method(){
}
}
Student.age
Student.method();
- 使用java.lang.reflect包方法时对类进行反射调用的时候。
- 初始化一个类的时候发现其父类还没初始化,要先初始化其父类。
- 当虚拟机开始启动时,用户需要指定一个主类(main),虚拟机会限制性这个主类的初始化。
类加载的过程
类加载过程是如下图所示的一个流水线过程,其中连接过程可细化为验证、准备和解析三个小步骤。
加载
class文件–>class对象
“加载”过程主要是靠类加载器实现的,包括用户自定义类加载器。
加载的过程
在加载过程中,JVM主要做以下3件事:
- 通过一个类的全限定名来获取定义此类的二进制字节流(class文件)。在程序运行过程中,当要访问一个类时,若发现这个类尚未被加载,并满足类初始化的条件时,就根据要被初始化的这个类的全限定名找到该类的二进制字节流,开始加载过程
- 将这个字节流的静态存储结构转化为方法区的运行时数据结构(即Class对象)
- 在内存中创建一个该类的java.lang.Class对象,作为方法区该类的各种数据的访问入口
程序在运行中所有对该类的访问都通过这个类对象,也就是这个Class对象是提供给外界访问该类的接口。
加载源
JVM规范对于加载过程给予了较大的宽松度,一般二进制字节流都从已经编译好的本地class文件中读取,此外还可以从这些地方读取:zip包(jar、war、ear等),由jsp文件中生成对应的Class类,数据库,网络,运行时计算生成(动态代理技术)。
加载过程的注意点
- 类和数组加载的区别:非数组类是由类加载器来完成;数组类本身不通过类加载器创建,它是由java虚拟机直接创建,但数组类与类加载器有很密切的关系,因为数组类的元素类型最终要靠类加载器创建。
- HotSpot将Class对象存放在方法区
验证
各种检查
验证阶段比较耗时,它非常重要但不一定必要,可用-Xverify:none参数关闭,以缩短类加载时间。
验证的目的
保证二进制字节流的信息符合虚拟机规范,并没有安全问题。
验证的必要性
Java语言的安全性是通过编译器来保证的,但编译器和虚拟机是两个独立的东西,虚拟机只认二进制字节流,它不会管所获得的二进制字节流是哪来的。当然,如果是编译器给它的那么就相对安全,但如果是从其它途径获得的,那么无法确保该二进制字节流是安全的。
验证的过程
其中文件格式验证阶段是基于二进制字节流进行的,只有通过本阶段验证,才被允许存放到方法区。后面的三个验证阶段都是基于方法区的存储结构进行,不会再直接操作字节流。
准备
为static分配内存并初始化0值。JDK1.7之前在方法区,1.7之后在堆。
仅仅为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即零值,这里不包含用final修饰的static,因为final在编译的时候就会分配好,同时这里也不会为实例变量分配初始化。类变量(静态变量)会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
准备阶段主要完成两件事情:
- 为已在方法区中的类的静态成员变量分配内存;
- 为静态成员变量设置初始值,具体初始值为下图所示。
注意:
public static int x = 1000;
实际上变量x在准备阶段过后的初始值为0,而不是1000。将x赋值为1000是在初始化阶段完成。
解析
将符号引用替换为直接引用
解析是虚拟机将常量池的符号引用替换为直接引用的过程。
初始化
调用方法
初始化过程就是调用类初始化方法的过程,完成对static修饰的类变量的手动赋值还有主动调用静态代码块。
注意点:此步骤中虚拟机会保证在多线程环境中一个类的方法被正确地加锁(静态内部类)
类加载器介绍
启动类加载器:
由C++实现,不是ClassLoader子类。
负责加载JAVA_HOME\lib目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt,jar)的类。
扩展类加载器:
负责加载JAVA_HOME\lib\ext目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
应用程序类加载器:
负责加载用户路径(classpath)上的类库。
自定义类加载器:
上述的加载器只能加载指定目录下的jar和class,如果想加载其他位置的jar或类时,则需要实现自定义类加载器来加载。
比如要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现特定业务逻辑,此时默认的ClassLoader就不能满足我们的需求,需要定义自己的ClassLoader。
双亲委派模型
JVM的类加载是通过多层次的类加载器来完成的,类的层次关系和加载顺序可以由下图来描述:
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader组层检查,只要某个classloader已加载就视为已加载此类,保证此类只加载一次。而加载的顺序是自顶向下,也就是由上层来组层尝试加载此类。这种类加载的层次关系就是双亲委派模型。
需注意的点:
- 当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器;
- 只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。
为什么使用双亲委派这种模型
因为这样可以避免类的重复加载,当父classloader经加载了该类的时候,就没必要子classloader再加载一次。
考虑到安全因素,我们试想一下,如果不适用这种委托模型,那我们就可以随时使用自定义的String来动态替代java核心api重定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况。因为String已经在启动时就被Bootstrap ClassLoader加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非改编JDK中ClassLoader搜索类的默认算法。
判定两个Class对象是否相同的依据
- class字节码是否相同
- ClassLoader是否相同
JVM在判定两个class是否相同,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。类的全限定名完全相同,但是加载它的类加载器不同,那么在方法区中会产生不同的Class对象。
只有两者同时满⾜的情况下,JVM才认为这两个class是相同的。就算两个class是同⼀份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同class。
破坏双亲委派模型
为什么需要破坏双亲委派
因为在某些情况下父类加载器需要加载的class文件由于受到加载范围的限制,父类加载器无法加载到需要的文件,这个时候就需要委托子类加载器进行加载。
双亲委派模型是在JDK1.2以后才使用的,但是有一些核心的API类在JDK1.2之前就已经写好了。
简单理解双亲委派模型是子类加载器去委托父类加载器完成类加载的工作,而破坏双亲委派模型是父类加载器去委托子类加载器完成类加载的工作。
以Driver接口为例,由于Driver接口定义在jdk当中,而其实现由各个数据库的服务商来提供,比如mysql就写了MySQL Connector,这些实现类都是以jar包的形式放到classpath目录下。
那么问题就来了,DriverManager(也由jdk提供,JDK1.2之前就写好了)要加载各个实现了Driver接口的实现类(在classpath下),然后进行管理,但是DriverManager由启动类加载器加载,只能加载JAVA_HOME\lib下的文件,而其实现是由服务商提供的,有系统类加载器加载,这个时候就需要启动类加载器来委托子类加载器来加载Driver实现,从而破坏了双亲委派。如下图所示。
最后
欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!
JVM类加载机制详解,建议看这一篇就够了,深入浅出总结的十分详细!的更多相关文章
- JVM类加载机制详解(二)类加载器与双亲委派模型
在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...
- JVM类加载机制详解
引言 如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 在加载阶段,虚拟机需要完成以下三件事情: 1)通过一个类的全限定名来获取定义此 ...
- JVM 类加载机制详解
如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lan ...
- 面试题:JVM类加载机制详解(一)JVM类加载过程 背1
首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...
- JVM类加载机制详解(一)JVM类加载过程
http://blog.csdn.net/zhangliangzi/article/details/51319033 http://chenzhou123520.iteye.com/blog/1597 ...
- Linux 虚拟网络设备 veth-pair 详解,看这一篇就够了
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 前面这篇文章介 ...
- 【K8S】Service服务详解,看这一篇就够了!!
k8s用命名空间namespace把资源进行隔离,默认情况下,相同的命名空间里的服务可以相互通讯,反之进行隔离. 1.1 Service Kubernetes中一个应用服务会有一个或多个实例(Pod, ...
- Java程序员的必备知识-类加载机制详解
类加载器的概念 类加载器是一个用来加载类文件的类. Java源代码通过javac编译器编译成类文件.然后JVM来执行类文件中的字节码来执行程序.类加载器负责加载文件系统.网络或其他来源的类文件. JV ...
- SPA路由机制详解(看不懂不要钱~~)
前言 总所周知,随着前端应用的业务功能起来越复杂,用户对于使用体验的要求越来越高,单面(SPA)成为前端应用的主流形式.而大型单页应用最显著特点之一就是采用的前端路由跳转子页面系统,通过改变页面的UR ...
随机推荐
- C#数据结构-线程安全队列
什么是线程安全? 答:线程安全是多线程编程时的计算机程序代码中的一个概念.在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意 ...
- adb命令如何获取appPackage和appActivity的信息
如何获取appPackage和appActivity的信息,这里有一个极为实用的命令:adb shell dumpsys activity |find "mFocusedActivity&q ...
- Git的介绍及使用
一.配置用户信息: 配置用户名和邮箱: $ git config --global user.name "chunyu" $ git config --global user.em ...
- It is better to have the ability of fast learning
来自某位大佬: 内功=算法+数据结构+编译原理+操作系统原理+软件工程+英文 十足的自信心+强烈的求知欲+对Programming&&C&&CPP的执着+百折不挠的钻研 ...
- leetcode144 longest-palindromic-substring
题目描述 找出给出的字符串S中最长的回文子串.假设S的最大长度为1000,并且只存在唯一解. Given a string S, find the longest palindromic substr ...
- 从零到千万用户,我是如何一步步优化MySQL数据库的?
写在前面 很多小伙伴留言说让我写一些工作过程中的真实案例,写些啥呢?想来想去,写一篇我在以前公司从零开始到用户超千万的数据库架构升级演变的过程吧. 本文记录了我之前初到一家创业公司,从零开始到用户超千 ...
- C#/VB.NET 给Excel添加、删除数字签名
一.程序环境 以下内容通过C#及VB.NET代码demo示例介绍如何给Excel文档添加数字签名,以及删除Excel文档中已有的数字签名.工具使用最近发布的Spire.XLS for .NET 版本1 ...
- LeetCode 热题 HOT 100(05,正则表达式匹配)
LeetCode 热题 HOT 100(05,正则表达式匹配) 不够优秀,发量尚多,千锤百炼,方可成佛. 算法的重要性不言而喻,无论你是研究者,还是最近比较火热的IT 打工人,都理应需要一定的算法能力 ...
- 内网渗透 day13-漏洞复现
漏洞复现 目录 1. 永恒之蓝(445端口) 2. 手动微笑漏洞(21端口 vsftpd2.3.4版本) 3. ingres数据库DBMS后门(1524端口) 4. distcc漏洞(3632) 5. ...
- SpringBoot整合JWT实战详解
jwt 介绍就不多说了,下面通过代码演示开发过程中jwt 的使用. (1)在pom.xml中引入对应的jar <dependency> <groupId>io.jsonwebt ...