JVM运行时数据区域详解
参考文章:
- 《Java Se11 虚拟机规范》
- 《深入理解Java虚拟机-JVM高级特性与最佳实践 第3版》- 周志明
本文基于Java Se 11讲解。
根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域:
对于不同的虚拟机实现,在运行时数据区的实现上并不完全相同。对于常用的HotSpot虚拟机来说,它的运行时数据区如下:
主要区别在于,HotSpot使用了直接使用本地内存(即机器本身内存)的元空间(metaspace)来实现方法区。
下面针对每个具体的数据区域进行详细的介绍。
1. 程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
JVM可以同时支持多个执行线程。每个Java虚拟机线程都有自己的pc(程序计数器)寄存器。在任何时候,每个Java虚拟机线程都在执行单个方法的代码,即该线程的当前方法。如果该方法不是native方法,则pc寄存器包含当前正在执行的Java虚拟机指令的地址。如果线程当前正在执行的方法是native的,则pc寄存器的值为undefined
。Java虚拟机的pc寄存器足够宽,可以容纳特定平台上的returnAddress
或native指针。
此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError
情况的区域。
2. Java虚拟机栈
与程序计数器一样,是线程私有的,生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型。
「虚拟机栈」里面的每条数据就是「栈帧」,在 Java 方法执行的时候则创建一个「栈帧」并入栈「虚拟机栈」。调用结束则「栈帧」出栈。
每个栈帧包含四个区域:
- 局部变量表:存储了方法执行过程中需要用到的所有局部变量
- 操作数栈:暂存变量,通过变量的入栈、出栈等操作来执行计算
- 动态连接:翻译符号引用为直接引用,即把一个字面量翻译为运行时的一个地址引用
- 返回地址
每个线程拥有一个「虚拟机栈」,每个「虚拟机栈」拥有多个「栈帧」,而栈帧则对应着一个方法。每个「栈帧」包含局部变量表、操作数栈、动态链接、方法返回地址。方法运行结束则意味着该「栈帧」出栈。
在《Java虚拟机规范》中,对这个内存区域规定了两类异常状况:
- 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出
StackOverflowError
异常; - 如果Java虚拟机栈容量可以动态扩展(HotSpot虚拟机的栈容量不能动态扩展),当栈尝试扩展时无法申请到足够的内存,或为一个新线程初始化JVM栈时没有足够的内存时会抛出
OutOfMemoryError
异常。
3. 本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
《Java虚拟机规范》对本地方法栈中方法使用的语言、使用方式与数据结构并没有任何强制规定,因此具体的虚拟机可以根据需要自由实现它,甚至有的Java虚拟机(譬如Hot-Spot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError
和OutOfMemoryError
异常。
4. Java堆
所有线程共享,虚拟机启动时创建。唯一的目的是用于存放对象实例和数组,绝大部分对象实例在堆上分配内存。
在 Java 中,数组也是对象。
现代垃圾收集器大部分基于分代收集理论设计。“新生代”、“老年代”这些名词仅仅是一部分GC的设计风格,而不是《Java虚拟机规范》定义的。而从G1收集器出现之后,出现了不采用分代设计的新垃圾收集器。
JDK8之后Class对象、static
变量、字符串常量池都放在堆里。
static
变量作为类的信息,存储在Class对象里。
Java 的对象可以分为基本数据类型和普通对象。普通对象会在堆上分配。对于基本数据类型,如果是局部变量,则会在栈上分配。其他情况,通常在在堆上分配,逃逸分析的情况下可能会在栈分配。
如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError
异常。
4.1 字符串常量池
字符串常量池是由String
类维护的一个字符串池。是一种池化思想的实现,是为了节省重复创建字符串对象的性能开销和内存空间。
每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。
字符串常量池从JDK7开始挪到了堆中。
可以通过调用String.intern()
方法把一个字符串对象放到字符串常量池中。如果池中已经存在相等的对象,则会返回已存在对象的引用;否则会把这个字符串对象加入到池中,并返回新加入的字符串对象的引用。
String s = new String("hello")
会创建几个对象?
如果字符串常量池中没有"hello",则生成2个,否则只生成一个。
String s = new String("abc"); System.out.println((s.intern() == s));
打印结果是什么?
打印结果为false。s
指向的是堆中的对象,s.intern()
返回的是字符串常量池中的对象的引用。
4.2 字面量和常量
字面量(literal) :用于表达源码中的一个固定值的符号(notation)。如整数、浮点数及字符串等。如1
、0x01
是整数字面量,Hello World
是字符串字面量。
常量:在java中,final
修饰的变量也可以被称为是常量。任何具有不变性的东西都可以称为常量。如String
对象是常量。
对象池:是Java语言层面实现的,如Integer.valueOf()
(Integer i = 10
也会调该方法)会使用IntegerCache
的缓存对象。如果使用new Integer(10)
则不会使用对象池中的实例。
字符串常量池:类似于对象池,但它是JVM层面的技术。字符串常量池的实现是c++实现的StringTable
,实际上是一个固定容量的Hashtable
,每一个bucket包含一系列相同hash码的字符串。
5. 方法区
用于存储被JVM加载的class的元数据信息,比如类的结构、运行时的常量池、字段、常量、方法数据、方法构造函数以及接口初始化等特殊方法。还有JIT编译器编译后的代码缓存等数据。
JDK8之前,HotSpot采用永久代的概念实现方法区,JDK8开始废弃了永久代的概念,改用在本地内存(Native Memory)中实现的元空间(Meta-space)来代替。
方法区的GC比较少出现,回收目标主要是针对常量池的回收和对类型的卸载。
根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError
异常。
5.1 运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
一般来说,除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。
既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError
异常。
JVM运行时数据区域详解的更多相关文章
- [jvm]运行时数据区域详解
了解虚拟机是怎么使用内存的,有助于我们解决和排查内存泄漏和溢出方面的问题.详解java虚拟机内存的各个区域,分析这些区域的作用服务对象以及可能发生的问题. 一.运行时数据区域 java虚拟机在执行ja ...
- JVM——内存区域:运行时数据区域详解
关注微信公众号:CodingTechWork,一起学习进步. 引言 我们经常会被问到一个问题是Java和C++有何区别?我们除了能回答一个是面向对象.一个是面向过程编程以外,我们还会从底层内存管理 ...
- JVM 运行时数据区详解
一.运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域. 1.有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,所有的线程共享这些数据区. 2.第二种则 ...
- JVM运行时数据区域
上面已经聊过JVM是什么东东,也谈过了JVM内存的垃圾回收机制.这一篇博客我们来聊聊JVM运行时数据区域. JVM运行时数据区域由5块部分组成,分别是堆,方法区,栈,本地方法栈,以及程序计数器组成. ...
- 深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域
深入理解Java虚拟机 -- 读书笔记:JVM运行时数据区域 本文转载:http://blog.csdn.net/jubincn/article/details/8607790 本系列为<深入理 ...
- JVM 运行时数据区域划分
目录 前言 什么是JVM JRE/JDK/JVM是什么关系 JVM执行程序的过程 JVM的生命周期 JVM垃圾回收 JVM的内存区域划分 一.运行时数据区包括哪几部分? 二.运行时数据区的每部分到底存 ...
- Java 虚拟机运行时数据区详解
本文摘自深入理解 Java 虚拟机第三版 概述 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟 ...
- JVM 运行时数据区域
Java虚拟机管理的内存包括以下几个运行时数据区域: 1.程序计数器: 程序计数器是一块比较小的内存空间,是当前线程执行的字节码行号指示器.Java多线程是通过线程轮流切换来实现的,所以每个线程都有一 ...
- 深入理解Java虚拟机-JVM运行时数据区域
一.运行时数据区域 1.程序计数器 程序计数器( Program Counter Register) 是一块较小的内存空间, 它可以看作是当前线程所执行的字节码的行号指示器. Java虚拟机的多线程是 ...
随机推荐
- 快速生成组件语法模板的插件:Auto Close Tag
好家伙, 这是一个"标签闭合"插件 Auto Close Tag的安装: 来到VScode的拓展 安装后, 在其中输入一个左标签符号"<",随后会出现提示 ...
- volatile 函数影响子查询提升
我们知道 volatile 函数会影响SQL的执行性能,比如:volatile 类型函数无法建函数索引.volatile 函数针对每条记录都要执行一次.本篇的例子主要讲述 volatile 类型的函数 ...
- 微信小程序语音提示
一. 老规矩, 先上demo图: 然后通过 wx.createInnerAudioContext 创建内部 audio 上下文 InnerAudioContext 对象 就能播放 filename ...
- idea每次换行后光标都跑到最左边问题
最进用idea时发现每次换行之后一段时间光标会自动跑到最左边,默认把我的首行空格删掉了 IDEA版本为:IntelliJ IDEA 2020.2.3 x64
- P8539 「Wdoi-2」来自地上的支援 题解
思路 根据题意,如果每次询问选中的为第 \(x\) 个数,那么前 \(x-1\) 次操作一定不会选中第 \(x\) 个数.(感觉在说废话.) 同样,因为第 \(x\) 个数必须被选中 \(k\) 次, ...
- 统一建模语言UML---类图
什么是统一建模语言,来看看百科中的介绍统一建模语言(Unified Modeling Language,UML)是一种为面向对象系统的产品进行说明.可视化和编制文档的一种标准语言,是非专利的第三代建模 ...
- k8s中安装各软件的yaml文件
网址:https://www.kubebiz.com/ 网站:https://k8syaml.com/
- ProxySQL的双层用户认证机制
转载自:https://www.likecs.com/show-203802325.html 如果使用了ProxySQL来做中间路由,那么与我们平时登录数据库有一些区别:平时我们直接使用数据库的用户密 ...
- 使用Kuboard界面在k8s上部署SpringCloud项目
先安装Ingress Controller 安装Ingress Controller后,其他服务设置Ingress后就可以通过设置的域名进行访问了,就不用通过代理的方式或者ip:port的方式进行访问 ...
- elk使用微信ElartAlert企业微信告警,自定义告警内容
第一种方式 alert: - "elastalert_modules.wechat_qiye_alert.WeChatAlerter" alert_text: " === ...