深入理解JVM运行时数据区(内存布局 )5大部分 | 异常讨论

news2024/9/22 15:46:32

前言:

        JVM运行时数据区(内存布局)是Java程序执行时用于存储各种数据的内存区域。这些区域在JVM启动时被创建,并在JVM关闭时销毁。它们的布局和管理方式对Java程序的性能和稳定性有着重要影响。  


目录

一、由以下5大部分组成

1.Heap 堆区(线程共享)

2.程序计数器(线程私有)

什么是线程私有?

特点:不会抛出OOM

3.Java虚拟机栈(线程私有)

4.本地方法栈(线程私有)

5.元数据区(线程共享) ( Java8前叫方法区 )

6.小结-思维导图: ​编辑

二、内存布局中的异常问题

1.堆内存溢出

 2.虚拟机栈和本地方法栈溢出

三、思考题- 判断每个变量在哪个区?


一、由以下5大部分组成

1.Heap 堆区(线程共享)

概念:

堆是JVM中最大的一块内存区域,用于存储所有的对象实例和数组。它是线程共享的。

 特点

堆中的内存空间是垃圾收集器(Garbage Collector)管理的主要区域。JVM通过垃圾收集机制回收不再使用的对象,以防止内存泄漏。

结构

       堆通常分为年轻代(Young Generation)和老年代(Old Generation)。年轻代又细分为Eden区和两个Survivor区(S0和S1)。新创建的对象首先分配到年轻代。

        垃圾回收的时候会将Endn中存活的对象放到⼀个未使⽤的Survivor中,并把当前的Endn和正在使用的Survivor中的对象清除掉。

        经过几次垃圾收集后仍存活的对象将被移到老年代。 

 


2.程序计数器(线程私有)

什么是线程私有?

每个线程都有自己的程序计数器。

概念:

       在JVM中,线程的执行是通过线程轮流切换(也称为上下文切换)来实现的。在这种机制下,每个线程都得到一小段时间片来执行它的指令。当时间片用完或者发生其他中断时,处理器会切换到另一个线程继续执行。由于处理器在任何一个时刻只能执行一条线程的指令,所以在进行线程切换时,必须用独立的程序计数器保存当前线程的执行状态,以便在切换回来时能够从正确的位置继续执行。

       程序计数器是一个很小的内存区域,专门用于记录当前线程正在执行的指令地址。也就是说,程序计数器保存了当前线程下一条将要执行的字节码指令的地址。

     对于本地方法来说,程序计数器则为空。

特点:不会抛出OOM

    由于它的内存需求极小,并且仅用于存储指令地址,因此JVM规范中没有规定它会抛出“OutOfMemoryError”异常,这使得它成为JVM中唯一 一个不会因为内存不足而导致异常的区域。


3.Java虚拟机栈(线程私有)

概念:

        Java虚拟机栈的⽣命周期和线程相同,其为每个线程创建的私有内存区域每个线程在执行方法时,会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。常说的堆内存、栈内存中,栈内存指的就是虚拟机栈。

作用

1.局部变量表  :保存了方法参数和局部变量,所需的内存空间在编译期间完成分配,当进⼊⼀个⽅法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在执⾏期间不会改变局部变量表⼤小。

2.操作数栈: 用于操作数的计算和方法调用

3.动态链接: 保存了方法调用中的引用(指向运⾏时常量池的方法引用)。

4.方法返回地址:PC寄存器的地址。

异常:

       如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常。如果虚拟机在栈扩展时无法分配足够的内存,也会抛出OutOfMemoryError异常。

   


4.本地方法栈(线程私有)

概念

       本地方法栈中存储了本地方法的调用信息。本地方法栈与虚拟机栈类似,区别在于它为本地方法(Native Methods)服务。本地方法栈也是线程私有的。

     本地方法是指那些使用非Java语言编写的、并通过Java调用的函数或方法。在Java中,本地方法通常使用C或C++编写,并通过本地库接口来进行调用。

简单了解JVM执行Java程序的基本流程 | 一次编译,到处运行-CSDN博客 

