11.JDK8内存模型
11.1.本地方法栈(Native Method Stacks)
11.2.虚拟机栈(Java Virtual Machine Stacks)
11.3.栈帧结构
11.3.1.局部变量表
11.3.2.操作数栈
11.3.3.方法出口
11.4.虚拟机栈与本地方法栈的关系
11.5.寄存器(The pc Register)
11.6.方法区(Method Area)
11.7.堆(Heap)
11.8.jvm中的常量池
11.9.Metaspace(元空间)
11.10.堆内存划分

11.JDK8内存模型

本文转自:https://www.jianshu.com/p/44df41ebdbf6

通常谈到JVM的内存模型,一般人会想到堆和栈等,那么堆和栈如何理解呢?
是运行时的单位
是存储的单位
通俗来说解决的是程序如何和运行,数据如何处理的问题;而堆解决的是数据如何存储,存储在哪儿的问题。

如上图所示,java虚拟机内存模型主要分为以上五个部分,这里以jdk8为学习对象。

11.1.本地方法栈(Native Method Stacks)

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的。其区别在于虚拟机栈为虚拟机执行Java方法所服务,而本地方法栈则是为虚拟机使用到的native方法所服务。

本地方法栈也是一个私有(线程私有)的内存区域,也是后进先出。

虚拟机可以自由实现它,有的虚拟机(如HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。

本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常

11.2.虚拟机栈(Java Virtual Machine Stacks)

每个Java线程都有一个私有Java虚拟机栈,与该线程同时创建。

在虚拟机栈内,每个方法会生成一个栈帧。每个栈帧代表一次次的方法调用,一个方法的执行到执行完成的过程,代表栈帧从入栈到出栈的过程。

虚拟机栈会抛出StackOverflowError和OutOfMemoryError。

11.3.栈帧结构

下图表示了栈帧的组成结构:

11.3.1.局部变量表

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量

11.3.2.操作数栈

操作数栈是一个后入先出的栈。

一个方法刚开始执行时操作数栈是空的,方法执行过程中会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈 / 入栈操作。

例如执行iadd指令时,就会将最接近栈顶的两个int元素取出并相加,然后将相加的结果再入栈。

操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,在编译程序代码的时候,编译器要严格保证这一点。比如刚才的iadd指令,它取出的元素必须是int的,不能出现诸如long和float类型的变量。

虽然概念模型中不同栈帧之间是完全相互独立的,但大多虚拟机实现中会有一些优化处理:令两个栈帧出现一部分重叠,让下面的栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠在一起重叠在一起,这样在进行方法调用时就可以公用一部分数据,无须进行额外的参数复制传递,如图所示:

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。

静态解析:我们知道Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数,这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接引用,这种转化称为静态解析。

动态链接:除去静态解析的另外一部分将在每一次运行期间转化为直接引用,这部分称为动态链接。

所以要执行某个方法时,某个指令(例如invokevirtual)将常量池中的引用作为参数,而根据这个引用就可以找到真正的栈帧。

关于方法的解析与调用,参考:https://blog.csdn.net/reachwang/article/details/103058653

11.3.3.方法出口

方法出口也可以通俗的理解为方法返回方式:在jvm中,方法返回方式有两种:正常和异常

正常出口:当程序执行遇到方法返回的字节码指令,就完成此次方法执行,并根据调用方指定的返回值返回(可以无返回值)。

异常出口:方法在执行中遇到了异常,并且在方法体内没有得到处理,会导致方法退出,这时候不会有任何返回值给调用方。

程序正常退出时,相当于把当前栈帧出栈,调用pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。

程序异常退出时:当程序发生异常时,返回地址需要通过异常表来确定,在栈帧中没有保存异常表。

11.4.虚拟机栈与本地方法栈的关系

为了更好地理解虚拟机栈和本地方法栈的结构模型以及关系,我们以网上的例子简单描述下,如下图:

11.5.寄存器(The pc Register)

Java虚拟机可以支持多个线程同时执行,每个Java线程都有其自己的 pc(程序计数器)寄存器。在任何时候,每个Java虚拟机线程都在执行单个方法的代码,即该线程的当前方法。(如果不是native,则该pc寄存器包含当前正在执行的Java虚拟机指令的地址。如果线程当前正在执行的方法是native,则Java虚拟机的pc寄存器值未定义。

pc寄存器中的值就是当前指令所在的内存地址,即returnAddress类型的数据,当线程执行native方法时,pc中的值为undefined。

11.6.方法区(Method Area)

Java虚拟机具有一个在所有Java虚拟机线程之间共享的方法区域。该方法区域类似于常规语言的编译代码的存储区域,或者类似于操作系统过程中的“文本”段。它存储每个类的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化以及接口初始化的特殊方法。

方法区是在虚拟机启动时创建的。尽管方法区在逻辑上是堆的一部分,但是可以选择不进行垃圾回收或压缩。该规范没有规定方法区域的位置或用于管理已编译代码的策略。方法区域可以是固定大小的,或者可以根据计算的需要进行扩展,如果不需要更大的方法区域,则可以缩小。方法区域的内存不必是连续的。

可能抛出OutOfMemoryError异常。

11.7.五、堆(Heap)

Java虚拟机具有一个在所有Java虚拟机线程之间共享的堆。堆是运行时数据区,从中分配所有类实例和数组的内存。

堆是在虚拟机启动时创建的。对象的堆存储由GC(垃圾收集器)回收;对象永远不会显式释放。Java虚拟机可以根据实现者的系统要求选择GC。堆的大小可以是固定的,也可以根据计算要求进行扩展,如果不需要更大的堆,则可以将其收缩。堆的内存不必是连续的。

可能抛出OutOfMemoryError异常。

在jdk1.8之前的版本对内存空间是不同的,主要区别在于:1.8中删除了永久代,新增了元空间。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过参数来指定元空间的大小。

11.8.jvm中的常量池

参考文章:https://blog.csdn.net/weixin_40999907/article/details/87907083
方法区:运行时常量池
Class文件:常量池
堆:String常量池

11.9.Metaspace(元空间)

本部分转自:https://blog.csdn.net/universe_ant/article/details/58585854

其实,移除永久代的工作从JDK 1.7就开始了。JDK 1.7中,存储在永久代的部分数据就已经转移到Java Heap或者Native Heap。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了Java heap;类的静态变量(class statics)转移到了Java heap。我们可以通过一段程序来比较JDK 1.6、JDK 1.7与JDK 1.8的区别,以字符串常量为例:

package com.paddx.test.memory;import java.util.ArrayList;
import java.util.List;public class StringOomMock {static String base = "string";public static void main(String[] args) {List<String> list = new ArrayList<String>();for(int i = 0; i < Integer.MAX_VALUE; i++) {String str = base + base;base = str;list.add(str.intern());}}}

JDK1.6的运行结果:

JDK1.7的运行结果:

JDK 1.8的运行结果:

从上述结果可以看出,JDK 1.6下,会出现“PermGen space”的内存溢出,而在JDK 1.7和JDK 1.8中,会出现堆内存溢出,并且JDK 1.8中参数PermSize和MaxPermSize已经失效。因此,可以大致验证JDK 1.7和JDK 1.8中将字符串常量由永久代转移到堆中,并且JDK 1.8中已经不存在永久代的结论。现在我们来看一看元空间到底是一个什么东西?

JDK1.8对JVM架构的改造将类元数据放到本地内存中,另外,将常量池和静态变量放到Java堆里。HotSpot VM将会为类的元数据明确分配和释放本地内存。在这种架构下,类元信息就突破了原来-XX:MaxPermSize的限制,现在可以使用更多的本地内存。这样就从一定程度上解决了原来在运行时生成大量类造成经常Full GC问题,如运行时使用反射、代理等。所以升级以后Java堆空间可能会增加。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间的最大区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数指定元空间的大小:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对改值进行调整:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对改值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

除了上面的两个指定大小的选项外,还有两个与GC相关的属性:

-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间导致的垃圾收集器。-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集。

现在我们在JDK 1.8重新运行一下上面第二部分(PermGen(永久代))的代码,不过这次不再指定PermSize和MaxPermSize。而是制定MetaspaceSize和MaxMetaspaceSize的大小。输出结果如下:

11.10.堆内存划分

以下转自:https://www.cnblogs.com/cjsblog/p/9850300.html

在JDK1.7以及其前期的JDK版本中,堆内存通常被分为三块区域:Young Generation、Old Generation、Permanent Generation for VM Matedata。

在JDK1.8中把存放元数据中的永久内存从堆内存中移到了本地内存中,JDK1.8中JVM堆内存结构就变成了如下:

推统计信息

11.JDK8内存模型、本地方法栈、虚拟机栈、栈帧结构(局部变量表、操作数栈、方法出口、虚拟机栈与本地方法栈的关系、寄存器、方法区、堆(Heap)、jvm中的常量池、Metaspace(元空间))相关推荐

  1. class字节码文件中的常量池结构详解

    文章目录 前言 方法区 常量池基本结构 JVM 所定义的11种常量 常量池元素的复合结构 常量池的结束位置 常量池元素总数量 第一个常量池元素 父类常量 变量型常量池元素 自己的学习笔记,部分节选自& ...

  2. JVM学习笔记之-运行时数据区概述及线程概述,程序计数器(PC寄存器),虚拟机栈(栈,局部变量表,操作数栈,动态连接,方法调用,方法返回地址等),本地方法接口,本地方法栈

    运行时数据区概述及线程概述 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JV ...

  3. 好好说说Java中的常量池之Class常量池

    前言 在Java中,常量池的概念想必很多人都听说过.这也是面试中比较常考的题目之一.在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的理解,几道简单的Strin ...

  4. 【Java 虚拟机原理】栈帧 | 局部变量表 | 操作数栈 | 方法出口 | JVM 指令逐条解析

    文章目录 前言 一.JVM 指令逐条解析 1.Java 代码 2.Java 虚拟机指令 3.分析 JVM 指令 4.局部变量表 与 操作数栈 二.方法出口 前言 " 栈帧 " 中存 ...

  5. 好好说说Java中的常量池之Class常量池 1

    转载自   好好说说Java中的常量池之Class常量池 在Java中,常量池的概念想必很多人都听说过.这也是面试中比较常考的题目之一.在Java有关的面试题中,一般习惯通过String的有关问题来考 ...

  6. String常量池 享元模式-设计模式

    String常量池 享元模式 本文基于java8 内存结构图 参考: Java三大特殊类之----String类 Java设计模式之享元模式 String String 类是由 final 修饰的类 ...

  7. stringbuilder调用tostring常量池_彻底弄懂java中的常量池

    作者:tracy_666链接:https://www.jianshu.com/p/55f65dac1b4b JVM常量池主要分为Class文件常量池.运行时常量池,全局字符串常量池,以及基本类型包装类 ...

  8. Java中整数常量池的概念

    Java中整数常量池的概念: java中为了提高程序的执行效率,将[-128, 127]之间256个整数所有的包装对象提前创建好了,类加载时就已经创好了,放在了一个方法区的"整数常量池&qu ...

  9. Java #JVM(HotSpot) 运行时数据区 #程序计数器(PC寄存器)#虚拟机栈(栈帧:局部变量表、操作数栈……)#堆……

    目录 JVM中线程的说明 程序计数器(PC寄存器) 虚拟机栈 · 栈帧 ·· 局部变量表 ·· 操作数栈 ·· 动态链接 ·· 方法返回地址 ·· 本地方法栈 堆 · 查看堆的大小 · 堆的默认大小 ...

最新文章

  1. 机器学习中目标函数、损失函数、代价函数之间的区别和联系
  2. Kotlin学习入门笔记
  3. oppo手机工程模式清除数据需要密码_「MIUI玩机技巧27」如何快速查询和清除电池信息...
  4. Revit API创建标高,单位转换
  5. [Python] MySQLdb(即 MySQL-python 包)在 OS X 中安装指南
  6. python什么元素为真_如何使用Python基础内置函数
  7. 3.6 SQL Server 内存
  8. java B2B2C Springboot电子商城系统-eureka详解
  9. oki5530sc打印错误_四通OKI5530常见故障分析
  10. ei加声调怎么加_微商怎么加好友找客源实操篇
  11. 兔子进洞算法_下兔子洞:一个varnishreload错误的故事-第1部分
  12. 常用测试用例设计方法4-场景法
  13. mac 修改hosts不生效问题
  14. 全网详解如何设计数据库的ER图,即实体关系图
  15. 发表科技类论文的期刊《科技新时代》杂志简介及投稿须知
  16. input输入框type参数
  17. C++中的dynamic_cast和static_cast转化
  18. 皇帝踏入31 克城冠军梦 时不我待
  19. 阿里云课堂:云安全的架构设计与实践之旅
  20. 关于CSS3:justify-self,justify-items和justify-content之间的区别

热门文章

  1. 触发器是不是不能喝外键同时存在_数字电路--RS触发器
  2. 我的第一个微信好友分析
  3. 2.Redis数据库(搭建redis主从的必要性)以及主从搭建(Windows为例)
  4. 1.Android(了解Android王国)
  5. opencv进阶学习笔记14:分水岭算法 实现图像分割
  6. 如何用Seaborn描绘柱状图(条形图),箱线图,小提琴图,分类散点图,分面网格分类图,散点图(3)
  7. VTK:行军案用法实战
  8. VTK:模型用法实战
  9. boost::assertion_failed_msg相关的测试程序
  10. boost::mp11::mp_fill相关用法的测试程序