JVM总括四-类加载过程、双亲委派模型、对象实例化过程
JVM总括四-类加载过程、双亲委派模型、对象实例化过程
目录:JVM总括:目录
一、 类加载过程
类加载过程就是将.class文件转化为Class对象,类实例化的过程,(User user = new User(); 这个过程是对象实例化的过程);
一个.class文件只有一个Class对象(字节码对象),可以有无数个对象(例如:new User(););

1、Load:
将编译后的.class文件以二进制流的方式加载到JVM内存中,并转化为特定的数据结构,用到的就是classLoad二类加载器。这个过程中校验cafe babe魔法数、常量池、文件长度、是否有父类等。
其实加载阶段用一句话来描述就是:把代码数据加载到内存中。
2、Link:
分为验证、准备、解析三步。
验证:更为详细的验证,比如:final是否规范(二次赋值不规范)、static是否合理(静态方法必须引用静态变量)、类型是否正确。
准备:当完成字节码文件的校验之后,JVM 便会开始为类变量分配内存并初始化。这里需要注意两个关键点,即内存分配的对象以及初始化的类型。
内存分配的对象。Java 中的变量有「类变量」和「类成员变量」两种类型,「类变量」指的是被 static 修饰的变量,而其他所有类型的变量都属于「类成员变量」。
在准备阶段,JVM 只会为「类变量」分配内存,而不会为「类成员变量」分配内存。「类成员变量」的内存分配需要等到初始化阶段才开始。
比如下面的代码在准备阶段,只会为 age 属性分配内存,而不会为 website 属性分配内存。
public static int age= 3;
public String website = "www.beijingdesigner.com";
例如下面的代码在准备阶段之后,age的值将是 0,而不是 3。
public static int age= 3;
但如果一个变量是常量(被 static final 修饰)的话,那么在准备阶段,属性便会被赋予用户希望的值。例如下面的代码在准备阶段之后,number 的值将是 3,而不是 0。
public static final int number = 3;
这其中的道理我们想下就明白了。
两个语句的区别是一个有 final 关键字修饰,另外一个没有。被final修饰的常量,是不允许再被改变的。所以初始化就是3了。
解析:把类中的符号引用转化为直接引用,完成内存结构布局。(符号引用转化为直接引用:例如:test1() { test2(); },这里test1调用test2方法就是符号引用,但实际test2()通过一个指针指向test2方法的内存地址,这个指针负责调用,它就是直接引用)。
3、Init:
执行类构造器<clinit>,递归初始化父类的静态代码块,不执行构造函数。(先执静态变量,再执行行静态代码块)。
任何小写的class定义的类都有一个魔法属性:class,
int j;
Class<Integer> integerClass = int.class;
Class<Integer> integerClass1 = Integer.class;
二、类加载的原则:双亲委派模型
类加载是怎么确定类文件的位置呢?