该博客介绍了什么是本地方法库。


5.元数据区(线程共享) ( Java8前叫方法区 )

作用

       用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在方法区中,保存了类的结构信息,例如字段表、方法的字节码、常量池等。JVM在加载类时,会在方法区中为其分配空间,存储类的相关信息。它是线程共享的,也就是说,所有线程都可以访问方法区中的数据。

       字段表:列出了该类声明的所有字段(成员变量和静态变量),包括字段的名称、类型、修饰符(如 privatestatic 等)等。字段表不存储字段的值,而是存储字段的结构和元数据。

演变:

       在《Java虚拟机规范中》把此区域称之为“⽅法区”,而在HotSpot虚拟机的实现中,在JDK7时此 区域叫做永久代(PermGen),JDK8中叫做元空间(Metaspace)。

元空间的改进:

  1. 使用本地内存:与永久代不同,元空间使用的是本地内存(即操作系统管理的内存),而不是 JVM 堆内存。这意味着元空间的大小不再受 JVM 堆内存的限制,而是可以根据实际需要动态增长(只要系统内存允许)。

  2. 自动调整:元空间的内存分配可以根据应用程序的需要动态调整,减少了内存溢出问题的发生。JVM 提供了 -XX:MaxMetaspaceSize 参数来限制元空间的最大大小,但如果不设置,该空间可以根据需求自动增长。

  3. 更高效的内存管理:元空间的实现使得 JVM 的内存管理更加高效,因为它减少了永久代中的一些垃圾回收开销,并且更好地适应了应用程序的内存需求。

运行时常量池:

运行时常量池方法区的⼀部分,存放字面量与符号引用。

字面量: 字符串(JDK8移动到堆中)final常量基本数据类型的值

符号引用: 类和结构的完全限定名、字段的名称和描述符、⽅法的名称和描述符


6.小结-思维导图: 

 


二、内存布局中的异常问题

1.堆内存溢出

堆:放对象的地方。

可以设置JVM参数-Xms:设置堆的最⼩值、-Xmx:设置堆最⼤值。

在对象数量达到最大堆容量后就会产生内存溢出异常。

出现Java堆内存溢出时,

异常堆栈信息"java.lang.OutOfMemoryError"会进⼀步提⽰"Java heap space"。很明确的告知我们,OOM发生在堆上。

此时要对Dump出来的⽂件进行分析,分析问题的产⽣到底是出现了内存泄漏

  • 内存泄漏:表示对象不再被程序使用,但由于某些错误引用无法被 GC 回收。这会逐渐耗尽内存,最终导致 OutOfMemoryError。泄漏对象是不必要的、意外存在的。
  • 内存溢出:表示应用程序的确需要那么多内存来存活当前的对象。如果内存不足以满足需求,就会出现 OutOfMemoryError。此时这些对象是必要的,只是 JVM 堆内存不够

 2.虚拟机栈和本地方法栈溢出

       由于我们HotSpot虚拟机将虚拟机栈与本地⽅法栈合⼆为⼀,因此对于HotSpot来说,栈容量只需要 由-Xss参数来设置。

两种异常:

抛出StackOverFlow异常: 线程请求的栈深度大于虚拟机所允许的最大深度(例如,递归调用过深)。

抛出OOM异常:虚拟机在拓展栈时无法申请到⾜够的内存空间(例如内存紧张、大量线程的场景)。

如何应对

应对 StackOverflowError

- 检查递归调用的逻辑,确保递归有合理的终止条件。

- 调整程序结构,减少不必要的深层次方法调用。

- 如果必须使用深度递归,考虑通过 JVM 参数 -Xss 增加单个线程的栈大小(但这可能会增加 OOM 的风险)。

- 应对 OutOfMemoryError

- 降低应用程序的内存使用,尤其是减少不必要的线程创建。

- 增加系统的物理内存,或通过 JVM 参数增加最大堆内存。

- 通过优化代码,减少栈的频繁扩展需求,例如减少对象创建或方法调用的频率。

三、思考题- 判断每个变量在哪个区?

