程序员进阶系列:多图教你掌握JVM
网上讲解 JVM 这块的文章非常多,不过鱼龙混杂,鉴于 JVM 也是最考验 Java 程序员的基础功底啦,今天静下来,一起画画图,一起梳理梳理,好好填补一下这块,争取无论走到哪里,大家在脑海中都能有行走的 JVM 内存模型图。
1
在讲解 JVM 之前,先来揭秘一下 Java 程序是如何实现一次编译到处运行的?
步骤一:用文本编辑器或者 IDE,快速编写 HelloWorld.java 的源代码文件;
步骤二:用 Java 编译器(javac)把源代码(*.java)编译成字节码文件(*.class);
步骤三:字节码文件(.class)便可以在任何安装了 JVM 的操作系统中运行,JVM 会将字节码翻译成可以被机器执行的本地机器码。
那么重点来了,Java 是如何实现一次编译到处运行的呢?通过上图应该很清晰找到解。
解一: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源创计划”,欢迎正在阅读的你也加入,一起分享。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
作为DBA,你不得不掌握的压测工具
mysqlslap mysqlslap是MySQL自带的一个用于实现负载性能测试和压力测试的工具。它可以模拟多个客户端对数据库进行施压,并生成报告来了解数据库的性能状况。 mysqlslap的运行过程主要分三步: 创建库、表,导入数据用于测试。此过程由单线程完成。 开始进行压力测试。该步骤可以使用多线程完成。 清理测试数据。此过程由单线程完成。 下面举几个例子来说明下如何使用mysqlslap。 1. 自动生成测试表,其中会生成自增列,采用单线程进行测试。 [root@node1 ~]# mysqlslap -uroot -p --auto-generate-sql --auto-generate-sql-load-type=mixed --auto-generate-sql-add-autoincrementEnter password:Benchmark#运行所有语句的平局时间,单位秒Average number of seconds to run all queries: 0.018 seconds#运行所有语句的最小秒数Minimum number of seconds to ...
- 下一篇
FlutterDojo设计之道—状态管理之路(四)
在Flutter中,跨Widget的数据共享,可以如下图这样表示。 当Child Widget想要跨Widget拿到其它Widget的数据时,通常就需要使用构造函数,将数据一层层传递到Child Widget,这显然不是一个好的解决方案,不仅让Widget之间有了很大的耦合,也产生很多的冗余数据。 为了解决这个问题,Flutter SDK提供了InheritedWidget这个Widget,InheritedWidget是除了StatefulWidget和StatelessWidget之外的第三个常用的Widget。当把InheritedWidget作为Widget Tree的根节点时,这个Widget Tree就具有了一些新的功能,例如,Child Widget可以根据BuildContext找到最近的指定类型的InheritedWidget,而不是通过Widget Tree的构造函数一层层进行传递,如下图所示。 InheritedWidget的使用其实非常简单,即共享数据给Child。所以它的核心点,其实就是两个。 需要共享的数据 重新updateShouldNotify的条件 通...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Mario游戏-低调大师作品
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8编译安装MySQL8.0.19
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用