目录
一、JVM 简介
JVM 是 Java Virtual Machine
的简称,意为 Java虚拟机。
虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。
常见的虚拟机:JVM、VMwave、Virtual Box
。
JVM 和其他两个虚拟机的区别:
VMwave
与VirtualBox
是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器;JVM
则是通过软件模拟Java字节码的指令集,JVM
中只是主要保留了PC寄存器,其他的寄存器都进行了裁剪。
JVM
是一台被定制过的现实当中不存在的计算机。
JVM出现的初心是为了"跨平台",“—次开发,到处运行”。
二、JVM 运行流程
JVM 是 Java 运行的基础,也是实现一次编译到处执行的关键
,那么 JVM 是如何执行的呢?
JVM 运行流程:
程序在执行之前先要把java代码转换成字节码(class文件),JVM 首先需要把字节码通过一定的方式:类加载器(ClassLoader)
把文件加载到内存中 运行时数据区(Runtime Data Area)
,而字节码文件是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器 执行引擎(Execution Engine)
将字节码翻译成底层系统指令再交由CPU去执行,而这个过程中需要调用其他语言的接口 本地库接口(Native Interface)
来实现整个程序的功能,这就是这4个主要组成部分的职责与功能。
总结来看, JVM 主要通过分为以下 4 个部分,来执行 Java 程序的,它们分别是:
- 类加载器(ClassLoader)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
- 本地库接口(Native Interface)
三、 JVM运行时数据区域(内存布局)
JVM 运行时数据区域也叫内存布局,但需要注意的是它和 Java 内存模型((Java Memory Model,简称JMM)完全不同,属于完全不同的两个概念,它由以下 5 大部分组成:
JVM实际上是一个Java 进程,进程就是用来管理硬件资源的,比如内存。
JVM中内存来自于操作系统.JVM启动之后就会从操作系统这里申请到一大块内存,再针对这个内存划分出一些区域。
具体的内存布局:
对于堆区和方法区,在整个JVM中只存在一份,而程序计数器和栈区是跟进程绑定在一起的,一个java进程中,可能包含着多个线程,每个不同的线程都有独立的一份程序计数器和栈区。
- 堆(运行时常量池):new的对象就放到堆中.
- 方法区:用来存储被虚拟机加载的类信息、常量、静态变量(静态成员)、即时编译器编译后的代码等数据的。
- 栈(JVM栈/本地方法栈):局部变量.
- 程序计数器:程序计数器的作用:用来记录当前线程执行的行号的存的,它是个地址,描述当前线程接下来要执行的指令在内存的哪个地方。
内存布局中的异常问题
堆溢出java.lang.OutOfMemoryError
,循环创建对象,把对象加到List集合类里.(防止被GC回收)典型的情况就是不断地去new 对象而不去释放内存。
栈溢出StackOverflowError
:写个递归方法,无限递归。
堆和栈的空间大小,都可以通过JVM(Java进程的命令行参数)来进行配置。
对引用类型的理解:
可以把引用类型当作一个“低配指针”,但从更严谨的角度去看,引用并不是一个指针。Java的引用相当于堆C语言的指针功能进行了裁剪,Java中的引用只能用来解引用(如:使用 .
就是默认地解引用)和比较(==或!=
) )。
四、JVM 类加载
关于.class文件的格式规范:
类加载其实是JVM 中的一个非常核心的流程,做的事情就是把.class
文件,转成JVM
中的类对象。
要想完成类加载,必须要明确的知道.class
文件中都有啥,按照.class
文件中的规则进行解析。因此,编译器和类加载器(JVM)必须要商量好.class
文件的格,而.class
文件的格式在 JVM虚拟机规范文档里面已经约定好了的,则编程语言的语法也可以理解为一种“协议”。
在JVM虚拟机规范文档中可以看到:
关于.class
文件格式的规范:
可以看到,它把java代码中定义的一个类的核心信息都体现进去了,只不过这个文件的格式是二进制的。
因此,根据上述的格式我们也可以自己开发一个编程语言,然后编译就根据.class
文件的格式一样,就可以直接在JVM中去解析执行了。