JVM 虚拟机 ----> Java 内存模型(JMM)

news2024/11/25 22:33:43

文章目录

    • Java 内存模型(JMM)
      • 一、运行时数据区域划分
      • 二、程序计数器(Program Counter Register)
        • 计数器的作用
      • 三、Java 虚拟机栈(VM Stack)
      • 四、本地方法栈(Native Method Stack)
      • 五、堆(Heap)
        • 1、概述
        • 2、新生代、老年代
        • 3、创建对象的内存分配
      • 六、元空间(Meta Space)
        • 1、作用
        • 2、发展历程
          • (1)JDK 1.6
          • (2)JDK 1.7
          • (3)JDK 1.8
      • 七、字符串常量池
        • 1、字符串的两种创建方式
        • 2、intern() 方法
        • 3、String 的拼接

Java 内存模型(JMM)

JMM ,全称 Java Memory Model ,中文释义 Java 内存模型

一、运行时数据区域划分

  • JVM 虚拟机在执行 Java 程序过程中会把它管理的内存划分成若干个不同的数据区域‘
  • JDK 1.8 之前分为:线程共享Heap堆区、Method Area方法区)、线程私有(虚拟机栈、本地方法栈、程序计数器)
  • JDK 1.8分为:线程共享Heap堆区、MetaSpace 元空间)、线程私有(虚拟机栈、本地方法栈、程序计数器)

image-20230912113150610

image-20230912113217554

二、程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,是当前线程所执行的字节码的行号指示器

  • 字节码解释器在解释执行字节码文件工作时,每当需要执行一条字节码指令时,就通过改变程序计数器的值来完成。程序中的分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。
  • 程序执行过程中,会不断的切换当前执行线程,切换后,为了能让当前线程恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,并且各线程之间计数器互不影响,独立存储。

计数器的作用

  1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理
  2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候,能够知道当前线程的运行位置
  3. 程序计数器是唯一一个不会出现 OutOfMemoryError的内存区域,它随着线程的创建而创建,随着线程的结束而死亡

三、Java 虚拟机栈(VM Stack)

与程序计数器一样,VM Stack虚拟机栈也是线程私有的,它的生命周期和线程相同,用于描述 Java 方法执行时的内存模型,每次方法调用的数据都是通过栈传递的。

JMM内存区域可以粗略的区分为堆内存(Heap)和栈内存 (Stack)。其中栈就是VM Stack虚拟机栈,或者说是虚拟机栈中局部变量表部分。

局部变量表主要存放了编译期可知的各种基本数据类型变量值(booleanbytecharshortintfloatlongdouble)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)

image-20230912141707290

Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表操作数栈动态链接方法出口信息

每一次方法调用都会有一个对应的栈帧被压入 VM Stack虚拟机栈,每一个方法调用结束后,代表该方法的栈帧会从VM Stack虚拟机栈中弹出。

image-20230912141730012

在活动线程中, 只有位于栈顶的帧才是有效的, 称为当前活动栈帧,代表正在执行的当前方法。

JVM执行引擎运行时, 所有指令都只能针对当前活动栈帧进行操作。虚拟机栈通过 poppush的方式,对每个方法对应的活动栈帧进行运算处理,方法正常执行结束,肯定会跳转到另一个栈帧上。

image-20230912141826042

  • Java 方法有两种返回方式,不管哪种返回方式都会导致当前活动栈帧被弹出

    • return 语句
    • 抛出异常

Java 虚拟机栈会出现两种错误:StackOverFlowErrorOutOfMemoryError

  • StackOverFlowError:当线程请求栈的深度超过 JVM虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。
  • OutOfMemoryErrorJVM的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError

四、本地方法栈(Native Method Stack)

本地方法栈用于虚拟机调用的 Native方法

native关键字修饰的本地方法被执行的时候,在本地方法栈中也会创建一个栈帧,用于存放该native本地方法的局部变量表、操作数栈、动态链接、方法出口信息。方法执行完毕后,相应的栈帧也会出栈并释放内存空间。也会出现 StackOverFlowErrorOutOfMemoryError两种错误

五、堆(Heap)

1、概述

Heap堆区,用于存放对象实例和数组的内存区域

Heap堆是JVM 所管理的内存中最大的一块区域,被所有线程共享的一块内存区域。堆区中存放对象实例,“几乎”所有的对象实例以及数组都在这里分配内存

Java 世界中“几乎”所有的对象都在堆中分配,但是,随着 JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。

JDK 1.7 开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存

2、新生代、老年代

Heap 堆是 **垃圾收集器 GC(Garbage Collected)**管理的主要区域,因此堆区也被称为 GC堆(Garbage Collected Heap)