1、Bootstrap:最高级的类加载器,装置最核心的类,如:Object、System、String;
2、Extension ClassLoader:JDK9之前的类加载器,以后的为Platform ClassLoader,加载系统的扩展类;
3、Application ClassLoader:应用类加载器,主要加载用户自定义CLASSPATH路径下的类。
类加载器并非继承关系,只是以组合的方式服用父加载器功能,符合优先原则。
其中第二、第三层为java实现,用户可以自定义类加载器,第三层为C++实现,所有为null:
ClassLoader classLoader = Son.class.getClassLoader();
ClassLoader parent = classLoader.getParent();
ClassLoader parent1 = parent.getParent();
System.out.println(classLoader);
System.out.println(parent);
System.out.println(parent1);
-----------------------------------
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@5acf9800
null
获取Bootstrap加载的类库:
URLClassPath bootstrapClassPath = Launcher.getBootstrapClassPath();
URL[] urLs = bootstrapClassPath.getURLs();
for (URL url : urLs){
System.out.println(url.toExternalForm());
}
---------------------------------------------------------
file:/D:/jdk-8u151-windows-x64/JDK/jre/lib/resources.jar
file:/D:/jdk-8u151-windows-x64/JDK/jre/lib/rt.jar
file:/D:/jdk-8u151-windows-x64/JDK/jre/lib/sunrsasign.jar
file:/D:/jdk-8u151-windows-x64/JDK/jre/lib/jsse.jar
file:/D:/jdk-8u151-windows-x64/JDK/jre/lib/jce.jar
file:/D:/jdk-8u151-windows-x64/JDK/jre/lib/charsets.jar
file:/D:/jdk-8u151-windows-x64/JDK/jre/lib/jfr.jar
file:/D:/jdk-8u151-windows-x64/JDK/jre/classes
Bootstrap加载的路径可以追加,在JVM增加启动参数,不过不建议修改:
Xbootclasspath/D:/test/src
自定义类加载器的情况:
1、隔离加载类:框架中吧类加载到不同的环境中
2、修改类加载方式:除了Bootstrap,其他类并非一定要加入。
3、扩展加载源:比如加载数据库;
4、防止源码泄露:可以对类进行加密,那加载的时候需要自定义加载器对类进行解密加载。
自定义ClassLoder,继承ClassLoader,重写findClass(),调用defineClass():
public class UserClassLoader extends ClassLoader {
private String classpath;
public UserClassLoader(String classpath) {
this.classpath = classpath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte [] classDate=getDate(name);
if(classDate==null){
throw new FileNotFoundException();
}
else{
//defineClass方法将字节码转化为类
return defineClass(name,classDate,0,classDate.length);
}
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
//返回类的字节码
private byte[] getDate(String className) throws IOException{
InputStream in = null;
ByteArrayOutputStream out = null;
String path=classpath + File.separatorChar +className.replace('.',File.separatorChar)+".class";
try {
in=new FileInputStream(path);
out=new ByteArrayOutputStream();
byte[] buffer=new byte[2048];
int len=0;
while((len=in.read(buffer))!=-1){
out.write(buffer,0,len);
}
return out.toByteArray();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
finally{
in.close();
out.close();
}
return null;
}
}
UserClassLoader
调用,其中D:\\wp目录下为.class文件,里面有个Son.class
//自定义类加载器的加载路径
UserClassLoader myClassLoader=new UserClassLoader("D:\\wp");
//包名+类名
Class sonClass=myClassLoader.loadClass("com.Son"); if(sonClass!=null){
Object obj=sonClass.newInstance();
System.out.println(sonClass.getClassLoader().toString());
}
三、对象实例化
从字节码、执行步骤两个方面分析
1、字节码方面:
Object object = new Object();,通过javap -verbose -p查看对象创建的字节码指令:

(1)new:如果找不到Class对象就进行类加载,然后分配内存(本类路径上所有的属性都分配),其中对象的引用也是个变量也占内存(4个字节),这个指令执行完毕会把对象的压入虚拟机栈顶。
(2)dup:在栈顶复杂引用,如果<init>有参数,把参数压入操作栈,两个引用,压入栈底的用来赋值或保存到局部变量表中,栈顶引用作为句柄调用相关方法。
(3)invokespecial:调用对象实例化方法,通过栈顶方法调用<init>方法(也就是调用构造方法)。
2、执行步骤
(1)确认类元信息是否存在:接到new指令时,在metaspace检查类元信息是否存在,没有就在双亲委派模式下进行类加载,生成Class对象。
(2)分配对象内存:首先计算对象占用空间大小(成员变量是引用变量就分配4个字节大小的变量空间),在堆中划分内存空间给新对象(分配空间需要进行同步操作,如:cas)。
(3)设定默认值:成员变量设置不同形式的0值;
(4)设置对象头:设置对象的哈希码、锁信息、对象所属的类元信息,设置取决于JVM。
(5)执行init方法:初始化成员变量,执行代码块,调用类的构造方法,把堆对象首地址赋值给引用变量。
四、思考:ClassLoader.loadClasshe和Class.forName区别
//
Class<Son> sonClass = Son.class;
//
Class<?> aClass = Maint.class.getClassLoader().loadClass("com.Son");
//
Class<?> bClass = Class.forName("com.Son");
代码2:
其实这种方法调运的是:ClassLoader.loadClass(name, false)方法
参数一:name,需要加载的类的名称
参数二:false,这个类加载以后是否需要去连接(不需要linking)
代码:3:
其实这种方法调运的是:Class.forName(className, true, ClassLoader.getCallerClassLoader())方法
参数一:className,需要加载的类的名称。
参数二:true,是否对class进行初始化(需要initialize)
参数三:classLoader,对应的类加载器
其中1、2都是将.class文件加载到JVM中,得到Class对象,但是还没有连接(Link),静态代码块不会执行;
代码3得到Class对象并且已经完成初始化<clinit>,静态代码块会执行。( Class.forName("com.mysql.jdbc.Driver");//通过这种方式将驱动注册到驱动管理器上)。
1、2、3都不会执行对象的构造函数。
五、思考:<clinit>和<init>区别
<clinit>是类(Class)初始化执行的方法,<init>是对象初始化执行的方法(构造函数);
JVM总括四-类加载过程、双亲委派模型、对象实例化过程的更多相关文章
- JVM探究之 —— 类加载器-双亲委派模型
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类加载器 ...
- JVM(16)之 双亲委派模型
开发十年,就只剩下这套架构体系了! >>> 在上一篇博文中,我们知道了如何获得二进制的字节流,并根据获得的字节流去装载一个类.同时也了解到类加载器的存在,每个加载器对应着不同的加 ...
- 深入理解JVM(③)虚拟机的类加载器(双亲委派模型)
前言 先解释一下什么是类加载器,通过一个类的全限定名来获取描述该类的二进制字节流,在虚拟机中实现这个动作的代码被称为"类加载器(Class Loader)". 类与类加载器 类加载 ...
- 类文件的结构、JVM 的类加载过程、类加载机制、类加载器、双亲委派模型
一.类文件的结构 我们都知道,各种不同平台的虚拟机,都支持 "字节码 Byte Code" 这种程序存储格式,这构成了 Java 平台无关性的基石.甚至现在平台无关性也开始演变出 ...
- JVM类加载过程与双亲委派模型
类加载过程 类加载过程为JVM将类描述数据从.class文件中加载到内存,并对数据进行解析和初始化,最终形成被JVM直接使用的Java类型.包含: 加载:获取该类的二进制字节流,将字节流代表的静态存储 ...
- JVM的类加载过程以及双亲委派模型详解
JVM的类加载过程以及双亲委派模型详解 这篇文章主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象 ...
- JVM——类加载器的双亲委派模型
类加载器双亲委派模型,如下图所示: 双亲委派模型的工作过程 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此 ...
- jvm类加载器和双亲委派模型
类加载器按照层次,从顶层到底层,分为以下三种: (1)启动类加载器(Bootstrap ClassLoader) 这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootcla ...
- JVM(四)JVM的双亲委派模型
1.两种不同的类加载器 从JAVA虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分:另 ...
随机推荐
- bootstrap modal 移除数据
有时业务需求,在弹出模态框时需要动态的展示,但是不做处理时,每次弹出的都是第一次的数据,所以需要监听模态框关闭,每次关闭需要先清除里面的数据. //监听弹页关闭 $("#myModal&qu ...
- LDAP学习总结
一.简介: LDAP(Light Directory Access Portocol),它是基于X.500标准的轻量级目录访问协议.目录是一个为查询.浏览和搜索而优化的数据库,它成树状结构组织数据,类 ...
- SVN简单使用
如果是window操作系统,默认安装.右键菜单就会有显示SVN 如果已经配置好SVN,直接确定既可以检出. 如果没有配置,那么会显示下面的验证: 输入用户名和密码即可
- C语言----管道
一.管道的概念 管道是一种队列类型的数据结构,它的数据从一端输入,另一端输出.管道最常见的应用是连接两个进程的输入输出,即把一个进程的输出编程另一个进程的输入.shell中存在专门的管道运算符&quo ...
- hadoop 安装过程记录
1)首先配置好了四个linux虚拟机 root pwd:z****l*3 关闭了防火墙 开通了 sshd服务 开通了 ftp服务 配置了 jdk 1.8 配置好了互信 (之前配置的过程忘了!--检查了 ...
- IIS 添加 MIME
参考:https://blog.brain1981.com/727.html 在项目的 Web.Config 里下添加如下段落即可: <?xml version="1.0" ...
- 一切为了解决隐私问题,绿洲实验室Ekiden协议介绍
绿洲实验室官网截图 下一代区块链平台的竞争已经悄然展开,每个月我们都能看到新成立的创业公司宣称,他们要采用区块链解决所有问题.大约80-90%的区块链项目,运行在像Ethereum这样的平台上. 创建 ...
- POI导入工具类
前言 导入的通用方法,包括xls.xlsx的取值方法,非空判断方法,空行判断,处理了手机号读取和日期读取格式问题.这几个方法就可以完成简单读取了,有时间我在优化下. maven依赖 <!-- P ...
- android 开发 View _12_ 用Canvas 绘制一张图片(博客中演示用Canvas画验证码图片)
package net.yt.yuncare.widgets; import android.graphics.Bitmap; import android.graphics.Canvas; impo ...
- Mysql相关问题集锦
1:连接阿里云的服务器时,用navicate连接SSH时提示:或提示指到另一个IP从而进不去. SSH:expected key exchange group packet form server 解 ...