首页 > 系统相关 >深入理解JVM运行时数据区(内存布局 )5大部分 | 异常讨论

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

时间:2024-08-17 21:57:27浏览次数:9  
标签:Java 虚拟机 布局 线程 内存 JVM 方法

前言:

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


一、由以下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在加载类时,会在方法区中为其分配空间,存储类的相关信息。它是线程共享的,也就是说,所有线程都可以访问方法区中的数据。

演变:

       在《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 参数增加最大堆内存。

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

标签:Java,虚拟机,布局,线程,内存,JVM,方法
From: https://blog.csdn.net/weixin_71246590/article/details/141285038

相关文章

  • Java面试题--JVM大厂篇之掌控Java未来:深入剖析ZGC的低停顿垃圾回收机制
    Java面试题--JVM大厂篇之掌控Java未来:深入剖析ZGC的低停顿垃圾回收机制引言:正文:一、ZGC的核心机制1.并发标记和重定位(Relocation)2.染色指针(ColoredPointers)与读屏障(LoadBarriers)二、实际案例分析1.在线游戏服务器2.金融交易系统三、解决方案和技巧1.调整ZGC参数......
  • HarmonyOS 层叠布局:(Stack)打造灵活多变的UI界面
    在应用开发中,布局设计是用户体验的关键之一。而在HarmonyOS中,层叠布局(Stack)是一种极为灵活的布局方式。它允许我们在同一个区域内放置多个组件,并根据需求将它们叠加起来,形成丰富的视觉效果。无论是广告展示还是卡片叠加效果,层叠布局都能轻松胜任。今天,我将带大家深入了解Stack......
  • 面试题:在Java中,JVM(Java虚拟机)的内存模型是如何设计的?请详细解释堆(Heap)、栈(Stack)、方法
    面试题:在Java中,JVM(Java虚拟机)的内存模型是如何设计的?请详细解释堆(Heap)、栈(Stack)、方法区(MethodArea)以及程序计数器(ProgramCounterRegister)的作用和它们之间的关系。更多答案在这里,手机或电脑浏览器就可以打开,面霸宝典【全拼音】.com这里可以优化简历,模拟面试,企业项......
  • 内存泄漏事故(三)
    背景:子线程中,分线程处理然后聚拢future。get但是在futureget的异常处理中,并未抛出异常,只是调用Thread.currentThread().interrupt(),因此主线程要监控这个interrup旗标,从而决定是否抛出异常 1起先 发现没什么用 2旗标的生命周期 子线程跑完后会复位flag,当然这个......
  • 坑!火山引擎云数据库 MySQL 版节点内存只增不减
    火山引擎云数据库说明文档什么是云数据库MySQL版?云数据库MySQL版是火山引擎基于开源数据库MySQL打造的弹性、可靠的在线关系型数据库服务。MySQL实例使用云原生方式部署,结合本地SSD存储类型,提供高性能读写能力;完全兼容MySQL引擎,并提供实例管理、备份恢复、日志管理......
  • C++编程:内存栅栏(Memory Barrier)详解及在多线程编程中的应用
    文章目录0.引言1.什么是内存栅栏?2.为什么需要内存栅栏?本质原因是什么?2.1编译器优化2.2CPU乱序执行3.ARM64和x86架构下的内存栅栏差异3.1x86架构3.2ARM64架构4.代码示例4.1代码解析4.2memory_order_release和memory_order_acquire解释4.3为什么是“releas......
  • C/C++内存管理
    文章目录前言C/C++内存分布C语言内存管理malloccallocreallocreallocarrayfreeC++内存管理new/delete内置类型自定义类型operatornew/operatordelete定位new内存泄漏前言        C++的内存管理是程序设计中的一个关键部分,涉及到内存的分配、使用和释......
  • C++八股文——内存管理(堆和栈的区别? C++内存分区? 内存泄漏?如何避免?什么是智能指针?有哪
    文章目录C++内存管理堆和栈的区别C++内存分区内存泄漏?如何避免?1、什么是内存泄露?2、内存泄漏的分类3、什么操作会导致内存泄露?4、如何防⽌内存泄露?5、智能指针有了解哪些?6、构造函数,析构函数要设为虚函数吗,为什么?什么是智能指针?有哪些种类?new和malloc有什么区别?d......
  • 【JVM】JVM 实战调优指南赋案例(保姆篇)
    文章目录JVM实战调优指南引言1.JVM基础知识1.1JVM架构1.2JVM垃圾回收2.垃圾回收调优2.1垃圾回收日志2.2GC日志分析2.3调优策略2.3.1调整堆大小2.3.2选择合适的GC算法2.3.3调整垃圾回收线程3.内存管理调优3.1内存泄漏检测3.2堆转储分析3.3内存分配策略......
  • 避免内存溢出
    Redis可以通过以下几种方式来避免内存溢出:设置最大内存限制:可以使用maxmemory配置项来限制Redis的最大内存使用量。当Redis的内存占用超过了设置的最大内存限制时,Redis会根据所配置的内存策略来处理数据,例如删除最近最少使用的键或者拒绝写入请求。使用内存淘汰策略:当Redis的......