深入理解Java虚拟机-JVM运行时数据区域
一、运行时数据区域
1.程序计数器
程序计数器( Program Counter Register) 是一块较小的内存空间, 它可以看作是当前线程所执行的字节码的行号指示器。 Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻, 一个处理器( 对于多核处理器来说是一个内核) 都只会执行一条线程中的指令。 因此, 为了线程切换后能恢复到正确的执行位置, 每条线程都需要有一个独立的程序计数器, 各条线程之间计数器互不影响, 独立存储, 我们称这类内存区域为“线程私有”的内存。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址; 如果正在执行的是Native方法, 这个计数器值则为空( Undefined) 。 此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2.Java虚拟机栈
与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放了编译期可知的各种基本数据类型( boolean、 byte、 char、 short、 int、float、 long、 double) 、 对象引用( reference类型, 它不等同于对象本身, 可能是一个指向对象起始地址的引用指针, 也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型( 指向了一条字节码指令的地址)。
在Java虚拟机规范中, 对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度, 将抛出StackOverflowError异常; 如果虚拟机栈可以动态扩展( 当前大部分的Java虚拟机都可动态扩展, 只不过Java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
3.本地方法栈
本地方法栈( Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。 在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。 与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
4.Java堆
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是: 所有的对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术逐渐成熟栈上分配、 标量替换优化技术将会导致一些微妙的变化发生, 所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。
Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。 从内存回收的角度来看, 由于现在收集器基本都采用分代收集算法, 所以Java堆中还可以细分为: 新生代和老年代;再细致一点的有Eden空间、 From Survivor空间、 To Survivor空间等。 从内存分配的角度来看, 线程共享的Java堆中可能划分出多个线程私有的分配缓冲区( Thread Local Allocation Buffer,TLAB)。不过无论如何划分, 都与存放内容无关, 无论哪个区域, 存储的都仍然是对象实例, 进一步划分的目的是为了更好地回收内存, 或者更快地分配内存。
根据Java虚拟机规范的规定, Java堆可以处于物理上不连续的内存空间中, 只要逻辑上是连续的即可, 就像我们的磁盘空间一样。 在实现时, 既可以实现成固定大小的, 也可以是可扩展的, 不过当前主流的虚拟机都是按照可扩展来实现的( 通过-Xmx和-Xms控制) 。 如果在堆中没有内存完成实例分配, 并且堆也无法再扩展时, 将会抛出OutOfMemoryError异常。
5.方法区
方法区( Method Area) 与Java堆一样, 是各个线程共享的内存区域, 它用于存储已被虚拟机加载的类信息、 常量、 静态变量、 即时编译器编译后的代码等数据。 虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分, 但是它却有一个别名叫做Non-Heap( 非堆) , 目的应该是与Java堆区分开来。
6.运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。 Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池( Constant Pool Table),用于存放编译期生成的各种字面量和符号引用, 这部分内容将在类加载后进入方法区的运行时常量池中存放。
运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性, Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池, 运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
字面量可以理解为实际值,int a = 8
中的8和String a = "hello"
中的hello都是字面量
符号引用就是一个字符串,只要我们在代码中引用了一个非字面量的东西,不管它是变量还是常量,它都只是由一个字符串定义的符号,这个字符串存在常量池里,类加载的时候第一次加载到这个符号时,就会将这个符号引用(字符串)解析成直接引用(指针)
7.直接内存
直接内存( Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。在JDK 1.4中新加入了NIO( New Input/Output) 类,引入了一种基于通道( Channel)与缓冲区( Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存, 然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。显然, 本机直接内存的分配不会受到Java堆大小的限制, 但是,既然是内存,肯定还是会受到本机总内存( 包括RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。 服务器管理员在配置虚拟机参数时, 会根据实际内存设置-Xmx等参数信息, 但经常忽略直接内存,使得各个内存区域总和大于物理内存限制( 包括物理的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。
深入理解Java虚拟机-JVM运行时数据区域的更多相关文章
- 深入理解Java虚拟机(一) 运行时数据区划分
前言:从我学Java的第一天开始,我的大学老师就告诉我 Java语言相比C.C++的语言有一个非常强大的功能,那就是自动内存管理:我们用Java编码时不需要申请或释放内存等,这些工作全部交由我们的Ja ...
- 深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域
深入理解Java虚拟机 -- 读书笔记:JVM运行时数据区域 本文转载:http://blog.csdn.net/jubincn/article/details/8607790 本系列为<深入理 ...
- JVM运行时数据区域
上面已经聊过JVM是什么东东,也谈过了JVM内存的垃圾回收机制.这一篇博客我们来聊聊JVM运行时数据区域. JVM运行时数据区域由5块部分组成,分别是堆,方法区,栈,本地方法栈,以及程序计数器组成. ...
- JVM 运行时数据区域划分
目录 前言 什么是JVM JRE/JDK/JVM是什么关系 JVM执行程序的过程 JVM的生命周期 JVM垃圾回收 JVM的内存区域划分 一.运行时数据区包括哪几部分? 二.运行时数据区的每部分到底存 ...
- Java虚拟机及运行时数据区
1.Java虚拟机的定义 Java虚拟机(Java Virtual Machine),简称JVM.当我们说起Java虚拟机时,可能指的是如下三种不同的东西: 抽象的虚拟机规范 规范的具体实现 一个运行 ...
- Java虚拟机_运行时数据区
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域. 这些区域都有各自的用途.各自的创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程启动 ...
- 【Java虚拟机】运行时数据区
Java在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途.创建和销毁的时间,有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,有些则是与线程一一对应,随 ...
- JVM运行时数据区域详解
参考文章: <Java Se11 虚拟机规范> <深入理解Java虚拟机-JVM高级特性与最佳实践 第3版>- 周志明 本文基于Java Se 11讲解. 根据<Java ...
- [jvm]运行时数据区域详解
了解虚拟机是怎么使用内存的,有助于我们解决和排查内存泄漏和溢出方面的问题.详解java虚拟机内存的各个区域,分析这些区域的作用服务对象以及可能发生的问题. 一.运行时数据区域 java虚拟机在执行ja ...
随机推荐
- ansible批量修改服务器密码
看了一下网上代码大多数是ansible-playbook实现的,需要写一个脚本,或者手动传递变量进去. 以前用python tcp模块写过客户端主动上报修改密码脚本 今天写一个ansible主控客户端 ...
- python-利用pymysql获取数据简单使用总结
python-利用pymysql获取数据库中的数据 1.前言 pymysql是一个第三方的模块,使用前需要安装 2.安装 pip install pymysql 3.基本使用 import pymys ...
- 大家好 这里是yi术家
好久没有来打卡了的说,可能你以为我忽然一时的热度刷题,但是事情并不是这样. 我在之前的章节里写到,我觉得每天的打卡有点浪费资源和时间,所以决定一周打一次卡. 可是上周好像也忘了打卡的样子. 这样任性的 ...
- Renix软件如何建立OSPF邻居——网络测试仪实操
OSPF可以通过OSPF向导的方式方便的创建OSPF邻居, 也可以通过纯手工的方式创建OSPF邻居, 本文介绍的是纯手工的方式创建. 在工作中, 推荐使用OSPF向导的方式来创建, 会比较简单和高效. ...
- VS编译时,出现无法将文件“obj\Debug\*.exe”复制到“bin\Debug\*.exe”。文件“bin\Debug\*.exe”正由另一进程使用,因此该进程无法访问此文件。
重命名将MyThread.exe 重命名 一下其他名字后就可以了.
- 【C#基础知识】C#控制台程序入口函数 Main(string[] args) 参数详解
测试环境vs2019+.net5.0 请看 :https://cloud.tencent.com/developer/article/1507934 本测试环境vs2022+.net6.0 +wind ...
- 【C# 基础概念】命名空间
命名空间 C#10 新功能==================== 命名空间格式:声明所有后续声明都是已声明的命名空间的成员: //C#10 命名空间的新写法,这种方式一个文件只能有一个命名空间. / ...
- 系统资源监控——联用awk与grep文本处理工具,截取磁盘使用量字段
一.使用到的命令行 1.df : df -h #将磁盘使用量用表的形式呈现. 2.awk: awk '{print $5}' #默认分隔符是空格,$后的数字是指定从第几列开始截取. awk -F [] ...
- 浏览器无插件播放rtsp流解决方案
1. 安装 FFmpeg 参考 CentOS下安装FFmpeg,特别详细. 我遇到的错误和解决办法: 缺少lame ffmpeg+libmp3lame库源码安装教程(CentOS) make ffmp ...
- Objective-C 基础教程第五章,复合
目录 Objective-C 基础教程第五章,复合 什么是复合? Car程序 自定义NSLog() 存取方法get Set Tires(轮胎) 存取方法 Car类代码的其他变化 扩展Car程序 复合还 ...