从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 JVM 中的堆区往往进行分代划分,例如:新生代老年代目的是更好地回收内存,或者更快地分配内存

image-20230912143319280

Heap 堆区中的新生代、老年代的空间分配比例,可以通过java -XX:+PrintFlagsFinal -version命令查看

image-20230912143425894

上述输出结果结果分析

  • InitialSurvivorRatio = 8

新生代Young(Eden/Survivor)空间的初始比例 = 8:代表Eden占新生代空间的80%

  • uintx NewRatio = 2

老年代Old / 新生代 Young的空间比例 = 2 : 代表老年代Old是新生代Young的2倍

因为新生代是由 Eden + s0 + s1 组成的,所以按照上述默认比例,如果 Eden` 区内存大小是 40M,那么两个 Survivor 区就是 5M,整个新生代区就是 50M,然后可以算出 Old 区内存大小是 100M,堆区总大小就是 150M

3、创建对象的内存分配

  • 创建一个新对象,在堆中分配内存
  • 大部分情况下,对象会在 Eden 区生成,当 Eden 区装满时,会触发 Young Garbage Collection,即 YGC 垃圾回收时,在 Eden 区实现清除策略,没有被引用的对象直接被回收
  • 依然存活的对象会被移送到 Survivor 区
  • Survivor 区分为 s0 和 s1 两块内存区域,每次 YGC 的时候,将存活的对象复制到未使用的 Survivor 空间(s0 或 s1),然后清空正在使用的空间,交换 s0 和 s1 的使用状态,每次交换时, 对象的Age+1
  • 如果 YGC 要移送的对象大于 Survivor 区容量的上限,则直接移交给老年代
  • 一个对象也不可能永远呆在新生代,JVM 中 一个对象从新生代晋升到老年代的阈值默认值是 15,可以在 Survivor区交换 14 次之后,晋升至老年代

image-20230912145803553

堆区最容易出现的就是 OutOfMemoryError错误,这种错误的表现形式会有以下两种:

  1. OutOfMemoryError: GC Overhead Limit Exceeded JVM花太多时间执行垃圾回收,并且只能回收很少的堆空间时,就会发生此错误。
  2. OutOfMemoryError: Java heap space**:**假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发此错误。

此种情况,与配置的最大堆内存有关,且受制于物理内存大小。

六、元空间(Meta Space)

1、作用

用于存放 类信息常量静态变量JIT 即时编译器编译后的机器代码等数据

例如:java.lang.Object类的元信息、Integer.MAX_VALUE常量等

2、发展历程

(1)JDK 1.6

HotSpot JVM 使用Method Area方法区存储,也叫永久代(Permanent Generation)。

  1. 方法区和“永久代(Permanent Generation)”的区别:方法区是JVM 的规范,而永久代(Permanent Generation)是 JVM规范的一种实现,并且只有 HotSpot JVM才有永久代“Permanent Generation”,而对于其他类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有;
  2. 方法区是一片连续的堆空间,当JVM加载的类信息容量超过了最大可分配空间,虚拟机会抛出OutOfMemoryError:PermGenspaceError
  3. 永久代的GC是和老年代(old generation)捆绑在一起的,无论谁满了,都会触发永久代和老年代的垃圾收集。
  4. 可以通过 -XX:PermSize=N 设置 方法区 (永久代) 初始空间,-XX:MaxPermSize=N 设置方法区 (永久代) 最大空间,超过这个值将会抛出错误:java.lang.OutOfMemoryError: PermGen
(2)JDK 1.7

将字符串常量池、静态变量转移到了堆区。

(3)JDK 1.8

正式移除永久代,采用 Meta Space 元空间替代

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

Java 8PermGen永久代为什么被移出 HotSpot JVM

  • 由于 PermGen 内存经常会溢出,容易抛出 java.lang.OutOfMemoryError: PermGen错误;
  • 移除 PermGen 可以促进 HotSpot JVMJRockit VM 的融合,因为 JRockit 没有永久代

**示例1:**不断的生成新的字符串,快速的消耗内存。通过 JDK 1.6JDK 1.7JDK 1.8 分别运行。

public class TestOOM {
  static String base = "ApeSource";
  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());
    }
  }
}

image-20230912150743991

image-20230912150753702

image-20230912150806460

上述运行结果可以看出,相同的代码,在JDK 1.6 会出现“PermGen Space”的永久代内存溢出,而在 JDK 1.7JDK 1.8 中,会出现"Java heap space"堆内存溢出,并且 JDK 1.8PermSize MaxPermGen 参数已经无效。因此,在 JDK 1.7JDK 1.8 中,已经将字符串常量由永久代转移到堆中,并且 JDK 1.8 中已经完全移除了永久代,采用元空间来代替。

**示例2:**在 JDK 8下重新运行一下运行测试代码TestOOM,指定 MetaSpaceSizeMaxMetaSpaceSize的大小,输出结果如下:

image-20230912150900689

  • -XX:MetaspaceSize**参数:主要控制Meta Space GC发生的初始阈值,也就是最小阈值,当使用的Meta Space 空间到达MetaspaceSize`**的时候,就会触发Metaspace的GC。
  • -XX:MaxMetaspaceSize参数:最大空间,默认是没有限制的。在jvm启动的时候,并不会分配MaxMetaspaceSize这么大的一块内存出来,metaspace是可以一直扩容的,直到到达MaxMetaspaceSize

