JVM 内存模型和Java结构详解 (五大模型图解)

2022-08-1023:24:25后端程序开发Comments1,458 views字数 3325阅读模式

JVM 内存模型和Java结构详解 (五大模型图解)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型和 Java 内存模型都是面试的热点问题,名字看感觉都差不多,实际上他们之间差别还是挺大的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

通俗点说,JVM 内存结构是与 JVM 的内部存储结构相关,而 Java 内存模型是与多线程编程相关 @mikechen。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

什么是 JVM

JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,JVM 是一个虚构出来的计算机,有着自己完善的硬件架构,如处理器、堆栈等。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

为什么需要 JVM?

Java 语言使用 Java 虚拟机屏蔽了与具体平台相关的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

Java 文件必须先通过一个叫 javac 的编译器,将代码编译成 class 文件,然后通过 JVM 把 class 文件解释成各个平台可以识别的机器码,最终实现跨平台运行代码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型和Java结构详解 (五大模型图解)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型

JVM 内存模型可以分为两个部分,如下图所示,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型和Java结构详解 (五大模型图解)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

在 JVM1.8 中,图中的 方法区为元数据区,下面展开谈一谈这五个区域的作用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

堆(Heap)

在 Java 中,堆被划分成两个不同的区域:新生代 (Young)、老年代 ( Old ),新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

下图中的 Perm 代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时 jdk1.8 之后永久代也将被移除。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型和Java结构详解 (五大模型图解)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

堆是 java 虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,该内存区域存放了对象实例及数组(但不是所有的对象实例都在堆中)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

其大小通过 - Xms (最小值) 和 - Xmx (最大值) 参数设置(最大最小值都要小于 1G),前者为启动时申请的最小内存,默认为操作系统物理内存的 1/64,后者为 JVM 可申请的最大内存,默认为物理内存的 1/4,默认当空余堆内存小于 40% 时,JVM 会增大堆内存到 - Xmx 指定的大小,可通过 - XX:MinHeapFreeRation = 来指定这个比列。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

当空余堆内存大于 70% 时,JVM 会减小堆内存的大小到 - Xms 指定的大小,可通过 XX:MaxHeapFreeRation = 来指定这个比列,当然为了避免在运行时频繁调整 Heap 的大小,通常 - Xms 与 - Xmx 的值设成一样。堆内存 = 新生代 + 老生代 + 持久代。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

在我们垃圾回收的时候,我们往往将堆内存分成新生代和老生代(大小比例 1:2),新生代中由 Eden 和 Survivor0,Survivor1 组成,三者的比例是 8:1:1,新生代的回收机制采用复制算法,在 Minor GC 的时候,我们都留一个存活区用来存放存活的对象,真正进行的区域是 Eden + 其中一个存活区,当我们的对象时长超过一定年龄时(默认 15,可以通过参数设置),将会把对象放入老生代,当然大的对象会直接进入老生代,老生代采用的回收算法是标记整理算法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

方法区(Method Area)

其实方法区是在 JDK1.8 以前的版本里存在的一块内存区域,主要就是存放从 class 文件里加载进来的类的,而且常量池也是在这块区域内的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

但是在 JDK1.8 之后,这块区域摇身一变,换了名字,叫做 “Metaspace”,翻译过来就是 “元数据空间” 的意思,当然它只是改了个名,实现的功能是没变的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型和Java结构详解 (五大模型图解)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

1. 类型信息文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

对每个加载的类型(类 class、接口 interface、枚举 enum、注解 annotation),JVM 必须在方法区中存储以下类型信息:
①这个类型的完整有效名称(全名 = 包名。类名)
②这个类型直接父类的完整有效名 (对于 interface 或是 java.lang.0bject,都没有父类)
③这个类型的修饰符 (public, abstract,final 的某个子集)
④这个类型直接接口的一个有序列表文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

2. 域信息 (Field) 成员变量文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。
域的相关信息包括:域名称、域类型、域修饰符 (public, private,protected,static,final, volatile, transient 的某个子集)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

3. 方法 (Method) 信息文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 必须保存所有方法的以下信息,同域信息一样包括声明顺序:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

  • 方法名称
  • 方法的返回类型 (或 void)・方法参数的数量和类型 (按顺序)
  • 方法的修饰符 (public, private,protected,static, final,synchronized,native,abstract 的一个子集)
  • 方法的字节码 (bytecodes)、操作数栈、局部变量表及大小 (abstract 和 native 方法除外)

虚拟机栈 (JVM Stack)

虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈,每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

虚拟机栈的作用:主管 Java 程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

每个方法被执行的时候都会创建一个” 栈帧”, 用于存储局部变量表 (包括参数)、操作栈、方法出口等信息。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

栈帧(Stack Frame) 是用于虚拟机执行时方法调用和方法执行时的数据结构,它是虚拟栈的基本元素,栈帧由局部变量区、操作数栈等组成,如下图所示:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型和Java结构详解 (五大模型图解)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

 文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

每一个方法从调用到方法返回都对应着一个栈帧入栈出栈的过程。最顶部的栈帧称为当前栈帧,栈帧所关联的方法称为当前方法,定义这个方法的类称为当前类,该线程中,虚拟机有且也只会对当前栈帧进行操作。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

栈帧的作用有存储数据,部分过程结果,处理动态链接,方法返回值和异常分派。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

每一个栈帧包含的内容有局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息。在编译代码时,栈帧需要多大的局部变量表,多深的操作数栈都可以完全确定的,并写入到方法表的 code 属性中。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

本地方法栈 (Native Stack)

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

甚至有的虚拟机(譬如 Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

与虚拟机栈一样,本地方法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

程序计数器(PC Register)

在 JVM 的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,为了各条线程之间的切换后计数器能恢复到正确的执行位置,所以每条线程都会有一个独立的程序计数器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

当线程正在执行一个 Java 方法,程序计数器记录的是正在执行的 JVM 字节码指令的地址;如果正在执行的是一个 Natvie(本地方法),那么这个计数器的值则为空(Underfined)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

程序计数器占用的内存空间很少,也是唯一一个在 JVM 规范中没有规定任何 OutOfMemoryError(内存不足错误)的区域。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

JVM 内存模型小结

本篇介绍了 JVM 虚拟机中运行时数据区的五个内存区域:堆、方法区、虚拟机栈、本地方法栈、程序计数器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

这些地方也是我们平时开发中最常接触到的地方,所以对其有所掌握了解还是很有必要的,也有助于 JVM 问题排查。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

以上!文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

作者简介

mikechen,10 年 + 大厂架构经验,《BAT 架构技术 500 期》系列文章作者,曾就职于阿里、淘宝、百度等一线互联网大厂。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/26922.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/26922.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定