背景介绍
这两天在对JVM的知识进行回顾,顺便来分享分享,接下来也会有系列文章,欢迎大家一起讨论。
过程
为什么叫JVM?
Java Virtual Machine,java虚拟机。可以理解成一个以字节码为机器指令的CPU
有哪些特点呢?
- 不同的运行平台,有不同的虚拟机
- 屏蔽了底层运行平台的差别,实现了“一次编译,随处运行”
为什么JVM可以跨平台?
不同操作系统有不同的JVM,可以运行字节码文件。JVM只和class字节码文件有关系,任何语言只要能翻译成符合规范的class字节码文件,都能够被JVM运行
JDK、JRE、JVM三者关系
- JDK:Java Development Kit(java开发者工具)
- JRE:Java Runtime Environment(java运行时环境)
- JVM:Java Virtual Machine(java虚拟机)
- jdk包含了jre,还提供了程序所需要的工具(java和javac运行编译环境的,javadoc可以把java生成一篇文档,jar把程序打包成应用……),jdk提供了java开发的环境(需要这样一个平台你才能写java代码)。jdk包含了jre,在jre的基础上扩充了一些开发工具,让开发者可以使用
- jre包含了jvm、函数库,jre运行时环境,只要安装了jre,就能运行java代码了
- jvm虚拟机,可以当成一种规范,可以用软件实现,也可以用硬件实现,它相当于在所有的操作系统上模拟了一个小小的CPU去处理java相关的东西。实现了跨平台,jvm屏蔽了底层的一些差别,一次编译到处执行
JVM包含了哪些部分?
注:这张图片引用自网上
接下来我针对JVM的每一部分起了哪些作用具体来说说吧!
一、编译器
为什么要用编译器?
我们在本地写的都是源代码,而电脑,也就是CPU只认识0和1,要想让电脑执行我们写的程序,就要翻译成电脑能够认识的语言0和1,这个翻译的过程就是编译器去做。
二、类加载子系统
类的加载过程(类的生命周期)
被javac编译之后的字节码文件是存在磁盘中的,而类加载的过程就是把字节码从磁盘加载到内存中的过程
1、加载(Loading):
作用:生成Class。类加载器通过类的全限定名在内存中成java.lang.Class类模板对象,作为方法区这个类的各种数据的访问入口
工作内容:
- 将class信息,静态变量、常量加载到方法区
- 将Class对象指向class信息
2、验证(Verify):
作用:确保Class文件是否符合当前虚拟机要求,保证被加载类的正确性,只有符合JVM字节码规范的才能被JVM正确执行
工作内容:
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
3、准备(Prepare):
作用:为成员变量分配内存并且设置该类变量的默认初始值,即零值
工作内容:
- final不分配,在编译的时候就为final分配内存了,准备阶段会显式初始化
- 不为实例变量分配初始化,类变量会分配在方法去中,实例变量会随着对象一起分配到Java堆中
4、解析(Resolve):
作用:将常量池内的符号引用转换为直接引用的过程
工作内容:
- 类或接口、字段、类方法、接口方法、方法类型等
为什么要把符号引用转为直接引用?
在编译阶段java类并不知道所引用的实际地址,因此只能使用符号代替。比方说类A引用了类B,此时类A是不知道类B有没有被编译的,也不清楚类B的实际地址,所以用字符串S代表类B的地址,这个S就称为符号引用
- 符号引用:可以理解为是一种占位符
- 直接引用:可以理解为指向类、字段、方法存储在内存中的实际地址的指针
5、初始化:正常赋值,执行类构造器的过程
6、使用:执行main方法
7、卸载:销毁
类加载器
什么是类加载器?
负责加载类文件并生成对应的类对象的组件
类加载器的作用是什么?
类加载的时候是通过具体的类加载器去进行加载的,主要用于加载Class字节码文件
类加载器有哪几种分类?
- 引导类加载器(Bootstrap Class Loader):也称为根类加载器,是JVM的一部分,负责加载Java的核心类库,如rt.jar等。它是用本地代码实现的,不继承自java.lang.ClassLoader,因此无法直接获取到引导类加载器的引用。
- 扩展类加载器(Extension Class Loader):是由sun.misc.Launcher$ExtClassLoader实现的,负责加载Java的扩展类库,如jre/lib/ext目录下的jar包。它是Java类,继承自java.lang.ClassLoader。
- 应用程序类加载器(Application Class Loader):也称为系统类加载器,是由sun.misc.Launcher$AppClassLoader实现的,负责加载应用程序classpath下的类。它也是Java类,继承自java.lang.ClassLoader。
- 自定义类加载器(User Class Loader):用户通过继承java.lang.ClassLoader类的方法自行实现的类加载器
每个类加载器的区别是什么?
- 加载的类目录不一样
- 类的加载顺序不同
- 可见性:类加载器可以根据需要通过父类加载器来委派加载类,但是父类加载器无法看到子类加载器加载的类
通过看底层的源码发现扩展类加载器和应用程序类加载器都是继承自ClassLoader类。而一个类只要继承了Classloader类,就是自定义类加载器
三个类加载器之间是什么关系?
层次关系,也称为双亲委派模型。每个类加载器之间互相配合、互相工作(类加载器都应该有自己的父类加载器,他们是组合关系去复用父类的代码)
什么是双亲委派模型(机制)?
当要加载一个类的时候会向上(父类)询问(委托)是否已加载过这个类,如果父类加载器无法加载该类,那么类加载器才会尝试自己加载
什么要使用双亲委派机制(好处)?
- ①、避免类重复加载:提高类加载效率,保证数据安全
设想一个场景:用户在ClassPath中编写了一个java.lang.Object的同名类,如果没有双亲委派机制,每个类加载器都可以自行加载类,那么是不是会产生多个Object类呢?那程序使用的时候如何选择?唯一性就保证不了了
补充:全限定类名相同,并且类加载器是同一个,则会认为两个对象是同一个
- ②、沙箱安全机制:保证核心.class不能被篡改
及时篡改也不会去加载,就算加载也不是同一个.class对象
总结
- 在idea写的源码通过javac编译成机器认识的字节码文件
- 字节码文件存储在磁盘中,想要到使用要通过类加载器把字节码文件加载到内存中
- 类加载的需要通过双亲委派机制加载
升华
对于JVM的了解通过系统的回顾又有了新的发现,每次的反复都建立在原有的基础上不断精进,螺旋式上升引起量变到质变的过程