public class test5 {
    int a; // 成员变量
    static int b; // 静态变量
    test2 test2 = new test2(); // 成员变量
    String s = "猜猜每个变量在哪个区?"; // 字符串成员变量

    public static void main(String[] args) {
        test1 t = new test1(); // 局部变量
    }
}

答案揭晓:

test5 类本身的元数据(如类的结构、方法、字段等信息表)存储在方法区中。在 JDK 8 中,这个区域被称为元数据区(Metaspace)。

成员变量 a: 存储在堆内存中,属于每个 test5 对象的实例。


静态变量 b: 存储在方法区的静态存储区中,属于 test5 类本身,而不是任何特定对象。

成员变量 test2: 作为引用类型(对象)的成员变量,存储在堆内存中。它指向 new test2() 创建的对象,后者也存储在堆内存中。

成员变量 s:  是引用类型(对象)的成员变量,存储在堆内存中。它指向字符串常量池中的 "猜猜每个变量在哪个区?" 对象。在 JDK 8 中,字符串常量池已经移动到了堆内存中。

局部变量 t: 局部变量,存储在栈内存中。它指向 new test1() 创建的对象,后者存储在堆内存中。

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

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

相关文章

【html+css 绚丽Loading】 - 000004 玄天旋轮

前言:哈喽,大家好,今天给大家分享htmlcss 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 &#x1f495…

STM32 编码器模式详解

编码器模式 stm32的定时器带的也有编码器模式。 所用的编码器是有ABZ三相,其中ab相是用来计数,z相输出零点信号。 AB相根据旋转的方向不同,输出的波形如下图所示: 从图上可以看出来,cw方向A相会超前B相90度左右&#…

egret 拖尾的实现 MotionStreak

背景:egret项目中需要用到拖尾效果,引擎原生没有提供,参考cocos2dx 的 MotionStreak实现拖尾效果。 原理 拖尾的原理很简单,定时记录节点的位置,根据运行的轨迹和指定的拖尾宽度生成拖尾网格,然后将纹理绘…

VS2019开发跨平台(Linux)程序时,怎么配置第三方库的路径

一、问题描述: 使用跨平台编译时,VS2019总是提示链接openssl库有问题; 二、错误时的配置: 1、前提 openssl在Linux系统默认下是1.0.0版本,而自己准备好的是1.1.1版本,并且路径完全不在一个地方&#xf…

【Linux-进程】系统初识:冯诺依曼体系结构

系列文章:《Linux入门》 目录 冯诺依曼体系结构 1)硬件上 🌷1.什么是冯诺依曼体系结构? 🌷2.冯诺依曼结构的五个主要组成部分 1.运算器 2.控制器 3.存储器 4.输入输出 设备 ⁉️3.为什么还需要内存呢&#xf…

c++数据结构算法复习基础-- 4 -- 线性表-单向循环链表-常用操作接口-复杂度分析

1、单向循环链表一 1)特点 每一个节点除了数据域,还有一个next指针域指向下一个节点(存储了下一个节点的地址) 末尾节点的指针域指向了头节点 析构函数思路图 2)代码实现 //定义结点 //单向循环链表 class CircleLink { public://构造函数…

使用python基于fastapi发布接口(一)

FastAPI官网地址 FastAPI基于Python 3.6+和Starlette框架,天生就带着高性能和异步的基因。 FastAPI的文档生成功能简直是开发者的福音! 你不再需要手动编写API文档,FastAPI能自动帮你搞定。 FastAPI还超级灵活,支持各种数据库和认证方式,无论是SQLite、PostgreSQL还是M…

【xilinx】TPM可信平台模块与 Zynq UltraScale+ PS SPI 接口

本博客(Venu Inaganti)介绍了可信平台模块 (TPM) 与 Zynq UltraScale PS SPI 控制器的连接。 目前唯一具有 TPM 的评估板是 KR260/KV260 SOM,因此为了帮助正在试验 Zynq UltraScale 设备的用户,本文介绍了如何通过 PMOD 连接器与…

【MongoDB】Java连接MongoDB

