运行时数据区域

总览

JDK. 1.7 之后版本略有不同

Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。

有必要深入了解这块的内容,因为它将决定服务器性能,除此之外还有助于快速定位虚拟机的相关Error。

首先来对整个运行时区域有一个整体的认识。

如下图

JDK 1.7 之前:

JDK 1.7 以及之后(1.8正式使用,1.7还需要手动设置一下) :

  • 线程私有的(图中红色)

  • 线程共享的(图中绿色、蓝色)

概念扫盲

什么是栈帧(Stack Frame)

每一次函数的调用,都会在调用栈上维护一个独立的栈帧,每个独立的栈帧一般包括:

  • 函数的返回地址和参数
  • 临时变量
  • 函数调用的上下文

栈是从高地址向低地址延伸,一个函数的栈帧用ebpesp 这两个寄存器来划定范围。

ebp 指向当前的栈帧的底部,esp 始终指向栈帧的顶部。

  • ebp 寄存器又被称为帧指针(Frame Pointer)
  • esp 寄存器又被称为栈指针(Stack Pointer)

JVM常见出现两种错误

  • StackOverFlowError 若 Java 虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
  • OutOfMemoryError Java 虚拟机栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常异常。

程序计数器

程序计数器占用较小的一块内存空间,每条线程都需要有一个独立的程序计数器,程序计数器用于记录当前线程执行的位置,从而当线程被来回切换的时候,能够知道该线程上次运行到哪儿了。

字节码解释器工作时通过改变这个计数器的值,来选取下一条需要执行的字节码指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域。

虚拟机栈

结构

虚拟机栈也是线程私有,而且生命周期与线程相同。

每个Java方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息

局部变量表

  • 存放编译器可知的各种基本数据类型(boolean、byte等)
  • 对象引用(reference类型,它不等同于对象本身)
    • 可能是一个指向对象起始地址的引用指针
    • 也可能是指向另一个代表对象的句柄
    • 其他次对象相关的位置
  • returnAddress类型,指向了一条字节码指令的地址

方法是如何调用的

每一次函数调用都会有一个对应的栈帧被压入 Java 栈,每一个函数调用结束后,都会有一个栈帧被弹出。

Java 方法有两种返回方式:

  1. return 语句。
  2. 抛出异常。

不管哪种返回方式都会导致栈帧被弹出。

本地方法栈

主要为虚拟机使用到的Native方法服务,作用其实类似虚拟机栈,其结构也和虚拟机栈一样

二者的区别是虚拟机栈为虚拟机执行字节码服务

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间。

在 HotSpot 虚拟机中和虚拟机栈合二为一

Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建

此内存区域的目的是存放对象实例几乎所有的对象实例以及数组都在这里分配内存。

说是几乎是因为由于多项技术的进步与成熟,如:逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术,一些对象也可能在栈上分配内存。

Java 堆是JVM中最大的一块内存区域,也是是垃圾回收(Garbage Collected)管理的主要区域,故又叫做GC堆

浅堆和深堆

浅堆和深堆是两个非常重要的概念,理解他们之前需要先了解什么是保留集。

保留集,即为单一对象所持有的对象的集合,如图:

  • 浅堆是指一个对象所消耗的内存。如上图
  • 深堆是指对象的保留集中所有的对象浅堆大小之和

堆的细分

HotSpot中还有永久代的概念,不过已经是历史了。

JDK 8 HotSpot 的永久代被彻底移除,取而代之是元空间,元空间使用的是直接内存。

现在垃圾收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分,堆分为新生代(占堆1/3),老生代(占堆2/3)

  • 新生代(内部比例8:1:1)

    • Eden 空间
    • From Survivor 空间
    • To Survivor 空间
  • 老年代

进一步划分的目的是更好地回收内存,或者更快地分配内存。

流程:

  • 大多数情况,对象都会首先在 Eden 区域分配
  • 在一次新生代垃圾回收后,如果对象还存活,则会进入两个Survivor中的一个,然后对象的年龄加 1
  • 它的年龄增加到年龄阈值(默认为 15 ),就会被晋升到老年代中

对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 设置

方法区

方法区与 Java 堆一样,也是所有线程共享的。

主要用于存储类的信息、常量池、方法数据、方法代码等。

方法区逻辑上属于堆的一部分,但是为了与堆进行区分,有一个别名叫做 Non-Heap(非堆)

该区域的内存回收目标主要针对常量池的回收类型的卸载

在HotSpot虚拟机中,用永久代来实现方法区,但是这样容易遇到内存溢出的问题,所以在Java 8之后就取消了方法区。

方法区和永久代的关系

摘自《深入理解Java虚拟机》第三版

《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。 方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。 也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。

为什么要将永久代替换为元空间 ?

  • 永久代内存有一个JVM固定的上限,经常会出现OutOfMemoryError
  • 元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。
  • 元空间里面存放的是类的元数据,由系统的实际可用空间来控制,这样能加载的类就变多了。
  • 在 JDK8,合并 HotSpot 和 JRockit 的代码时,JRockit 没有永久代,如果强行保留实现起来困难重重。

当元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace

运行时常量池

运行时常量池用于存放编译期间生成的各种字面量符号引用,是方法区的一部分。

运行时常量池用来动态获取类信息,包括:

  • Class文件元信息描述
  • 编译后的代码数据
  • 引用类型数据
  • 类文件常量池

运行时常量池是在类加载完成之后,将每个Class常量池中的符号引用值转存到运行时常量池中。

每个Class都有一个运行时常量池,类在解析之后将符号引用替换成直接引用,与全局常量池中的引用值保持一致

运行时常量池相的另外一个重要特性是具备动态性,Java语言并不要求常量一定只有编译器才能产生,也就是并非预置入Class文件中的常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中。

