参考文章:

本文基于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 方法执行的时候则创建一个「栈帧」并入栈「虚拟机栈」。调用结束则「栈帧」出栈。

每个栈帧包含四个区域:

  1. 局部变量表:存储了方法执行过程中需要用到的所有局部变量
  2. 操作数栈:暂存变量,通过变量的入栈、出栈等操作来执行计算
  3. 动态连接:翻译符号引用为直接引用,即把一个字面量翻译为运行时的一个地址引用
  4. 返回地址

每个线程拥有一个「虚拟机栈」,每个「虚拟机栈」拥有多个「栈帧」,而栈帧则对应着一个方法。每个「栈帧」包含局部变量表、操作数栈、动态链接、方法返回地址。方法运行结束则意味着该「栈帧」出栈。

在《Java虚拟机规范》中,对这个内存区域规定了两类异常状况:

  1. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
  2. 如果Java虚拟机栈容量可以动态扩展(HotSpot虚拟机的栈容量不能动态扩展),当栈尝试扩展时无法申请到足够的内存或为一个新线程初始化JVM栈时没有足够的内存时会抛出OutOfMemoryError异常。

3. 本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务

《Java虚拟机规范》对本地方法栈中方法使用的语言、使用方式与数据结构并没有任何强制规定,因此具体的虚拟机可以根据需要自由实现它,甚至有的Java虚拟机(譬如Hot-Spot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowErrorOutOfMemoryError异常。

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)。如整数、浮点数及字符串等。如10x01是整数字面量,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运行时数据区域详解的更多相关文章

  1. [jvm]运行时数据区域详解

    了解虚拟机是怎么使用内存的,有助于我们解决和排查内存泄漏和溢出方面的问题.详解java虚拟机内存的各个区域,分析这些区域的作用服务对象以及可能发生的问题. 一.运行时数据区域 java虚拟机在执行ja ...

  2. JVM——内存区域:运行时数据区域详解

    关注微信公众号:CodingTechWork,一起学习进步. 引言   我们经常会被问到一个问题是Java和C++有何区别?我们除了能回答一个是面向对象.一个是面向过程编程以外,我们还会从底层内存管理 ...

  3. JVM 运行时数据区详解

    一.运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域. 1.有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,所有的线程共享这些数据区. 2.第二种则 ...

  4. JVM运行时数据区域

    上面已经聊过JVM是什么东东,也谈过了JVM内存的垃圾回收机制.这一篇博客我们来聊聊JVM运行时数据区域. JVM运行时数据区域由5块部分组成,分别是堆,方法区,栈,本地方法栈,以及程序计数器组成. ...

  5. 深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域

    深入理解Java虚拟机 -- 读书笔记:JVM运行时数据区域 本文转载:http://blog.csdn.net/jubincn/article/details/8607790 本系列为<深入理 ...

  6. JVM 运行时数据区域划分

    目录 前言 什么是JVM JRE/JDK/JVM是什么关系 JVM执行程序的过程 JVM的生命周期 JVM垃圾回收 JVM的内存区域划分 一.运行时数据区包括哪几部分? 二.运行时数据区的每部分到底存 ...

  7. Java 虚拟机运行时数据区详解

    本文摘自深入理解 Java 虚拟机第三版 概述 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟 ...

  8. JVM 运行时数据区域

    Java虚拟机管理的内存包括以下几个运行时数据区域: 1.程序计数器: 程序计数器是一块比较小的内存空间,是当前线程执行的字节码行号指示器.Java多线程是通过线程轮流切换来实现的,所以每个线程都有一 ...

  9. 深入理解Java虚拟机-JVM运行时数据区域

    一.运行时数据区域 1.程序计数器 程序计数器( Program Counter Register) 是一块较小的内存空间, 它可以看作是当前线程所执行的字节码的行号指示器. Java虚拟机的多线程是 ...

随机推荐

  1. 【java】学习路径27-HashSet、TreeSet,HashMap

    学习路径20-27的所有源代码都可以在此下载 https://www.aliyundrive.com/s/cg8jTRbg6vy HashSet.TreeSet中,Set表示集合,特性在于:无序的.不 ...

  2. 试用 ModVB(一):安装教程+使用 JSON 常量和 JSON 模式匹配

    前排提醒:阅读此文章并充分尝试 ModVB 的新语法需要较长的时间.对于程序员而言,如果你工作时不用 VB,则最好避免在上班时间看,以免被领导认为你在长时间摸鱼. 什么是 ModVB ModVB 是一 ...

  3. 简单创建一个SpringCloud2021.0.3项目(三)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上俩篇教程 3. Gateway集成sentinel,网关层做熔断降级 1. 超时熔断降级 2. 异常熔断 3. 集成sentine ...

  4. 第六十一篇:Vue的绑定事件和修饰符

    好家伙,补基础加实践 1.绑定事件 我们使用v-on(简写为@)来绑定事件 写个例子, 按钮绑定数字加一(太tm经典了) 在<button>元素中使用@点击事件绑定方法"的&qu ...

  5. openstack中Keystone组件简解

    一.Keystone服务概述 在Openstack框架中,keystone(Openstack Identity Service)的功能是负责验证身份.校验服务规则和发布服务令牌的,它实现了Opens ...

  6. fastadmin后台分页设置显示方法

    ​ 1.参照日志列表的分页(后台代码都有) 2.修改默认分页配置,在初始化里面加上: pageList: [5,10,'all'], 3.显示列表: [$where, $sort, $order, $ ...

  7. DFS文件夹无法访问

    最近DFS的文件服务器出现了部分文件和文件夹无法访问的情况.客户端直接访问DFS成员的共享文件夹时有是会出现Element not found的错误.有时打开文件的时候会出现文件不存在,或者你没有权限 ...

  8. eclipse最常应用的几个快捷键,新手必看!

    首先eclipse快捷键可以使用 Ctrl + Shift + L 打开,在这里可以查看所有快捷键. 另外就是常用的几个快捷键 选中光标所在行 好像没有,但是可以 Ctrl + d 然后 Ctrl + ...

  9. getSessionFactory().openSession()导致druid连接池中的连接都占用满但无法回收

    该问题产生的现象 页面刷新几次后,就卡住,线上就得需要重新部署(还好是测试环境,不是真正生产环境) 过程及原因 查看日志线程池满了 Caused by: org.springframework.jdb ...

  10. ProxySQL SSL 配置

    后端 SSH 连接配置 从版本 v1.2.0e 开始,ProxySQL 支持对后端使用 SSL 连接. 重要提示: 仅支持 v1.x 中的后端 SSL.在 v2.x 之前的版本中,客户端是无法使用 S ...