image-20230912151105891

七、字符串常量池

1、字符串的两种创建方式

  • 第一种方式是在常量池中获取字符串对象;
  • 第二种方式是直接在堆内存空间创建一个新的字符串对象
// 先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"abcd"
String str1 = "apesource"; 
String str2 = new String("apesource"); //堆中创建一个新的对象
String str3 = new String("apesource"); //堆中创建一个新的对象

System.out.println(str1==str2); //false
System.out.println(str2==str3); //false

2、intern() 方法

  • 检查指定字符串在常量池中是否存在?如果存在,则返回地址,如果不存在,则在常量池中创建
String s1 = new String("Apesource");
String s2 = s1.intern(); // 查看字符串常量池中是否存在"Apesource",如果存在则返回地址,如果不存在,则在常量池中创建
String s3 = "Apesource"; // 使用常量池中的已有字符串常量"Apesource"

System.out.println(s2 == s3); // true,地址相同

3、String 的拼接

String str1 = "str";
String str2 = "ing";

String str3 = "str" + "ing"; // 常量池中的新字符串对象
String str4 = str1 + str2; // 在堆中创建的新字符串对象
String str5 = "string"; // 常量池中的已有字符串对象

System.out.println(str3 == str4); //false
System.out.println(str3 == str5); //true
System.out.println(str4 == str5); //false

字符串拼接-常量池.png

  • String s1 = new String("abc");这句代码创建了几个字符串对象?

创建 12 个字符串。如果常量池中已存在字符串常量“abc”,则只会在堆空间创建一个字符串常量“abc

如果常量池中没有字符串常量“abc”,那么它将首先在池中创建,然后在堆空间中创建,因此将创建总共2 个字符串对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1005382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

fcpx视频编辑处理 Final Cut Pro for Mac

Final Cut Pro是一款专业的视频剪辑软件&#xff0c;适用于Mac操作系统。Final Cut Pro X版本在视频剪辑方面进行了大规模的更新和改进&#xff0c;下面将介绍Final Cut Pro X中的一些主要功能和特性&#xff1a; Magnetic Timeline。这个新功能使得多条剪辑片段如同磁铁般吸合…

Databend 开源周报第 110 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 使用 BendSQL 管…

班主任须知,查询发布技巧

Hey&#xff0c;亲爱的班主任们&#xff01;今天小红书博主要跟你们分享一个超级实用的技巧&#xff0c;让你们的查询发布变得轻松又高效&#xff01;它能帮你们把查询结果变成在线查询&#xff0c;让家长们随时随地都能轻松查询各种信息&#xff0c;简直就是教师“神器”。 通…

Fiddler抓包工具(详细讲解)

序章 Fiddler是一个蛮好用的抓包工具&#xff0c;可以将网络传输发送与接受的数据包进行截获、重发、编辑、转存等操作。也可以用来检测网络安全。反正好处多多&#xff0c;举之不尽呀&#xff01;当年学习的时候也蛮费劲&#xff0c;一些蛮实用隐藏的小功能用了之后就忘记了&…

SQL11 高级操作符练习(1)

描述 题目&#xff1a;现在运营想要找到男性且GPA在3.5以上(不包括3.5)的用户进行调研&#xff0c;请你取出相关数据。 示例&#xff1a;user_profile iddevice_idgenderageuniversitygpa12138male21北京大学3.423214male复旦大学4.036543female20北京大学3.242315female23浙…

《TCP/IP网络编程》阅读笔记--标准I/O和I/O流分离

目录 1--标准I/O 1-1--标准I/O的优缺点 1-2--FILE 指针和文件描述符的转换 1-3--基于Socket的标准I/O函数使用 2--I/O流分离 2-1--文件描述符的复制和半关闭 2-2--复制文件描述符实现 I/O 流的分离 1--标准I/O 1-1--标准I/O的优缺点 标准 I/O 函数的优点&#xff1a; ①…

