jvm学习一:类加载过程详解
(自学笔记,持续更新,欢迎指正)
我们都知道一个java程序运行要经过编译和执行,但是这太概括了,中间还有很多步骤,今天来说说类加载
学完类加载之后,java运行过程就可以分为 编译 》 类加载 》 执行
类加载主要是由jvm虚拟机负责的,过程非常复杂,类加载分三步 加载 》 连接 》初始化,(这里的加载和本文标题的类加载是不同的,标题的类加载包含了完整的三个步骤)下面详细说说每一步的过程
1、加载:这个很简单,程序运行之前jvm会把编译完成的.class二进制文件加载到内存,供程序使用,用到的就是类加载器classLoader ,这里也可以看出java程序的运行并不是直接依 靠底层的操作系统,而是基于jvm虚拟机。如果没有类加载器,java文件就只是磁盘中的一个普通文件。
2、连接:连接是很重要的一步,过程比较复杂,分为三步 验证 》准备 》解析
验证:确保类加载的正确性。一般情况由javac编译的class文件是不会有问题的,但是可能有人的class文件是自己通过其他方式编译出来的,这就很有可能不符合jvm的编 译规则,这一步就是要过滤掉这部分不合法文件
准备:为类的静态变量分配内存,将其初始化为默认值 。我们都知道静态变量是可以不用我们手动赋值的,它自然会有一个初始值 比如int 类型的初始值就是0 ;boolean类型初始值为false,引用类型的初始值为null 。 这里注意,只是为静态变量分配内存,此时是没有对象实例的
解析:把类中的符号引用转化为直接引用。解释一下符号引用和直接引用。比如在方法A中使用方法B,A(){B();},这里的B()就是符号引用,初学java时我们都是知道这是java的引用,以为B指向B方法的内存地址,但是这是不完整的,这里的B只是一个符号引用,它对于方法的调用没有太多的实际意义,可以这么认为,他就是给程序员看的一个标志,让程序员知道,这个方法可以这么调用,但是B方法实际调用时是通过一个指针指向B方法的内存地址,这个指针才是真正负责方法调用,他就是直接引用。
3、初始化:为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋予的才是程序编写者为变量分配的真正的初始值
现在java程序的执行就可以分为
类加载的内存分析
类的加载过程我们已经了解,现在来分析一下类加载的内存分配,
类加载到底是什么呢?其实类加载不过就是居民虚拟机为类分配了几块内存空间,说的具体一点,就是jvm虚拟机将类的.class文件加载到内存,并将它放到运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构
这里可能很多人不知道什么是运行时方法区。我们简单了解一下jvm虚拟机的内存管理
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。java虚拟机所管理的内存将会包括以下几个运行时数据区域
很多区域的作用大家可能都不知道,这里也不多做解释,我们只需要知道 其中的两个
方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
堆区:存放对象实例,几乎所有的对象实例都在这里分配内存
也就是说,类被加载后,方法区会被分出一块内存,存储这个类的所有信息,但是这个内存块存储的依然是.class文件,并不能被我们使用,我们还需要一个能被直接使用的对象,此时堆区就开始发挥作用。类的信息被存储在方法区后,jvm虚拟机又会堆区创建一个java.lang.Class对象,这个对象就好像方法区对应类的一个镜子,把方法区存储的类的结构全部反射过来,然后封装起来,成为了一个Class类的对象(此处运用到反射知识)。这个Class对象与对应的类是一对一服务,因为他有类的结构信息,所以他自然可以构造出一个类的对象。我们平时使用的对象就是由这个Class类的对象生成。到此,类的加载已经完成,但是此时依旧没有我们需要使用的对象产生(这里比较绕,说的比较啰嗦)
反正都啰嗦了,在加一个反应内存的图也不多余
来个总结
- 类的加载的最终产品是位于堆区中的Class 对象
- Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口
说完了类的加载过程,我们还要绕回去,谈谈类为什么会被加载,如何触发jvm虚拟机加载一个类?
先来一句看不懂的话:所有的Java虚拟机实现必须在每个类或接口被Java程序“ 首次主动使用”时才初始化他们
什么意思 ?
Java程序对类的使用方式可分为两种
– 主动使用
– 被动使用
被动使用以后再讲,这里说说什么是主动使用,java对类的主动使用有六种情况
- 创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射(如Class.forName(“com.shengsiyuan.Test”))
- 初始化一个类的子类(先初始化所有的父类,最后初始化本身,接口除外,类初始化的时候,它所实现的接口不会初始化,就算字接口初始化,父接口也不会初始化,只有当程序调用接口的静态变量的时候才会导致接口的初始化)
- Java虚拟机启动时被标明为启动类的类
这里唯一需要解释的可能就是最后一个,什么叫被标记为启动类的类呢
比如我们有一个Hello.java文件,但是里面包含了class Hello ,class Person1 ,class Person2,我们在控制台运行的时候会写java Hello,这个class Hello就是被标记为启动类的类。简单说就是拥有main方法的类
以上的六个活动在第一次发生时,都会促使jvm虚拟机加载类。
除了上述六种情况以外,其他情况都属于类的被动调用,主动调用和被动调用的区别请看下面代码
class Test2{
public static final int n = 2;
static{
System.out.println("test");
}
}
public class Test1 {
public static void main(String[] args) {
System.out.println(Test2.n);
}
}
大家思考一下会出现什么?不管你的答案是什么,正确答案是2 。 Test2中的静态代码块是没有运行的,因为Test2并没有初始化
注意,这里的final关键字不可缺少,我们知道变量被关键字fianl修饰之后就不可修改,亦即此变量相当于编译期常量(是相当于并非就是常量),常量在java编译期已经确定,不需要初始化,但是把fianal去掉,或者把 final int n = 2 改为 final int n = new Random()。,运行的结果将变为 test ,2 ,因为n的值为变量或者n值在编译期不能确定,就必须经过初始化才能使用n的值
jvm学习一:类加载过程详解的更多相关文章
- JVM学习笔记——类加载过程
JVM学习笔记——类加载过程 类加载模型——双亲委派模型(Parents Delegation Model)也可称为“溯源委派加载模型” Java的类加载器是一个运行时核心基础设施模块,主要是启动之初 ...
- [jvm学习笔记]-类加载过程
JVM类加载的过程 加载=>验证=>准备=>解析=>初始化 5个阶段所执行的具体动作 加载 在加载阶段,虚拟机需要完成3个事情1.通过一个类的全限定名获取定义此类的二进制字节流 ...
- android学习-Activity启动过程详解
注:只是说明启动activity的过程(ActivityThread如何与ActivityManagerService简称AmS进行进程间通信调用全过程),不解析android从zygote(受精卵) ...
- 深入理解java虚拟机(八)类加载过程详解
类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(In ...
- jvm学习笔记之对象详解
一.对象的组成 对象头(Header): 运行时数据:存储对象运行时的数据,如哈希码.GC分代年龄.锁状态标志.线程持有的锁.偏向线程ID.偏向时间戳等,这部分数据官方成为“Mark Word”,它的 ...
- Java程序员的必备知识-类加载机制详解
类加载器的概念 类加载器是一个用来加载类文件的类. Java源代码通过javac编译器编译成类文件.然后JVM来执行类文件中的字节码来执行程序.类加载器负责加载文件系统.网络或其他来源的类文件. JV ...
- JVM类加载机制详解(二)类加载器与双亲委派模型
在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...
- JVM类加载机制详解
引言 如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 在加载阶段,虚拟机需要完成以下三件事情: 1)通过一个类的全限定名来获取定义此 ...
- Hadoop学习之Mapreduce执行过程详解
一.MapReduce执行过程 MapReduce运行时,首先通过Map读取HDFS中的数据,然后经过拆分,将每个文件中的每行数据分拆成键值对,最后输出作为Reduce的输入,大体执行流程如下图所示: ...
随机推荐
- 解决基于IIS的.net core HttpWebRequest 连接特别慢
用的是HttpWebRequest 连接特别慢,查找原因发现 : 由 HttpWebRequest. Proxy 代理的原因导致 . 其实请求1秒就完成了,那15秒是用来等待默认proxy超时的…… ...
- #WEB安全基础 : HTML/CSS | 0x4.1嵌套列表
如果你认为列表只有ul和ol那你就错了 我要为你展示新的列表 这次只有一个index.html文件 这是它的效果 以下是它的代码 <html> <head> <title ...
- maven 聚合
聚合很简单, 在父 pom 中写出子 pom 文件的路径即可 <name>parent Maven Webapp</name> <!-- FIXME change it ...
- [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(六.结语)
到这里就结束了,真的结束了,源码会在文末分享! 另外录了两个视频,对这个系统进行了演示! 做有意义的事情,原此生无悔! 视频地址:使用asp.net core 快速构建权限管理模块1 使用asp.ne ...
- web-garden 和 web-farm 有什么不同 ?
相同:都是网络托管系统. 不同: web-garden:是在单个服务器包含许多处理器的设置: web-farm:是使用多个服务器的较大设置.
- Class.isAssignableFrom与instanceof的区别
isAssignableFrom 假设有两个类Class1和Class2.Class1.isAssignableFrom(Class2)表示: 类Class1和Class2是否相同. Class1是否 ...
- CFS调度器(1)-基本原理
首先需要思考的问题是:什么是调度器(scheduler)?调度器的作用是什么?调度器是一个操作系统的核心部分.可以比作是CPU时间的管理员.调度器主要负责选择某些就绪的进程来执行.不同的调度器根据不同 ...
- 5.4Python数据处理篇之Sympy系列(四)---微积分
目录 目录 前言 (一)求导数-diff() 1.一阶求导-diff() 2.多阶求导-diff() 3.求偏导数-diff() (二)求积分-integrate() (三)求极限-limit() ( ...
- Ubuntu下安装JDK详细教程
Ubuntu下安装JDK详细教程 作者:凯鲁嘎吉 - 博客园http://www.cnblogs.com/kailugaji/ Ubuntu版本:Ubuntu-12.04.5-desktop-i386 ...
- Java Scala 混合编程导致 编译失败 ,【找不到符号】问题解决
大致就是 工程里分了 java 代码 和 scala 代码. 然后在java代码中 引用了 scala 的代码. 运行不报错. 但是打包就是一直报错. [ERROR] Failed to execut ...