连接URI 连接 URI提供驱动程序用于连接到 MongoDB 部署的指令集。该指令集指示驱动程序应如何连接到 MongoDB,以及在连接时应如何运行。下图解释了示例连接 URI 的各个部分: 连接的URI 主要分为 以下四个部分 第一部分 连接协议 示例中使用的 连接到具有…

计算机视觉中的上采样与下采样:深入浅出实例代码解析

文章目录 一、引言二、下采样(Downsampling)三、上采样(Upsampling)1. 最近邻插值2.双线性插值3.转置卷积(Deconvolution)4.代码部分 四、总结 在计算机视觉领域,尤其是在深度学习和卷积神经网络…

宝塔面板部署webman项目+nginx反向代理

新建站点 新建一个站点,php版本选择纯净态即可,反正都是用不上的,域名填写你申请得到的域名 拉取代码 新建一个目录,然后将代码部署到本地 启动项目 推荐使用宝塔面板的进程守护管理器启动项目,其实就是用superviso…

ATT格式与Intel格式x86汇编指令的区别

AT&T公司 这个公司的创始人就是发明电话的贝尔,而Unix和C语言都是出自贝尔实验室的产物。 Intel公司 世界上第一片CPU是1971年发明的,型号是Intel生产的4004微处理器。 两种格式的区别 AT&T格式Intel格式目的操作数d、源操作数s op s, d 注…

vue2中使用i18n配置elementUi切换语言

1、下载插件 npm i vue-i18n8.22.2 2、新建文件夹i18n 3、编写index.js文件 import Vue from "vue"; import VueI18n from "vue-i18n"; import locale from element-ui/lib/locale; // 引入 elementui 的多语言 import enLocale from element-ui/lib/l…

【MySQL】C/C++连接MySQL客户端,MySQL函数接口认知,图形化界面进行连接

【MySQL】C/C引入MySQL客户端 安装mysqlclient库mysql接口介绍初始化mysql_init链接数据库mysql_real_connect下发mysql命令mysql_query获取出错信息mysql_error获取执行结果mysql_store_result获取结果行数mysql_num_rows获取结果列数mysql_num_fields判断结果列数mysql_field…

域自适应,你适应了嘛?

“最难的深度学习是谁?” “嗯,是迁徙学习吧?” “要分情况,不过,应该是迁徙学习吧? ” “不是迁徙学习嘛?” 目录 域自适应是啥? 域自适应的方法? 基于差异的方法…

Kafka系列之:Kafka Connect深入探讨 - 错误处理和死信队列

Kafka系列之:Kafka Connect深入探讨 - 错误处理和死信队列 一、快速失败二、YOLO:默默忽略坏消息三、如果一条消息掉在树林里,会发出声音吗?四、将消息路由到死信队列五、记录消息失败原因:消息头六、记录消息失败原因…

k8s Pod生命周期详解

文章目录 一、创建Pod二、启动Pod三、销毁Pod 共分为三步:创建Pod、启动Pod、销毁Pod 一、创建Pod K8S创建Pod的过程 二、启动Pod 1、kubelet调用容器运行时创建Pause容器,准备一个容器环境 2、创建初始化容器init container。如果有多个,…

打印网页使内容包含有效网络连接Print webpage with workable hyperlinks

小虎想打印网页,并且将里面有链接的文字带文字一起打印保存。 解决方法 利用谷歌浏览器的打印功能即可: Use print options in chrome.

构建一个Markdown编辑器:Fyne综合案例

在本文中,我们将通过一个完整的案例来介绍如何使用Go语言的Fyne库来构建一个简单的Markdown编辑器。Fyne是一个易于使用的库,它允许开发者使用Go语言来创建跨平台的GUI应用程序。 1. 项目结构 首先,我们需要创建一个Go项目,并引…

基于vllm部署大模型

VLLM(非常大的语言模型)在中文中通常指的是经过大量文本数据训练的神经网络模型,能够理解和生成类似人类语言的文本。这类模型是许多先进AI系统的核心,比如ChatGPT、GPT-4等。 VLLM 可以执行各种任务,如自然语言处理、…