基于SSM的农产品仓库管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

Windows 安装 Nacos

1. 安装包下载&#xff1a; 2. 目录文件说明&#xff1a; 将下载好后的安装包进行解压&#xff1a; 3. 修改端口&#xff1a; 在Nacos的安装目录下&#xff0c;找到conf目录&#xff0c;然后打开nacos-server.properties文件&#xff0c;在配置文件中找到以下两个属性&#xff…

临沂大学图书馆藏《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书

临沂大学图书馆藏《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书

发现服务器被入侵了该怎么办?

如果服务器被入侵了&#xff0c;最好的做法应该是立即断开网络连接&#xff0c;以防止攻击者进一步损害系统。然后检查下服务器&#xff0c;看看是否有任何指示受到入侵的迹象。如果有发现了任何可疑的活动&#xff0c;应该立即联系网络管理员&#xff0c;或者联系专业的防护厂…

软件系统测试有什么注意事项?对软件产品起到什么作用?

在软件开发领域&#xff0c;软件系统测试是确保软件质量的重要环节。它旨在发现和解决软件中的缺陷和错误&#xff0c;确保软件能够按照预期功能正常运行。 一、软件系统测试的过程 1、进行测试计划的制定&#xff0c;确定测试的目标、范围和方法。 2、根据测试计划设计测试…

htaccess绕过上传实验

实验目的 利用上传htaccess文件解析漏洞绕过验证进行上传PHP脚本木马 实验工具 火狐&#xff1a;Mozilla Firefox&#xff0c;中文俗称“火狐”&#xff08;正式缩写为Fx或fx&#xff0c;非正式缩写为FF&#xff09;&#xff0c;是一个自由及开放源代码网页浏览器&#xff0…

车机多用户系统的适配问题

多用户问题出现背景 记录一下多用户的适配问题&#xff1a; 背景是system/app下面新push了两个apk&#xff0c;一个是我们的业务场景apk一个是虚拟车CarService服务的apk&#xff0c;我们的apk需要链接CarService服务通过AIDL通信。 下面这两张图是未roo的情况&#xff08;当…

什么是无人机全自动飞行系统?概念、构成、作用深度解析

无人机的工业化应用深入催生出新的痛点&#xff0c;无人机应用飞手培养难、成本高、技术参差不齐&#xff0c;以及应急响应和采集作业价值等没有得到充分释放&#xff0c;由此无人机自动飞行系统、无人机自动机场横空出世&#xff0c;因其无人化、自动化、无人机值守的应用特性…

免费的代码审查工具你知道这几个就够了?新手程序员必读

代码质量关系到一个项目的好坏&#xff0c;一直以来都是程序员和项目经理所关心的事情&#xff0c;在之前代码的检查用于人工或者静态页面&#xff0c;再后来就会用的各种工具来做因为不良的代码不仅会影响代码的可维护性&#xff0c;而且还会在某些情况下影响其性能。此外&…

MYSQL优化——B+树讲解

B-/B树看 MySQL索引结构 B-树 B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树.它类似普通的平衡二叉树&#xff0c;不同的一点是B-树允许每个节点有更多的子节点。下图是 B-树的简化图. B-树有如下特点: 所有键值分布在整颗树中&#xff1b; 任何一…

远程双屏电脑的时候有的窗口默认打开在第二块屏幕上,导致无法看到和操作【伸手党福利】

解决方法&#xff1a; 点击看不到的窗口&#xff0c;使之处于激活状态 win 左箭头 或者 win右箭头 Alt空格 按M 按 左箭头 或者 右箭头 就能看到窗口移出来了。

2023-9-12 分组背包问题

题目链接&#xff1a;分组背包问题 #include <iostream> #include <algorithm>using namespace std;const int N 110;int n, m;int v[N][N], w[N][N], s[N]; int f[N];int main() {cin >> n >> m;for(int i 1; i < n; i ){cin >> s[i];for(…

VSCODE 使用技巧

vscode批量去掉代码中空行的方法 1、在vscode中使用ctrl f组合快捷键打开替换窗口. 2、输入下面的正则表达式 ^\s*(?\r?$)\n https://mp.weixin.qq.com/s/ZKV2sZWszxBLNTNLEWhsng

PHP-学习笔记-部署wordpress

这部署wordpress 准备工作wordpress和php的版本需要进行匹配apache和php的匹配一二 php和mysql的匹配msyqlapache 正式使用注意事项 准备工作 wordpress和php的版本需要进行匹配 https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versio…