理解Java虚拟机(2)之.class文件加载过程
来源:互联网 发布:北bi数据分析 编辑:程序博客网 时间:2024/05/16 15:43
理解Java虚拟机(2)之.class文件加载过程
读《深入理解Java虚拟机》-周志明 读书笔记
虚拟机只能执行.class文件,在.class文件加载过程中,生命周期包括:加载,验证,准备,解析,初始化,使用,卸载
加载将.clss文件加载进虚拟机,加载来源有
- 1.常见jar包;
- 2.网络获取(dubbo的RPC是典型,底层通过java的RMI方法,通过固定协 议,将远端生成者接口,对象序列化,消费者调用);
- 3.运行时动态生成,典型代表反射技术;
- 4.从其他文件,如jsp;
- 5.通过数据库读取,(没见到过,书里说中间件服务器)
加载完成后,虚拟机就会按照.class文件里面的内容相应的存储到内存的方法区(方法区是虚拟机加载类信息,常量,静态变量,即使编译后的代码,是线程共享的,简单讲就是加载的东西放在这里面),加载是通过ClassLoader这个加载,这个加载机制是通过(双亲委派模型,后面详细讲双亲委派模型,简单讲就是JVM有一个系列的继承加载,自己也可以自定义ClassLoader,先通过自己的ClassLoader,在通过JVM的ClassLoader),平常使用Tomcat,Jetty,这些容器都是有自己的ClassLoader,用过maven配置jetty启动时,都会在pom.xml配置标签,这个就是去配置jetty的ClassLoader。
验证
验证是连接阶段的第一步,并不是加载完了才验证(这样的太傻瓜式了,要是很多.class文件,在最开始加载就有错误,等加载完了再验证那就不用工作了),验证和加载是相辅相成的。在.java编译成.class文件里讲过.class打头就是个”cafebabe”的魔数,首先验证这个,不是这个打头的必然报错,接着看看版本号,版本号是不是java没有的(比方搞个java6.0,必然错了),然后看常量类型啊,验证相当于看这个.class文件是否符合JVM规范,用过Eclipse的就知道,每次”ctrl+s”就能看到自己写的代码有没有错误,因为eclipse就是有自己的CLassLoader,即时编译,你一保存就给你编译了.class,打开文件夹就能看到class目录的文件夹,然后通过去验证编译后的.class是不是符合JVM规范,然后错了就各种红叉叉,iead就没有即时编译,”ctrl+s”就找不到对应的class文件夹,eclipse的验证不是虚拟机的验证,这只是eclipse自己根据jVM规范验证的,编译后的.class文件JVM在加载的过程中,还是要自己验证的,不过虚拟机有个参数可以设置不验证(-Xverify:none),这样可以加快加载速度,当然前提是你相信.class符合规范,验证是很重要的一件事,不然怎么承受恶意的攻击,使得代码更安全,当然验证也会使得加载时间长,在安全和时间上,还是选择更安全些,毕竟java大都都是用来做企业级应用的,安全是最重要的。准备
准备阶段是正式给类变量(static修饰的变量)分配内存并设置类变量的初始值,实例变量(不是static修饰的类层面的变量)会在对象实例化时随着对象一起分配在java堆中,要注意的是初始值的含义,和final修饰的实例变量,初始值指数据类型的0值,下面a在这个时候初始值为0;final修饰的变量这个时候会给值,c此时的值就是为3;(感觉final修饰符有些破坏了面向对象的设计,也不知道这个设计的初衷)
public Class Test(){ private static int a = 1;//类变量 private int b = 2 ;//实例变量 private final int c = 3 ;}
- 解析
解析是虚拟机将常量池(常量池位于方法区)的符号引用替换成直接引用的过程,先解释下符号引用,直接引用
符号引用:
public class Test { public static int sa = new Random().nextInt(); } public class TestClass { //这里就是符号引用 public static int sa = Test.sa; public static int sb = 1;}
编译下上上面的,再反编译过来如下(javap -verbose TestClass.class):
D:\learn\myLearn\readBook\src\main\java\JVM>javap -verbose TestClass.classClassfile /D:/learn/myLearn/readBook/src/main/java/JVM/TestClass.class Last modified 2016-8-12; size 330 bytes MD5 checksum 96ed1ae561b16a0cfecbdf7518c947cb Compiled from "TestClass.java"public class JVM.TestClass SourceFile: "TestClass.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPERConstant pool: #1 = Methodref #6.#17 // java/lang/Object."<init>":() #2 = Fieldref #18.#19 // JVM/Test.sa:I #3 = Fieldref #5.#19 // JVM/TestClass.sa:I #4 = Fieldref #5.#20 // JVM/TestClass.sb:I #5 = Class #21 // JVM/TestClass #6 = Class #22 // java/lang/Object #7 = Utf8 sa #8 = Utf8 I #9 = Utf8 sb #10 = Utf8 <init> #11 = Utf8 ()V #12 = Utf8 Code #13 = Utf8 LineNumberTable #14 = Utf8 <clinit> #15 = Utf8 SourceFile #16 = Utf8 TestClass.java #17 = NameAndType #10:#11 // "<init>":()V #18 = Class #23 // JVM/Test #19 = NameAndType #7:#8 // sa:I #20 = NameAndType #9:#8 // sb:I #21 = Utf8 JVM/TestClass #22 = Utf8 java/lang/Object #23 = Utf8 JVM/Test{ public static int sa; flags: ACC_PUBLIC, ACC_STATIC public static int sb; flags: ACC_PUBLIC, ACC_STATIC public JVM.TestClass(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 8: 0 static {}; flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: getstatic #2 // Field JVM/Test.sa:I 3: putstatic #3 // Field sa:I 6: bipush 114 8: putstatic #4 // Field sb:I 11: return LineNumberTable: line 9: 0 line 11: 6}
截部分看看
java/lang/Object."<init>":() #2 = Fieldref #18.#19 // JVM/Test.sa:I #3 = Fieldref #5.#19 // JVM/TestClass.sa:I static {}; flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: getstatic #2 // Field JVM/Test.sa:I 3: putstatic #3 // Field sa:I 6: bipush 114 8: putstatic #4 // Field sb:I
最后satatic{}可以看到Test.java中sa(#3 = Fieldref #5.#19 // JVM/TestClass.sa:I)变量指向的TestClass(JVM/Test.sa:I 3: putstatic #3 // Field sa:I ),初始化就是将这种指向变成指向new Random().nextInt(),的具体内存地址,这样才能真正的调用直接引用就是直接new 对象了,将句柄指向对象的指针地址;在解析的过程中,遇到引用其他的类,就去触发对应类的加载,最后都是直接引用
- 初始化
初始化是类加载的最后一步,这个阶段才是真正执行类中定义的代码,在准备阶段,变量已经赋值过系统要求的值,初始化阶段根据我们写代码的主观习惯去初始化变量和其他资源。相当于程序真正运行起来。
- 理解Java虚拟机(2)之.class文件加载过程
- 理解Java虚拟机(3)之.class文件加载双亲委派模型
- 深入理解Java虚拟机之Class类文件的结构
- Java虚拟机之Class文件
- Java虚拟机如何加载Class文件
- 《深入理解java虚拟机》Java Class类文件结构及类加载机制
- 深入理解java虚拟机(八)类加载过程详解
- Java加载类(Class)文件过程
- 深入理解Java虚拟机读书笔记之:第6章 Java class文件
- 理解Java虚拟机(1)之一个.java文件编译成.class文件发生了什么
- java加载class过程
- 深入理解java虚拟机【Java Class类文件结构】
- 深入理解Java虚拟机笔记---class类文件结构概述
- 深入理解Java虚拟机笔记---class类文件结构概述
- 学习《深入理解java虚拟机》笔记-class文件检验器
- 深入理解Java虚拟机:Class文件检验器
- 深入理解JAVA虚拟机——总结2:虚拟机类文件&类加载
- 深入理解Java虚拟机笔记---类加载过程
- JavaScript中类似java常量constants使用方法
- 国内常用的Android镜像下载地址(附教育网主要镜像站)
- 总结c++类的构造函数 拷贝构造函数 析构函数 赋值运算符重载的特点以及函数调用顺序
- matlab 定时器
- 微信小程序 websocket
- 理解Java虚拟机(2)之.class文件加载过程
- Bugtags 2016-10-17 更新内容
- 怎样才算真正理解“区块链”?看完图解全知道!
- 稀疏表示学习笔记--正交阵,协方差
- 理解Java虚拟机(4)之JVM运行时内存分配管理和对象内存分布
- Codeforces Round #377 (Div. 2) D 贪心+二分
- 1 PCL 1.7.2库简单安装教程
- 理解Java虚拟机(3)之.class文件加载双亲委派模型
- iOS9 Universal Links 的坑!!!