网上讲解 JVM 这块的文章很是多,不过鱼龙混杂,鉴于 JVM 也是最考验 Java 程序员的基础功底啦,今天静下来,一块儿画画图,一块儿梳理梳理,好好填补一下这块,争取不管走到哪里,你们在脑海中都能有行走的 JVM 内存模型图。java
1程序员
在讲解 JVM 以前,先来揭秘一下 Java 程序是如何实现一次编译处处运行的?json
步骤一:用文本编辑器或者 IDE,快速编写 HelloWorld.java 的源代码文件;
微信步骤二:用 Java 编译器(javac)把源代码(*.java)编译成字节码文件(*.class);网络
步骤三:字节码文件(.class)即可以在任何安装了 JVM 的操做系统中运行,JVM 会将字节码翻译成能够被机器执行的本地机器码。架构
那么重点来了,Java 是如何实现一次编译处处运行的呢?经过上图应该很清晰找到解。app
解一:Java 一次编译,处处运行,跨平台的特性是经过 JVM 来实现的,经过 JVM 来屏蔽底层操做系统的差别;
框架解二:Java 经过 JVM 来实现跨平台,可是 JVM 是不跨平台的,也就是说不一样操做系统之上的 JVM 是不一样的,Linux 系统上的 JVM 不能用在 Windows 系统上。编辑器
2
函数
既然已经知晓 Java 程序能够经过 JVM 来实现一次编译,处处运行的跨平台特性,那么 JVM 究竟是什么呢?
JVM 是 Java Virtual Machine(Java虚拟机)的缩写,JVM 是一种用于计算设备的规范,它是一个虚构出来的计算机,是经过在实际的计算机上仿真模拟各类计算机功能来实现的 —— 百度百科。
上面是引了一段百度百科对 JVM 的解释,大意就是 Java 虚拟机是在计算机上虚构出来的一个计算机,既然是虚构的就意味着看不到,只在于内存之中。
(图片来源于网络)
如上图所示,计算机主要有运算器、控制器、内外存储器、输入和输出设备组成,那么 JVM 结构长啥样子呢?
仍是以开篇的 HelloWorld 为例,窥探一下 JVM 的运行流程。
1. Java 源代码文件会被 Java 编译器编译为字节码文件(.class);
2. JVM 中的类加载器进行加载各个类的字节码文件,将全部类结构和方法变量放入运行时数据区;
3. 字节码文件加载完毕以后,交给 JVM 执行引擎进行执行。
JVM = 类加载器 ClassLoader + 执行引擎 Execution Engine + 运行时数据区域 Runtime Data Area。
在程序执行过程当中,会分配内存空间来存储程序执行期间须要用到的数据相关信息,分配的内存空间被称做为 Runtime Data Area(运行时数据区),也就是常说的 JVM 内存,接下来就重点说一说 JVM 运行时数据区。
(JDK 1.7 内存模型)
如上图所示,JVM 运行时数据区主要分为程序计数器、虚拟机栈、本地方法栈、方法区、堆。
如上图所示,按照内存共享来划分 JVM 内存,主要划分为线程共享内存区域(堆、方法区)、线程私有内存区域(程序计数器、虚拟机栈、本地方法栈)、直接内存。
3
从上面介绍,能够清晰知道 JVM 运行时数据区,主要分为程序计数器、虚拟机栈、本地方法栈、方法区、堆。接下来就逐步解剖,简单了解一下。
1. 程序计数器
程序计数器(PC Register),也有人称它为 PC 寄存器。
主要是为了可以使得每一个线程都在线程切换后可以恢复在切换以前的程序执行位置,每一个线程都须要有本身独立的程序计数器。
2. 虚拟机栈
虚拟机栈(VM Stack),也有人称它为 Java 虚拟机栈、栈或者 JVM 栈,这块就是你们常常分析一段程序执行时要关注的区域。
JVM 栈是每一个线程私有的,线程建立的同时都会建立对应的 JVM 栈。
JVM 栈中存放的是当前线程中局部基本类型的变量、返回结果以及 Stack Frame,而非基本类型的对象在 JVM 栈上仅存放一个指向堆上的地址。
3. 本地方法栈
本地方法栈(Native Method Stack)与 JVM 栈很类似,本地方法栈也是每一个线程私有的,只不过是服务的对象不一样,JVM 栈是为执行 Java 方法服务,而本地方法栈则是为执行本地方法(Native Method)服务。
4. 方法区
方法区(Method Area),也有人称它为永久代,是线程共享的区域。
在方法区中,主要存放静态变量、常量、类信息、运行时常量池以及全部的方法的信息。运行时常量池(Runtime Constant Pool)是方法区的重要一部分,用于存储编译器生成的常量和引用。
(JDK 1.8 内存模型)
如上图所示,值得注意的是 JDK 1.8 相比 JDK 1.7,JVM 运行时数据区划分中的方法区(持久代)从 JVM 运行时数据区拿掉了,而在本地内存加入了元数据区(Metadata Memory),简而言之在 JDK 1.8 中,元数据区替代了方法区(持久代)。
5. 堆
堆(Heap),全部 new 出来的对象实例都存储在该区域,这块区域也是线程共享的,也是分析 Java 程序时关注较多的一块。
6. 其它
直接内存(Direct Memory),有时也称做堆外内存,是不受 JVM控制的内存。
在 JDK 1.4 中加入了 NIO,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可使用 Native 函数库直接分配堆外内存,而后经过一个存储在 Java 堆里面的DirectByteBuffer 对象做为这块内存的引用进行操做。
避免了在 Java 堆和 Native 堆中来回复制数据,能在一些场景中显著提升性能。
4
本次,主要让你们了解一下 JVM 的内存结构,但愿经过本次分享,你们对 JVM 能有个梗概的认识,想要完全掌握还需针对性的弥补,说句内心话,但愿可以把这些图都记在脑子里,只要作到脑中有图,心就不慌。
好了,本次就谈到这里,一块儿聊技术、谈业务、喷架构,少走弯路,不踩大坑。会持续输出原创精彩分享,敬请期待!
本文分享自微信公众号 - 一猿小讲(yiyuanxiaojiangV5)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。