Java 内存模型(JMM)

news2024/11/15 9:50:59

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

1. 概述

        对于 Java 程序员来说,在虚拟机⾃动内存管理机制下,不再需要像 C/C++ 程序开发程序员这 样为每⼀个 new 操作去写对应的 delete / free 操作,不容易出现内存泄漏和内存溢出问 题。正是因为 Java 程序把内存控制权利交给 JVM 虚拟机。⼀旦出现内存泄漏和溢出⽅⾯的问题, 如果不了解虚拟机是怎样使⽤内存的,那么排查错误将会是⼀个⾮常艰巨的任务。

 2. 运行时数据区域划分

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

 

 

3. 程序计数器(Program Counter Register)

程序计数器是⼀块较⼩的内存空间,是当前线程所执⾏的字节码的⾏号指示器。

        字节码解释器在解释执⾏字节码⽂件⼯作时,每当需要执⾏⼀条字节码指令时,就通过改变程序 计数器的值来完成。程序中的分⽀、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器 来完成。

        程序执⾏过程中,会不断的切换当前执⾏线程,切换后,为了能让当前线程恢复到正确的执⾏位 置,每条线程都需要有⼀个独⽴的程序计数器,并且各线程之间计数器互不影响,独⽴存储。

程序计数器主要有两个作⽤:

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

 4. Java 虚拟机栈(VM Stack)

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

        JMM 内存区域可以粗略的区分为堆内存( Heap )和栈内存 ( Stack )。其中栈就是 VM St ack 虚拟机栈,或者说是虚拟机栈中局部变量表部分。 因为局部变量表主要存放了编译期可知的各 种基本数据类型变量值(boolean、byte、char、short、int、float、long、double)、对象引⽤ (reference 类型,它不同于对象本身,可能是⼀个指向对象起始地址的引⽤指针,也可能是指向⼀ 个代表对象的句柄或其他与此对象相关的位置)。

        Java 虚拟机栈是由⼀个个栈帧组成,⽽每个栈帧中都拥有:局部变量表、操作数栈、动态链 接、⽅法出⼝信息。每⼀次⽅法调⽤都会有⼀个对应的栈帧被压⼊ VM Stack 虚拟机栈,每⼀个 ⽅法调⽤结束后,代表该⽅法的栈帧会从 VM Stack 虚拟机栈中弹出。

         在活动线程中, 只有位于栈顶的帧才是有效的, 称为当前活动栈帧,代表正在执⾏的当前⽅ 法。在 JVM 执⾏引擎运⾏时, 所有指令都只能针对当前栈帧进⾏操作。虚拟机栈通过 pop 和 p ush 的⽅式,对每个⽅法对应的活动栈帧进⾏运算处理,⽅法正常执⾏结束,肯定会跳转到另⼀个 栈帧上。

 Java ⽅法有两种返回⽅式: return 语句或抛出异常,不管哪种返回⽅式都会导致栈帧被弹出。

 Java 虚拟机栈会出现两种错误: StackOverFlowError 和 OutOfMemoryError 。

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

5. 本地方法栈(Native Method Stack)

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

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

6. 堆(Heap)

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

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

6.1. 新⽣代、⽼年代

        Heap 堆是垃圾收集器 GC ( Garbage Collected )管理的主要区域,因此堆区也被称 作 GC 堆( Garbage Collected Heap )。从垃圾回收的⻆度,由于现在收集器基本都采⽤分 代垃圾收集算法,所以 JVM 中的堆区往往进⾏分代划分,例如:新⽣代 和 ⽼年代。⽬的是更好地 回收内存,或者更快地分配内存。

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

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

6.2. 创建对象的内存分配

        创建⼀个新对象,在堆中的分配内存。

        ⼤部分情况下,对象会在 Eden 区⽣成,当 Eden 区装填满的时候,会触发 Young Gar bage Collection ,即 YGC 垃圾回收的时候,在 Eden 区实现清除策略,没有被引⽤的对象 则直接回收。

        依然存活的对象会被移送到 Survivor 区。 Survivor 区分为 s0 和 s1 两块内存区 域。每次 YGC 的时候,它们将存活的对象复制到未使⽤的 Survivor 空间( s0 或 s1 ), 然后将当前正在使⽤的空间完全清除,交换两块空间的使⽤状态。每次交换时,对象的年龄会 加 +1 。

        如果 YGC 要移送的对象⼤于 Survivor 区容量的上限,则直接移交给⽼年代。⼀个对象 也不可能永远呆在新⽣代,在 JVM 中 ⼀个对象从新⽣代晋升到⽼年代的阈值默认值是 15 ,可 以在 Survivor 区交换 14 次之后,晋升⾄⽼年代。

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

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