直接内存

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。

使用的方式是通过 JDK1.4 中加入的NIO(New Input/Output)类,它可以直接使用 Native 函数库直接分配堆外内存

通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。

避免了在 Java 堆Native 堆之间来回复制数据,在一些场景中显著提高了性能,

本机直接内存的分配不受 Java 堆的限制,但受到本机总内存大小,以及处理器寻址空间的限制,因此也可能导致 OutOfMemoryError 错误出现。

总结

以上的各个分区,各司其职,是了解Java虚拟机的基础。

理解各区域的指责和作用,对JVM后续的学习有非常大的帮助,如果这些没搞懂,后面学起来是真头大‍。

结合图例,相信可以较为清晰了理解各分区的架构和指责,觉得有用欢迎点个推荐、点个赞。

参考:

《深入理解Java虚拟机》第三版 ——周志明 (吹爆)

JVM虚拟机-运行时数据区概述的更多相关文章

  1. 【JVM之内存与垃圾回收篇】运行时数据区概述及线程

    运行时数据区概述及线程 前言 本节主要讲的是运行时数据区,也就是下图这部分,它是在类加载完成后的阶段 当我们通过前面的:类的加载-> 验证 -> 准备 -> 解析 -> 初始化 ...

  2. 【JVM从小白学成大佬】2.Java虚拟机运行时数据区

    目录 1.运行时数据区介绍 2.堆(Heap) 是否可能有两个对象共用一段内存的事故? 3.方法区(Method Area) 4.程序计数器(Program Counter Register) 5.虚 ...

  3. 【JVM学习】2.Java虚拟机运行时数据区

    来源: 公众号: 猿人谷 这里我们先说句题外话,相信大家在面试中经常被问到介绍Java内存模型,我在面试别人时也会经常问这个问题.但是,往往都会令我比较尴尬,我还话音未落,面试者就会"背诵& ...

  4. 面试常问的 Java 虚拟机运行时数据区

    写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...

  5. Java 虚拟机运行时数据区

    写在前面 本文描述的有关于 JVM 的运行时数据区是基于 HotSpot 虚拟机. 概述 JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以 ...

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

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

  7. JVM入门——运行时数据区

    这张图我相信基本上对JVM有点接触的都应该很熟悉,可以说这是JVM入门的第一课.其中的“堆”和“虚拟机栈(栈)”更是耳熟能详.下面将围绕这张图对JVM的运行时数据区做一个简单介绍. 程序计数器(Pro ...

  8. jvm理论-运行时数据区

    三大流行jvm sun HotSpot ibm j9 BEA JRockit Oracle 会基于HotSpot整合 JRockit. jvm运行时数据区 java虚拟机所管理的内存将会包括以下几个运 ...

  9. 《深入理解Java虚拟机》(二)Java虚拟机运行时数据区

    Java虚拟机运行时数据区 详解 2.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第二章 ,为了整理思路,简单记录一下,方便后期查阅. 2.2 运行时数据区域 Java虚拟机 ...

随机推荐

  1. docker 部署mysql服务之后,执行sql脚本

    1,先将.sql文件copy到docker容器里 docker ps //找到容器的短ID或者指定的name. docker inspect  -f '{{.Id}}' id or name 得到指定 ...

  2. vue项目安装sass步骤等遇到的问题

    1.安装sass依赖包 npm install --save-dev sass-loader 注释(可能会出现问题:sass-loader版本过高导致,可以将其package.json中的版本改为7. ...

  3. 【odoo14】第三章、创建插件

    现在我们已经有了开发环境并了解了如何管理实例及数据库,现在让我们来学习下如何创建插件模块. 本章内容如下: 创建和安装模块 完成manifest文件 组织模块文件结构 添加模型 添加菜单及视图 添加访 ...

  4. 为什么是InfluxDB | 写在《InfluxDB原理和实战》出版之际

    1年前写的一篇旧文,文中的分析,以及探讨的问题和观点,至今仍有意义. 从2016年起,笔者在腾讯公司负责QQ后台的海量服务分布式组件的架构设计和研发工作,例如微服务开发框架SPP.名字路由CMLB.名 ...

  5. concurrentHashMap的put方法详解

    本文主要介绍ConcurrentHashMap的put操作如果有错误的地方欢迎大家指出. 1.ConcurrentHashMap的put操作 ConcurrentHashMap的put操作主要有3种方 ...

  6. OpenCV图像处理中“找圆技术”的使用

    一.为什么"找圆"     圆是基本图形的一种,更为重要的是,自然情况下采集的图像,很少大量存在"圆":但凡存在的,大都是人工的,那么就必然代表特定的意义,从而 ...

  7. Java中的集合Set - 入门篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的集合Set - 入门篇>,希望对大家有帮助,谢谢 简介 前面介绍了集合List,映射Map,最后再简单介绍下集合Set,相关类如下 ...

  8. 专家动态页面的实现——php基于CI框架的学习(二)

    以下是本次学习的页面 打开相关文件,整个定义了一个Expert类 class Expert extends CI_Controller{} 在Expert类里定义了几个参数以及说明其使用了哪些mode ...

  9. --系统编程-网络-tcp客户端服务器编程模型、socket、htons、inet_ntop等各API详解、使用telnet测试基本服务器功能

    PART1 基础知识 1. 字节序 网络字节序是大端字节序(低地址存放更高位的字节), 所以,对于字节序为小端的机器需要收发网络数据的场景,要对这些数据进行字节序转换. 字节序转换函数,常用的有四个: ...

  10. Leedcode算法专题训练(贪心)

    1. 分配饼干 455. 分发饼干 题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足.求解最多可以获得满足的孩子数 ...