先上一张重要的原理图,这里挂上原理图的psd文件,方便大家有了自己的理解后在里面增加修改自己理解的内容。psd文件地址:https://download.csdn.net/download/qq_14868375/12922475
理解jvm主要要理解两个内容
1、Java类的编译、加载和执行。这是今天篇文章主要讲的内容。
2、JVM的内存管理和垃圾回收机制。这个主要讲的是堆的的管理,是下一篇文章主要讲的内容。
今天先讲Java类的编译、加载和执行。
1. Java类的编译过程
这是由*.java源码文件转为 .class二进制字节码文件的过程。
我们编写好的源代码,就是.java文件。使用“javac test.java”就可以编译test.java文件。
编译过程主要有三步:
1、词法分析和输入到符号表
2、注解处理
3、语义分析和生成字节码
详细过程:
源代码文件*.java -> 词法分析器 -> tokens流 -> 语法分析器 -> 语法树/抽象语法树 -> 语义分析器 -> 注解抽象语法树 -> 字节码生成器 -> JVM字节码文件*.class
最后剩成的JVM字节码文件,使用命令“javap -c test”可以查看test.class的字节码信息,主要包含三项内容:
1、结构信息:class文件相关信息。
2、元数据:Java源码中的声明和常量信息。
3、方法信息:Java源码语句和表达式对应的字节码。
2. 类加载机制
加载指的是将类的class文件读入到内存。
2.1 类加载器分类
1、根加载器BootstrapClassLoader,负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类。
2、扩展加载器ExtensionClassLoader,负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3、系统应用加载器APPClassLoader,负责记载classpath中指定的jar包及目录中class。
4、用户自定义加载器CustomerClassLoader,属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。
2.2 类加载的时机
1、创建类的实例,也就是new一个对象
2、访问某个类或接口的静态变量,或者对该静态变量赋值
3、调用类的静态方法
4、反射(Class.forName("com.lyj.load"))
5、初始化一个类的子类(会首先初始化子类的父类)
6、JVM启动时标明的启动类,即文件名和类名相同的那个类
2.3 类加载机制
1、双亲委派机制。
双亲委派机制,其工作原理的是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成。
双亲委派机制的优势:采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
2、全盘负责机制。
所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
3、缓存机制
缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。
2.4类加载过程
类的加载过程基本为连接、初始化两个过程。
1、链接
连接(linking)包括三个部分:
-
验证verifying:验证类符合Java规范和JVM规范,和编译阶段的语法语义分析不同。
-
准备preparing:为类的静态变量分配内存,初始化为系统的初始值。(不初始化静态代码块)。对于final static修饰的变量,直接赋值为用户的定义值。
-
解析resolving:将符号引用(字面量描述)转为直接引用(对象和实例的地址指针、实例变量和方法的偏移量)
2、初始化
初始化类的静态变量和静态代码块为用户自定义的值。非静态类在实例化类,初始化Java堆中的对象。
2.5类方法的执行机制
JVM是基于堆栈的虚拟机。JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
JVM执行class字节码,线程创建后,都会产生程序计数器(PC)和栈(Stack),程序计数器存放下一条要执行的指令在方法内的偏移量,栈中存放一个个栈帧,每个栈帧对应着每个方法的每次调用,而栈帧又是有局部变量区和操作数栈两部分组成,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法执行过程中产生的中间结果。