详解JVM中的内存模型是什么?
强烈推荐
不管是找工作还是提升水平,都建议读一下《深入理解Java虚拟机》这本书,详细讲解了JVM中的内存管理、类加载过程、垃圾回收以及最重要的性能调优实战。
本博客也是参考了这本书,有不对的地方还请指正。在这里给大家准备了电子版和视频教程,详情请扫右侧公告栏中二维码后,回复【java虚拟机】获得。
一、快速扫盲
1. JVM是什么
JVM是Java Virtual Machine的缩写,即咱们经常提到的Java虚拟机。虚拟机是一种抽象化的计算机,有着自己完善的硬件架构,如处理器、堆栈等,具体有什么咱们不做了解。目前我们只需要知道想要运行Java文件,必须先通过一个叫javac的编译器,将代码编译成class文件,然后通过JVM把class文件解释成各个平台可以识别的机器码,最终实现跨平台运行代码。
2. JDK、JRE、JVM之间的关系
- JDK:全称为Java Development Kit,汉语为java开发工具包,即所有有关java的东西都包含在里面,比如运行环境JRE、java的核心代码、JVM等等。
- JRE:全称为Java Runtime Environment,汉语为java运行环境,即想要运行java文件必须先有java的环境才行,jre就是提供了这么一个环境。
- JVM:上面已经提到了JVM,它是java最核心的部分。
简单用一张图来理解这三个的关系:

3. jvm的组成成分

不了解jvm的同学看到这张图后可能会有点懵逼,不过没关系,放这张图只是想让你了解jvm中有三块内容非常重要,1.java代码如何执行?2.内存如何管理?3.线程资源如何利用?脑袋里有个印象即可,带着问题去学习。文章最下方有本博客参考书和视频下载地址。
4. 运行java文件的大概流程
想要运行java的源文件,必须要经过javac编译器编译成.class文件,也就是字节码文件。然后通过jvm中的解释器,解释成特定机器上的机器码。每种机器上的解释器是不一样的,我们经常用的也就是windows和linux系统,这也是为什么java能够跨平台的原因。当一个程序从开始运行,虚拟机就开始实例化,多个程序运行就会存在多个虚拟机实例,程序退出或者关闭,虚拟机实例也将随之消亡,多个虚拟机之间的数据是不共享的。
二、JVM运行时数据区
1. 运行时数据区域组成
虚拟机在执行java程序时,会将自己管理的内存划分为几个区域,每个区域都有自己的用途,并且创建时间和销毁时间也不一样。在程序运行时的内存区域主要可以划分为五个,分别是:方法区、堆、虚拟机栈、本地方法栈、程序计数器。可以用下面的图来描述:

2. Java堆
Java堆是java虚拟机所管理的内存中最大的一块,是被所有线程都共享的内存区域。存在的唯一目的就是存放对象实例,几乎所有的对象实例都在这里进行分配内存。不过目前随着技术的不断发展,也并不是所有的对象实例都在堆中分配内存,可能也存在栈上分配。由于所占空间大,又存放各种实例对象,因此java虚拟机的垃圾回收机制主要管理的就是此区域,详细的垃圾回收方法以后会提到。JVM规范中规定堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。并且可以通过-Xmx和-Xms来扩展堆的内存大小,如果在堆中没有足够的内存为实例分配,并且堆也无法在扩展时,就会报OutOfMemoryError异常。
3 方法区
跟Java堆一样,方法区是各个线程共享的内存区域,此区域是用来存储类的信息(类的名称、字段信息、方法信息)、静态变量、常量以及编译器编译后的代码。JVM规范中并不区分方法区和堆,只把方法区描述为堆的逻辑部分,但是它却有一个别名叫做非堆(Non-Heap),目的就是与Java堆区分开。根据垃圾回收机制中分代回收的思想,如果在HotSpot虚拟机上开发,可以把方法区称为“永久代”(只是可以这么理解,但实质是不一样的),垃圾回收机制在Java堆中划分一个部分称为永久代,用此区域来实现方法区,这样HotSpot的垃圾收集器就可以像管理Java堆一样管理这部分内存,而不必为方法区开发专门的内存管理器。
运行时常量池
运行时常量池是方法区的一个部分,class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期间生成的各种字面量和符号引用,这部分内容会在类加载后进入方法区的运行时常量池中。Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。
4. 程序计数器
虽然在上图中程序计数器的面积很大,但实际上它是一块较小的内存空间,可以看做当前线程所执行字节码的行号指示器。字节码解释器在工作中时下一步该干啥、到哪了,就是通过它来确定的。大家都知道在多线程的情况下,CPU在执行线程时是通过轮流切换线程实现的,也就是说一个CPU处理器(假设是单核)都只会执行一条线程中的指令,因此为了线程切换后能恢复到正确的执行位置,每个线程都要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。很明显,程序计数器就是线程私有的。如果线程正在执行的是一个java方法,程序计数器记录的是正在执行的虚拟机字节码指令地址;如果执行的Native方法,程序计数器记录的值为空(Undefined),此内存区域是java中唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
5. Java虚拟机栈
我们经常会把java内存粗糙的分为两个部分,堆和栈,Java虚拟机栈就是栈这一部分,或者说是虚拟机栈中局部变量表部分。跟程序计数器一样,虚拟机栈也是线程私有的,它的生命周期跟线程相同。每个方法在执行的同时都会创建一个栈帧(Stack Frame),每个栈帧对应一个被调用的方法,栈帧中用于存储局部变量表、操作数栈、动态链表、方法出口等信息。每一个方法从开始执行到结束就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
- 局部变量表:顾名思义,他就是用来存储方法中的局部变量(包括在方法中生命的非静态变量以及函数形参),对于基本数据类型,直接存值,对于引用类型的变量,存储指向该对象的引用。由于它只存放基本数据类型的变量、引用类型的地址和返回值的地址,这些类型所需空间大小已知且固定,所以当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全可以确定的,在方法运行期间也不会改变局部变量表的大小。
- 指向运行常量池的引用:在方法执行过程中难免会使用到类中定义的常量,因此栈帧中要存放一个指向运行时常量池的引用。
- 方法返回地址:当一个方法执行结束后,要返回到之前调用它的地方,因此在栈帧中需要保存一个方法返回地址。
6. 本地方法栈
本地方法栈与虚拟机栈的功能非常的相似,区别不过是虚拟机栈为虚拟机执行java方法服务,而本地方法栈为虚拟机执行Native方法服务。有的虚拟机并不会区分本地方法栈和虚拟机栈,比如Sun HotSpot虚拟机直接将两个合二为一。
7. 用一张图总结

