暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JVM内存区域深度解析

IT那活儿 2025-03-03
12

点击上方“IT那活儿”公众号--专注于企业全栈运维技术分享,不管IT什么活儿,干就完了!!!
  
Java虚拟机(JVM)在执行Java程序时,会将其管理的内存划分为多个不同的数据区域。这些区域各有其特定的用途和特性,理解这些内存区域对于深入掌握Java性能优化和故障排查至关重要。
本文将换一种阐述方式来详细解析JVM内存区域的各个部分。



JVM内存区域概览

JVM内存区域主要包括:

  • 程序计数器(PC寄存器);

  • Java虚拟机栈(JVM栈);

  • 本地方法栈;

  • 堆;

  • 方法区(在JDK 1.8以后称为元空间);

  • 运行时常量池以及直接内存。

这些区域可以大致分为线程私有线程共享两类。


线程私有的内存区域

2.1 程序计数器(PC寄存器)

程序计数器是每个线程独有的,用于存储当前线程正在执行的Java方法的JVM指令地址。它是线程私有的,生命周期与线程相同。

由于Java的线程是轮流执行的,程序计数器负责记录每个线程的执行位置,以便线程切换后能恢复到正确的执行点。程序计数器是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

2.2 Java虚拟机栈(JVM栈)

Java虚拟机栈也是线程私有的,生命周期与线程相同。每个线程在创建时都会创建一个虚拟机栈,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

每次方法调用都会创建一个栈帧(Stack Frame)来存储这些信息。栈帧的入栈和出栈过程对应着方法的调用和返回。虚拟机栈的主要异常有StackOverflowError(栈深度超过限制)和OutOfMemoryError(内存扩展失败)。

  • 局部变量表
    用于存储方法参数和局部变量,在编译期间分配内存空间。
  • 操作数栈
    用于保存计算过程的中间结果和变量临时存储空间,采用后进先出(LIFO)的栈结构。

2.3 本地方法栈

本地方法栈与Java虚拟机栈类似,但它主要用于执行Native方法(如C/C++编写的方法)。在某些JVM实现中,本地方法栈和Java虚拟机栈是合二为一的。


线程共享的内存区域

3.1 堆(Heap)

堆是JVM中最大的一块内存区域,用于存放对象实例。堆被所有线程共享,是垃圾收集器的主要工作区域。Java堆在逻辑上被划分为新生代(Young Generation)和老年代(Old Generation),新生代又被进一步划分为Eden区和两个Survivor区(From Survivor和To Survivor)。新生代的对象在经历多次垃圾收集后仍然存活,将被转移到老年代。堆内存不足时会抛出OutOfMemoryError异常。

3.2 方法区(元空间)

在JDK 1.8及以后的版本中,方法区被元空间(Metaspace)所取代。方法区(或元空间)用于存储已被虚拟机加载的类信息、常量、静态变量等数据。虽然方法区被描述为堆的逻辑部分,但它有“非堆”(Non-Heap)的别名,以区别于Java堆。方法区可以选择不实现垃圾收集,但在内存不足时同样会抛出OutOfMemoryError异常。

3.3 运行时常量池

运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。运行时常量池具有动态性,即不仅限于存放编译期产生的常量,还可以在运行时将新的常量放入池中。当运行时常量池无法申请到足够内存时,也会抛出OutOfMemoryError异常。


直接内存

直接内存不属于JVM运行时数据区的一部分,但它是通过NIO(New Input/Output)类引入的一种堆外内存。直接内存可以显著提高I/O性能,因为它避免了在Java堆和Native堆之间复制数据。然而,直接内存的使用受到本机总内存的限制,若分配不当,可能导致OutOfMemoryError异常。

JVM内存区域的划分和管理是Java性能优化的关键。

了解每个内存区域的作用、特性和异常类型,有助于我们更好地编写高效、稳定的Java程序。


END


本文作者:李伟康(上海新炬中北团队)

本文来源:“IT那活儿”公众号

文章转载自IT那活儿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论