7. 元空间(Meta Space)

        ⽤于存放类信息、常量、静态变量、JIT即时编译器编译后的机器代码等数据等。例如: java.l ang.Object 类的元信息、 Integer.MAX_VALUE 常量等。

JDK1.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:PermGenspace 的 Error 。
  3. 永久代的GC是和⽼年代( old generation )捆绑在⼀起的,⽆论谁满了,都会触发永久代和 ⽼年代的垃圾收集。
  4. 可以通过 -XX:PermSize=N 设置 ⽅法区 (永久代) 初始空间, -XX:MaxPermSize=N 设 置⽅法区 (永久代) 最⼤空间,超过这个值将会抛出错误: java.lang.OutOfMemoryError: PermGen

JDK1.7:

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

JDK1.8:正式移除永久代,采⽤ Meta Space 元空间代替

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

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

 8. 字符串常量池

 String 的两种创建⽅式:

  •  第⼀种⽅式是在常量池中获取字符串对象;
  •  第⼆种⽅式是直接在堆内存空间创建⼀个新的字符串对象;

 String 的 intern() ⽅法:

 String 的拼接:

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

创建 1 或 2 个字符串。如果常量池中已存在字符串常量“ abc ” ,则只会在堆空间创建⼀个字符串常 量“ abc ” 。如果常量池中没有字符串常量“ abc ” ,那么它将⾸先在池中创建,然后在堆空间中创建,因 此将创建总共 2 个字符串对象。

8 种基本类型的包装类和常量池

  • Java 基本类型的包装类的⼤部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;
  • 前⾯ 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据;
  • Character 创建了数值在[0,127]范围的缓存数据;
  • Boolean 直接返回 True Or False;
  • 如果超出对应范围仍然会去创建新的对象

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

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

相关文章

使用Locust进行接口性能测试:关联与参数化技巧分析

Locust是一款开源的Python性能测试工具,它可以模拟大量并发用户对网站或者其他接口进行压力测试 1. 关联:精确模拟用户操作 在某些场景下,我们需要将之前请求的响应参数关联到后续请求中,以模拟用户操作。这在会话管理&#xff…

2024年中国电子学会青少年软件编程(Python)等级考试(一级)核心考点速查卡

考前练习: 2024年06月中国电子学会青少年软件编程(Python)等级考试试卷(一级)答案 解析-CSDN博客 2024年03月中国电子学会青少年软件编程(Python)等级考试试卷(一级)答…

MySQL 中的 UTF-8 与 UTF8MB4:差异解析