详解JVM中的内存模型是什么?的更多相关文章
- (转载)JVM中的内存模型与垃圾回收
转载自微信公众号:Java高级架构(Java-jiagou)-----看完这篇文章,我奶奶都知道JVM中的内存模型与垃圾回收了! 六.内存模型 6.1 内存模型与运行时数据区 Java虚拟机在执行J ...
- 看完这篇文章,我奶奶都知道什么是JVM中的内存模型与垃圾回收!
扩展阅读:JVM从入门开始深入每一个底层细节 六.内存模型 6.1.内存模型与运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干不同数据区域. Java内存模型的主要目 ...
- Java虚拟机详解----JVM内存结构
http://www.cnblogs.com/smyhvae/p/4748392.htm 主要内容如下: JVM启动流程 JVM基本结构 内存模型 编译和解释运行的概念 一.JVM启动流程: JVM启 ...
- Java虚拟机详解----JVM常见问题总结
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- 【转载】NeurIPS 2018 | 腾讯AI Lab详解3大热点:模型压缩、机器学习及最优化算法
原文:NeurIPS 2018 | 腾讯AI Lab详解3大热点:模型压缩.机器学习及最优化算法 导读 AI领域顶会NeurIPS正在加拿大蒙特利尔举办.本文针对实验室关注的几个研究热点,模型压缩.自 ...
- Java 详解 JVM 工作原理和流程
Java 详解 JVM 工作原理和流程 作为一名Java使用者,掌握JVM的体系结构也是必须的.说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Java ...
- 详解C++中的多态和虚函数
一.将子类赋值给父类 在C++中经常会出现数据类型的转换,比如 int-float等,这种转换的前提是编译器知道如何对数据进行取舍.类其实也是一种数据类型,也可以发生数据转换,但是这种转换只有在 子类 ...
- 图文详解Unity3D中Material的Tiling和Offset是怎么回事
图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...
- 详解Java中的clone方法
详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...
随机推荐
- java方法中参数传递与随机点名器、库存管理案例
一 参数传递 1.定义: 参数传递,可以理解当我们要调用一个方法时,我们会把指定的数值,传递给方法中的参数, 这样方法中的参数就拥有了这个指定的值,可以使用该值,在方法中运算了.这种传递方式,我 们称 ...
- C#LeetCode刷题之#695-岛屿的最大面积( Max Area of Island)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3736 访问. 给定一个包含了一些 0 和 1的非空二维数组 gr ...
- oracle正则表达式语法介绍及实现手机号码匹配方法
Oracle10g提供了在查询中使用正则表达的功能,它是通过各种支持正则表达式的函数在where子句中实现的.本文将简单的介绍oracle正则表达式常用语法,并通过一个手机特号匹配的例子演示正则表达式 ...
- HahMap(jdk=1.8)源码解读
简介:岁月磨平了人的棱角,让我们不敢轻易的去放手,即使它在你心中并不那么重要,你依旧害怕失去它,不是舍不得,是内心的迷茫. 一 : 创建HashMap HashMap<Object, Objec ...
- 【有向图】强连通分量-Tarjan算法
好久没写博客了(都怪作业太多,绝对不是我玩的太嗨了) 所以今天要写的是一个高大上的东西:强连通 首先,是一些强连通相关的定义 //来自度娘 1.强连通图(Strongly Connected Grap ...
- java Semaphore实现ABC三个线程循环打印
Semaphore位于java.util.concurrent包下.其中有两个重要的方法acquire()和release().acquire用来获取一个信号量,并且是阻塞型的,如果当前还有可用的信号 ...
- linux命令查询网站
http://linux.51yip.com/ http://man.linuxde.net/ Linux命令查询手册Linux终端下 esc + . 可以获取上次文件名
- BIGI行情-实时行情数据源接口websocket接入方法
BIGI行情-实时行情数据源接口socket接入方法1.国际期货.国内期货.外汇.贵金属.现货.期权.股指.数字货币和A股实时行情和历史行情2.推送的有:socket,websocket,http接收 ...
- ARM开发板挂载Ubuntu18.04主机的NFS共享文件夹
环境 ubuntu主机环境:Window10 下装VMWare下装的 ubuntu18.04LTS x64 IP 192.168.10.119 Window10下配置192.168.10该网段 开发板 ...
- IDEA 代码自动补全/自动联想 功能
IDEA 的代码补全/自动联想功能,可以仅仅输入几个字母,自动补全一整段代码,非常舒服. 代码自动联想功能在 设置 -> Editor -> Live Templates 查看,很多都非常 ...