运行时数据区域

总览

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. 擅用ABAP错误捕捉,避免系统Dump

    有时候我们在写程序时,会因为计算公式不符合算术表达式,计算公式的字段值不是纯数值等等问题造成程序dump,这个时候我们在无法避免字段赋值错误的情况下,又不想程序dump可以采取catch异常的方法进行 ...

  2. [WC2014]时空穿梭

    这才叫莫比乌斯反演题. 一.题目 点此看题 二.解法 也没有什么好的思路,我们不妨把暴力柿子写出来,我们想枚举直线,但是这道题不能枚举直线的斜率,所以就要用整数来表示直线,我们不妨枚举出发点和终止点的 ...

  3. Reactive Spring实战 -- 响应式Redis交互

    本文分享Spring中如何实现Redis响应式交互模式. 本文将模拟一个用户服务,并使用Redis作为数据存储服务器. 本文涉及两个java bean,用户与权益 public class User ...

  4. java常见面试题3:线程间通信

    写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z. 打印顺序为12A34B56C78D--5152Z.要求用线程间的通信. 代码清单: class Printer { private in ...

  5. Python-sendgrid邮箱库的使用

    Python中sendgrid库使用 #帮助文档https://github.com/sendgrid/sendgrid-python https://sendgrid.com/docs/ui/acc ...

  6. c语言链表从本地文件中读取和写入数据

    1 typedef struct Data{ 2 40 char *name; 3 41 char *IDCARD; 4 42 char *job_id; 5 43 char *length; 6 4 ...

  7. Java字符串==和equals的区别

    首先我们来了解一下String类,Java的字符串是一旦被赋值之后无法更改的(这里的无法更改是指不能将字符串中单个或一段字符重新赋值),这也是Java虚拟机为了减少内存开销,避免字符串的重复创建设立的 ...

  8. Web安全(更新中)

    sql注入 创建一个数据库 create database admin1; 查询数据库 查看所有数据库 show databases; 使用该数据库    use admin1; 创建一个表 创建一个 ...

  9. 学习C#第二天

    变量 变量是什么? 在数学中,我们对变量的概念有一定的了解和认识,如y=x^2,其中,x,y都是变量. 定义 一个变量就是存储区(内存)中的一个存储单元 变量的声明及初始化 使用变量的步骤 声明一个变 ...

  10. 如何在CMDB中落地应用的概念?

    如何在CMDB中落地应用的概念? 我们前面讲了应用是整个微服务架构体系下运维的核心,而CMDB又是整个运维平台的基石.今天我就讲讲在CMDB中如何落地应用这个核心概念,以及如何建立应用集群分组的思路. ...