乐趣区

关于jvm:JVM内存模型和结构详解五大模型图解

JVM 内存模型和 Java 内存模型都是面试的热点问题,名字看感觉都差不多,实际上他们之间差异还是挺大的。

艰深点说,JVM 内存构造是与 JVM 的外部存储构造相干,而 Java 内存模型是与多线程编程相干 @mikechen

目录

  • 什么是 JVM 
  • 为什么须要 JVM?
  •  JVM 内存模型 堆(Heap)
  •  办法区(Method Area)
  • 虚拟机栈 (JVM Stack) 
  • 本地办法栈 (Native Stack) 
  • 程序计数器(PC Register)
  • JVM 内存模型小结

什么是 JVM

JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,JVM 是一个虚构进去的计算机,有着本人欠缺的硬件架构,如处理器、堆栈等。

为什么须要 JVM?

Java 语言应用 Java 虚拟机屏蔽了与具体平台相干的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行的指标代码(字节码),就能够在多种平台上不加批改地运行。

Java 文件必须先通过一个叫 javac 的编译器,将代码编译成 class 文件,而后通过 JVM 把 class 文件解释成各个平台能够辨认的机器码,最终实现跨平台运行代码。

JVM 内存模型

JVM 内存模型能够分为两个局部,如下图所示,堆和办法区是所有线程共有的,而虚拟机栈,本地办法栈和程序计数器则是线程公有的。

在 JVM1.8 中,图中的 办法区为元数据区,上面开展谈一谈这五个区域的作用。

堆(Heap)

在 Java 中,堆被划分成两个不同的区域:新生代 (Young)、老年代 (Old),新生代 (Young) 又被划分为三个区域:Eden、From Survivor、To Survivor。

下图中的 Perm 代表的是永恒代,然而留神永恒代并不属于堆内存中的一部分,同时 jdk1.8 之后永恒代也将被移除。

堆是 java 虚拟机所治理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,该内存区域寄存了对象实例及数组(但不是所有的对象实例都在堆中)。

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

当空余堆内存大于 70% 时,JVM 会减小堆内存的大小到 -Xms 指定的大小,可通过 XX:MaxHeapFreeRation= 来指定这个比列,当然为了防止在运行时频繁调整 Heap 的大小,通常 -Xms 与 -Xmx 的值设成一样。堆内存 = 新生代 + 老生代 + 长久代。

在咱们垃圾回收的时候,咱们往往将堆内存分成新生代和老生代(大小比例 1:2),新生代中由 Eden 和 Survivor0,Survivor1 组成,三者的比例是 8:1:1,新生代的回收机制采纳复制算法,在 Minor GC 的时候,咱们都留一个存活区用来寄存存活的对象,真正进行的区域是 Eden+ 其中一个存活区,当咱们的对象时长超过肯定年龄时(默认 15,能够通过参数设置),将会把对象放入老生代,当然大的对象会间接进入老生代,老生代采纳的回收算法是标记整顿算法。

办法区(Method Area)

其实办法区是在 JDK1.8 以前的版本里存在的一块内存区域,次要就是寄存从 class 文件里加载进来的类的,而且常量池也是在这块区域内的。

然而在 JDK1.8 之后,这块区域摇身一变,换了名字,叫做“Metaspace”,翻译过去就是“元数据空间”的意思,当然它只是改了个名,实现的性能是没变的。

办法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、动态变量、即时编译器编译后的代码缓存等数据。

1. 类型信息

对每个加载的类型(类 class、接口 interface、枚举 enum、注解 annotation),JVM 必须在办法区中存储以下类型信息:\
①这个类型的残缺无效名称(全名 = 包名. 类名)\
②这个类型间接父类的残缺无效名(对于 interface 或是 java.lang.0bject,都没有父类)\
③这个类型的修饰符(public, abstract,final 的某个子集)\
④这个类型间接接口的一个有序列表

2. 域信息 (Field) 成员变量

