什么是JVM?
首先,我们要知道 JVM是Java Virtual Machine(Java虚拟机) 的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的;Java虚拟机有自己完善的硬件架构,如处理器、堆栈等,还具有相应的指令系统
引入Java语言虚拟机后,java程序就可以具有了跨平台特性,Java语言在不同平台上运行时不需要重新编译
Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行; JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统
Java语言最重要的特点就是跨平台运行
- 使用JVM就是为了支持与操作系统无关,实现跨平台
- Java虚拟机不仅是一种跨平台的软件,而且是一种新的网络计算平台
- 该平台包括许多相关的技术,如符合开放接口标准的各种API、优化技术等
- Java技术使同一种应用可以运行在不同的平台上
- Java平台可分为两部分,即Java虚拟机(Java virtual machine,JVM)和Java API类库
JVM的运行原理
基本概念
JVM是可运行Java代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆 和 一个存储方法域;JVM是运行在操作系统之上的,它与硬件没有直接的交互
运行过程
我们都知道Java源文件,通过编译器,能够生产相应的.Class文件,也就是字节码文件,而字节码文件又通过Java虚拟机中的解释器,编译成特定机器上的机器码
具体如下分为两个步骤:
- ① Java源文件—->编译器—->字节码文件
- ② 字节码文件—->JVM—->机器码
每一种平台的解释器是不同的,但是实现的虚拟机是相同的,这也就是Java为什么能够跨平台的原因了 ; 当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据不能共享
JVM的体系结构
(1) Class Loader类加载器
负责加载 .class文件,class文件在文件开头有特定的文件标示,并且ClassLoader负责class文件的加载等,至于它是否可以运行,则由Execution Engine决定
(2) Native Interface本地接口
本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候C/C++横行的时候,要想立足,必须有调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体作法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies.
(3) Execution Engine 执行引擎
执行包在装载类的方法中的指令,也就是方法
(4) Runtime data area 运行数据区
JVM内存,冲整个计算机内存中开辟一块内存存储Jvm需要用到的对象,变量等,运行区数据有分很多小区,分别为:方法区,虚拟机栈,本地方法栈,堆,程序计数器
JVM数据运行区详解
JVM数据运行区中,栈主要负责运行,堆负责存储,一般来说,JVM调优主要就是优化 Heap堆 和 Method Area 方法区
(1) Native Method Stack本地方法栈
它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies
(2) PC Register程序计数器
每个线程都有一个程序计算器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记
(3) Method Area方法区
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义;简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间
静态变量+常量+类信息+运行时常量池存在方法区中,实例变量存在堆内存中
(4) Stack 栈
栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的, 基本类型的变量和对象的引用变量都是在函数的栈内存中分配; 遵循“先进后出”/“后进先出”原则
(5) Heap 堆
堆这块区域是JVM中最大的,应用的对象和数据都是存在这个区域,这块区域也是线程共享的,也是 gc 主要的回收区,一个 JVM 实例只存在一个堆类存,堆内存的大小是可以调节的; 类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行
Java虚拟机运行时数据区的图(JVM内存模型)
方法区
方法区和java堆一样,是各个线程共享的内存区域,用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译的代码等数据。通常被开发人员成为“永久带”; 这个区域的内存回收的目标就是针对常量池的回收和对类型的卸载,也是较为难处理的部分
堆
堆是java虚拟机中内存中最大的一块,被所有线程共享的一块内存区域,在虚拟机创建时创建;作用就是存放对象实例,所有的对象的实例都需要在这里分配内存
几乎所有的对象实例和对象数组都需要在堆上分配; 是java虚拟机内存回收的管理的重要区域,因此也别称为“GC堆,可被分为:新生代和老年代;Eden空间、From Survivor空间、To Survivor空间;如果堆中没有内存完成实例分配,并且堆也无法扩展时,则抛出OutOfMemoryError异常
新生代(Young Generation)
年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁; 年轻代分成Eden空间、From Survivor空间、To Survivor空间;当对象在堆创建时,将进入年轻代的Eden空间;垃圾回收器进行垃圾回收时,扫描Eden空间和From Survivor空间,如果对象仍然存活,则复制到To Survivor空间
如果To Survivor空间已经满,则复制到老年代(Old Generation); 同时扫描Survivor空间是,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个持久化对象,则将其移到老年代(Old Generation);扫描完毕后,JVM将Eden空间和From Survivor空间清空,然后交换From Survivor空间和To Survivor的角色,这样做主要的目的是为了减少内存碎片的产生
老年代(Old Generation)
老年代主要存放JVM认为生命周期比较长的对象(进过几次的年轻代的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁。老年代主要采用压缩的方式来避免内存碎片
元空间(MetaData Space)
JDK8之前叫“永久代”
虚拟机栈
java虚拟机栈也是线程私有,与线程的生命周期一致,在执行每个方法都会创建一个Stack Frame用于存储局部变量表、操作数栈、动态链接、方法出口信息;每一个方法从开始执行到结束,对应一个Stack Frame在虚拟机值栈中从入栈和出栈的过程
如果线程请求的栈深度大于虚拟机所允许的深度,就会出现StackOverFlowException;如果允许动态扩展,在扩展的过程中,如果无法申请到足够的内存,则会抛出OutOfMemoryException异常
本地方法栈
本地方法栈和java虚拟机栈的作用类似,同样式线程私有,它们之间的区别不过是虚拟机栈为java方法服务,而本地方法栈为虚拟机使用到的Native方法提供服务; 在JVM规范中,并没有对本地方法发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接把本地方法栈和java栈和二为一;本地方法栈也会抛出StackOverFlowException和OutOfMemoryException异常
程序计数器
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器(也称为PC寄存器); 在虚拟机的概念模型里,字节码解析器工作是就是通过改变这个计数器的值来选取下一条需要执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计算器来完成
在多线程中,每个线程都有一个独立的程序计数器,每个线程的程序计数器之间互不影响,即"线程私有";同时,程序计数器是Java虚拟机规范中唯一一个没有规定OutOfMemoryError的区域
尾述
点击 “此处” 即可 免费获取 更多《Android 学习笔记+源码解析+面试视频》
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
PS:有问题欢迎指正,可以在评论区留下你的建议和感受;
欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下
网友评论