在 MySQL 数据库中,字符集的选择对于数据的存储和处理至关重要。其中,UTF-8 和 UTF8MB4 是两个常见的字符集选项。那么,它们之间到底有什么区别呢? 一、字符集简介 UTF-8 UTF-8(8-bit Unicode Transformation Format&…

文献阅读(81)FACT

题目:FACT: FFN-Attention Co-optimized Transformer Architecture with Eager Correlation Prediction时间:2023会议:ISCA研究机构:清华 主要贡献 算法优化:提出了Eager Prediction (EP),预测softmax前重…

线性系统分析

一、定义 (1)叠加性 若 且 则称该系统具有叠加性。 叠加性:系统的一个输入不影响系统对其他输入的响应。 (2)均匀性 若 对任意常数a下式都成立 则称该系统具有均匀性。 均匀性:系统能够保持对输入信号的缩放因子不变。 (3)线性系统 若一个系统同时具有叠加性和…

Web开发:基础Web开发的支持

1. 构建项目&#xff1a; 2.添加依赖 <!-- SpringBoot热部署支持(开发阶段使用)--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></d…

头部姿态估计代码+教程

前言 头部姿态估计是计算机视觉中的一个具有挑战性的问题&#xff0c;因为它需要完成多个步骤。首先&#xff0c;我们需要在画面中定位人脸&#xff0c;然后识别出各种面部特征点。如今&#xff0c;当人脸正对摄像头时&#xff0c;识别人脸似乎是一个简单的任务。但问题在于&am…

个人为什么不能参与场外期权?也有办法可以开始交易!

今天带你了解个人为什么不能参与场外期权&#xff1f;也有办法可以开始交易&#xff01;由于监管政策、风险管理、市场透明度和适合性等因素&#xff0c;个人投资者在中国市场上不能参与场外期权交易。 个人为什么不能参与场外期权&#xff1f; 在中国市场上&#xff0c;个人…

Web+Mysql——MyBatis

MyBatis 目标 能够完成Mybatis代理方式查询数据能够理解Mybatis核心配置文件的配置 1&#xff0c;Mybatis 1.1 Mybatis概述 1.1.1 Mybatis概念 MyBatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由…

Python 从入门到实战23(属性property)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们讨论了类的定义、使用方法的相关知识。今天我们将学…

【HTTP】请求“报头”(Host、Content-Length/Content-Type、User-Agent(简称 UA))

Host 表示服务器主机的地址和端口号 URL 里面不是已经有 Host 了吗&#xff0c;为什么还要写一次&#xff1f; 这里的 Host 和 URL 中的 IP 地址、端口什么的&#xff0c;绝大部分情况下是一样的&#xff0c;少数情况下可能不同当前我们经过某个代理进行转发。过程中&#xf…

【JAVA开源】基于Vue和SpringBoot的蜗牛兼职平台

本文项目编号 T 034 &#xff0c;文末自助获取源码 \color{red}{T034&#xff0c;文末自助获取源码} T034&#xff0c;文末自助获取源码 目录 一、系统介绍1.1 平台架构1.2 管理后台1.3 用户网页端1.4 技术特点 二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景…

LinuxC高级作业2

1.整理思维导图 2.做一套笔试题 一&#xff1a; 1.cd .. mkdir dir1 cd dir1 touch file1 2.cp ~/mnt/dir1/ -r * ~/home/dir2/ 3.pwd 4.ls -l 5.ifconfig 6.top 10.find /usr -type f -name "*name*" 11.:wq 13.df -h 14.tar -xzvf tmp.tar.gz 15.sudo c…

我的数据库旅程:从迷茫到觉醒

我的数据库旅程&#xff1a;从迷茫到觉醒 《中国数据库前世今生》纪录片的上线&#xff0c;使我回顾了中国数据库技术的演进历程&#xff0c;也联想到了自己在这一领域的工作经历。数据库技术从80年代在中国的初步应用到如今蓬勃发展&#xff0c;贯穿了整个信息化进程。作为一名…

【Qt之·文件操作·类QTextStream、QDataStream】

系列文章目录 文章目录 前言一、概述1.1 QTextStream类1.2 QTextStream类的作用和用途 二、基本用法2.1 QTextStream成员函数2.2 QTextStream格式描述符、描述符方法2.3 QDataStream成员函数2.4 创建QTextStream对象并关联输入/输出设备&#xff08;如文件、标准输入/输出流等&…

EPSILON环境配置和本地测试

文章目录 一、环境配置1.1 拉取镜像构建容器1.2 在容器中安装常用的包1.3 安装依赖1.4 安装OOQP1.4.1 安装blas1.4.2 安装ma271.4.3 安装OOQP 1.5 安装Protobuf 二、本地编译测试2.1 拉取源码并编译2.2 X11转发docker图形化界面2.3 测试一个小例子 三、镜像 一、环境配置 宿主…

Vue3:$attrs实现组件通信

目录 一.性质 1.响应式 2.包含所有非prop属性 3.动态属性 二.作用 1.访问非prop属性 2.灵活性 3.组件重用 三.使用 1.爷爷组件 2.父亲组件 3.儿子组件 四.代码 1.爷爷组件代码 2.父亲组件代码 3.孙子组件代码 五.效果图 在Vue 3中&#xff0c;$attrs 是一个响…

string类的模拟实现以及oj题

前言 上篇博客实现了string类的begin()、end()、构造函数、析构函数、c_str、size()、clear()、capacity()、[ ]、reserve()、push_back、append()、insert()、。这篇博客实现剩下的一些重要功能。 string类的模拟实现 string.h #include<iostream> #include<stri…

稳了,搭建Docker国内源图文教程

大家好&#xff0c;之前分享了我的开源作品 Cloudflare Workers Proxy&#xff0c;它的作用是代理被屏蔽的地址&#xff0c;理论上支持代理任何被屏蔽的域名&#xff0c;使用方式也很简单&#xff0c;只需要设置环境变量 PROXY_HOSTNAME 为被屏蔽的域名&#xff0c;最后通过你的…

强化信息安全:密码机密钥管理的策略与实践

强化信息安全&#xff1a;密码机密钥管理的策略与实践 随着信息技术的飞速发展&#xff0c;信息安全已成为企业和社会关注的焦点。密码机作为加密通信和数据保护的关键设备&#xff0c;其密钥管理直接关系到整个信息系统的安全性。本文旨在探讨密码机密钥管理的策略与实践&…