JVM 必须在办法区中保留类型的所有域的相干信息以及域的申明程序。\
域的相干信息包含: 域名称、域类型、域修饰符(public, private,protected,static,final, volatile, transient 的某个子集)

3. 办法 (Method) 信息

JVM 必须保留所有办法的以下信息,同域信息一样包含申明程序:

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

虚拟机栈(JVM Stack)

虚拟机栈(Java Virtual Machine Stack),晚期也叫 Java 栈,每个线程在创立时都会创立一个虚拟机栈,其外部保留一个个的栈帧(Stack Frame),对应着一次次的 Java 办法调用。

虚拟机栈的作用:主管 Java 程序的运行,它保留办法的局部变量、局部后果,并参加办法的调用和返回。

每个办法被执行的时候都会创立一个”栈帧”, 用于存储局部变量表(包含参数)、操作栈、办法进口等信息。

每个办法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

栈帧(Stack Frame)是用于虚拟机执行时办法调用和办法执行时的数据结构,它是虚构栈的根本元素,栈帧由局部变量区、操作数栈等组成,如下图所示:

每一个办法从调用到办法返回都对应着一个栈帧入栈出栈的过程。最顶部的栈帧称为以后栈帧,栈帧所关联的办法称为以后办法,定义这个办法的类称为以后类,该线程中,虚拟机有且也只会对以后栈帧进行操作。

栈帧的作用有存储数据,局部过程后果,解决动静链接,办法返回值和异样分派。

每一个栈帧蕴含的内容有局部变量表、操作数栈、动静链接、办法返回地址和一些额定的附加信息。在编译代码时,栈帧须要多大的局部变量表,多深的操作数栈都能够齐全确定的,并写入到办法表的 code 属性中。

本地办法栈(Native Stack)

本地办法栈(Native Method Stacks)与虚拟机栈所施展的作用是十分类似的,其区别不过是虚拟机栈为虚拟机执行 Java 办法(也就是字节码)服务,而本地办法栈则是为虚拟机应用到的 Native 办法服务。

虚拟机标准中对本地办法栈中的办法应用的语言、应用形式与数据结构并没有强制规定,因而具体的虚拟机能够自在实现它。

甚至有的虚拟机(譬如 Sun HotSpot 虚拟机)间接就把本地办法栈和虚拟机栈合二为一。

与虚拟机栈一样,本地办法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异样。

程序计数器(PC Register)

在 JVM 的概念模型里,字节码解释器工作时就是通过扭转这个计数器的值来选取下一条须要执行的字节码指令。

分支、循环、跳转、异样解决、线程复原等根底性能都须要依赖这个计数器来实现。

JVM 的多线程是通过线程轮流切换并调配处理器执行工夫的形式来实现的,为了各条线程之间的切换后计数器能复原到正确的执行地位,所以每条线程都会有一个独立的程序计数器。

当线程正在执行一个 Java 办法,程序计数器记录的是正在执行的 JVM 字节码指令的地址;如果正在执行的是一个 Natvie(本地办法),那么这个计数器的值则为空(Underfined)。

程序计数器占用的内存空间很少,也是惟一一个在 JVM 标准中没有规定任何 OutOfMemoryError(内存不足谬误)的区域。

JVM 内存模型小结

本篇介绍了 JVM 虚拟机中运行时数据区的五个内存区域:堆、办法区、虚拟机栈、本地办法栈、程序计数器。

这些中央也是咱们平时开发中最常接触到的中央,所以对其有所把握理解还是很有必要的,也有助于 JVM 问题排查。

作者简介

mikechen,10 年 + 大厂架构教训,《BAT 架构技术 500 期》系列文章作者,曾就任于阿里、淘宝、百度等一线互联网大厂。

浏览 mikechen 的互联网架构更多技术文章合集

Java 并发 |JVM|MySQL|Spring|Redis| 分布式 | 高并发 | 架构师

关注「mikechen 的互联网架构」公众号,回复【架构】支付我原创的《300 期 + BAT 架构技术系列与 1000 + 大厂面试题答案》

退出移动版