likes
comments
collection
share

阿里面试题:JVM内存结构

作者站长头像
站长
· 阅读数 26

Java 虚拟机(JVM)内存结构是 JVM 管理内存分配和垃圾回收的核心部分。理解 JVM 的内存结构对于优化 Java 应用程序的性能和解决内存问题至关重要。以下是 JVM 内存结构的详细解析,包括各个内存区域的功能、特点及其在运行时的作用。

JVM 内存结构概览

JVM 内存结构通常分为以下几个主要区域:

  1. 程序计数器
  2. Java 虚拟机栈
  3. 本地方法栈
  4. 方法区
  5. 运行时常量池

下图展示了 JVM 内存结构的整体概览:

┌─────────────────────────┐
│    堆(Heap)           │
│ ┌─────────────────────┐ │
│ │ Eden                │ │
│ ├─────────────────────┤ │
│ │ Survivor (from)     │ │
│ ├─────────────────────┤ │
│ │ Survivor (to)       │ │
│ ├─────────────────────┤ │
│ │ Old Generation      │ │
│ ├─────────────────────┤ │
│ │ PermGen/Metaspace   │ │
│ └─────────────────────┘ │
└─────────────────────────┘
┌─────────────────────────┐
│ Java 虚拟机栈(JVM Stack) │
│ ┌─────────────────────┐ │
│ │ Frame               │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ Frame               │ │
│ └─────────────────────┘ │
│   ...                   │
└─────────────────────────┘
┌─────────────────────────┐
│ 本地方法栈(Native Stack)│
│ ┌─────────────────────┐ │
│ │ Frame               │ │
│ └─────────────────────┘ │
│   ...                   │
└─────────────────────────┘
┌─────────────────────────┐
│ 程序计数器(PC Register)│
└─────────────────────────┘

1. 程序计数器

  • 作用:存储当前线程正在执行的字节码指令的地址。每个线程都有自己的程序计数器。
  • 特性
    • 独立于线程,是线程私有的。
    • 是 JVM 执行 Java 字节码时指令位置的标识器。

2. Java 虚拟机栈

  • 作用:存储 Java 方法执行时的栈帧(Stack Frame),包括局部变量、操作数栈、方法返回值等信息。
  • 特性
    • 线程私有,每个线程有自己的 Java 虚拟机栈。
    • 每个方法调用都会创建一个新的栈帧,方法结束时栈帧销毁。
    • 可以出现栈溢出(StackOverflowError)和栈内存不足(OutOfMemoryError)。

3. 本地方法栈

  • 作用:用于本地方法(Native Method)的执行,类似于 Java 虚拟机栈,但主要用于本地代码(通常是通过 JNI 调用的 C/C++ 代码)。
  • 特性
    • 线程私有,每个线程有自己的本地方法栈。
    • 也可能出现栈溢出(StackOverflowError)和栈内存不足(OutOfMemoryError)。

4. 堆

  • 作用:用于存储所有对象实例和数组,是垃圾收集器主要管理的区域。
  • 特性
    • 线程共享,全局只有一个堆。
    • 分为年轻代(Young Generation)和老年代(Old Generation),年轻代通常进一步划分为 Eden 区、Survivor from 区和 Survivor to 区。
    • 年轻代的对象容易被垃圾回收,老年代存储生命周期较长的对象。

堆的详细结构

  • 年轻代(Young Generation)

    • Eden 区:新对象首先分配在 Eden 区。
    • Survivor 区
      • Survivor from:幸存的对象从 Eden 区移动到 Survivor from 区。
      • Survivor to:用于对象复制的目标区,GC 后交换角色。
    • 垃圾回收:主要是 Minor GC(次要垃圾回收),发生频繁且回收时间短。
  • 老年代(Old Generation)

    • 作用:存储长生命周期的对象。
    • 垃圾回收:主要是 Major GC 或 Full GC(全局垃圾回收),回收频率较低但耗时较长。

5. 方法区

  • 作用:存储类结构、常量、静态变量、即时编译器编译后的代码等。是 JVM 规范的一部分。
  • 特性
    • 线程共享。
    • 在 JDK 1.8 之前,方法区被实现为永久代(PermGen);在 JDK 1.8 之后,方法区被实现为元空间(Metaspace)。
    • 元空间在本地内存中分配,避免了内存溢出问题。

方法区的详细结构

  • 永久代(PermGen,JDK 1.7 及之前)

    • 存储类元数据和静态变量。
    • 受到固定大小的限制,容易出现内存溢出(OutOfMemoryError)。
  • 元空间(Metaspace,JDK 1.8 及之后)

    • 存储类元数据和静态变量。
    • 使用本地内存(而不是 JVM 堆内存),提高了内存管理的灵活性。

6. 运行时常量池

  • 作用:存储编译期间生成的各种字面量和符号引用。是方法区的一部分。
  • 特性
    • 包含编译时生成的常量(如字符串、数值)和符号引用。
    • 在类加载时从 .class 文件的常量池中加载。

内存结构示意图

JVM 内存结构
+-----------------------------------------------------------+
|                   JVM 内存结构总览                        |
|                                                           |
|   +-----------------------------------------------------+ |
|   |                       堆(Heap)                      | |
|   |                                                     | |
|   |   +---------------------------------------------+   | |
|   |   |               年轻代(Young Generation)     |   | |
|   |   |                                             |   | |
|   |   |   +-------------+  +------------+           |   | |
|   |   |   | Eden        |  | Survivor   |           |   | |
|   |   |   |             |  | (from)     |           |   | |
|   |   |   +-------------+  +------------+           |   | |
|   |   |                                             |   | |
|   |   +---------------------------------------------+   | |
|   |                                                     | |
|   |   +---------------------------------------------+   | |
|   |   |               老年代(Old Generation)       |   | |
|   |   +---------------------------------------------+   | |
|   |                                                     | |
|   +-----------------------------------------------------+ |
|                                                           |
|   +------------------------+ +------------------------+   |
|   | Java 虚拟机栈           | | 本地方法栈             |   |
|   +------------------------+ +------------------------+   |
|                                                           |
|   +-----------------------------------------------------+ |
|   |                   方法区(Method Area)               | |
|   |                                                     | |
|   |   +---------------------------------------------+   | |
|   |   |            运行时常量池(Runtime Constant Pool) |   | |
|   |   +---------------------------------------------+   | |
|   +-----------------------------------------------------+ |
|                                                           |
|   +------------------------+                              |
|   | 程序计数器(PC Register)|                              |
|   +------------------------+                              |
+-----------------------------------------------------------+

总结

  • 程序计数器:跟踪当前线程的执行位置。
  • Java 虚拟机栈:管理方法调用的栈帧,包含局部变量和操作数栈。
  • 本地方法栈:处理本地方法调用。
  • :存储对象实例,分为年轻代和老年代。
  • 方法区:存储类结构、静态变量、运行时常量池,JDK 1.8 之后实现为元空间。
  • 运行时常量池:包含字面量和符号引用,是方法区的一部分。

了解 JVM 的内存结构有助于优化 Java 应用程序的性能、调试内存泄漏和调整垃圾回收策略。不同的内存区域有其特定的用途和管理方式,结合应用需求调整各区域的大小和配置,可以有效提高系统稳定性和性能。

转载自:https://juejin.cn/post/7386967785090842643
评论
请登录