JVM内存模型

本文详细介绍了JVM的作用和组成,重点解析了JVM内存模型的六大区域:PC寄存寄、方法区、运行时常量池、Java虚拟机栈、本地方法栈和堆内存,阐述了各区域的功能、生命周期及异常情况。
摘要由CSDN通过智能技术生成

本文内容基于目前使用最广泛的 HotSpot JVM。

一、JVM 的作用

JVM,即 JAVA 虚拟机(Java virtual machine),是运行 Java 程序必不可少的工具。

JVM 实现了 Java 语言最重要的特征:平台无关性。

1、Java 程序的运行机制

程序员编写的代码都是源代码(.java),不能直接执行,必须通过 javac 编译成字节码文件(.class),最后在 JVM 中运行。

java 程序的运行过程

2、Java 如何实现平台无关性?

Java 语言很重要的一个优点就是具有平台无关性:“一次编写,到处运行”,可以在 Windows、Solaris、Linux、MacOS 等各种操作系统平台上运行同样的 java 代码。

Java 实现平台无关性的关键就是 JVM

因为 Java 程序不是直接运行在操作系统上,而是运行在 JVM 中。而我们在运行 java 程序之前必须先搭建好 java 环境,其实就是在不同的操作系统上安装与系统对应的 JVM ,屏蔽了与具体平台相关的信息,最终给用户的感觉就是平台无关的。

二、JVM 组成

JVM 是通过在实际的计算机上仿真模拟各种计算机功能来实现的 java 虚拟机。

它由一套字节码指令集、一组寄存器、垃圾回收器、堆、栈、存储方法域等组成,如下图所示:

JVM 组成

翻译成中文,如下所示:

在这里插入图片描述

可以看出,JVM 主要由以下三部分组成:

JVM = 类加载器 classloader + 执行引擎 execution engine + 运行时数据区域 runtime data area

类加载器 classloader 把硬盘上的 class 文件加载到 JVM 中的运行时数据区域中,由执行引擎负责执行,执行过程中产生的数据都放在运行时数据区。

JVM 运行时数据区 (Runtime Data Area) 其实就是指 JVM 在运行期间,对 JVM 内存空间进行管理和分配。

三、JVM 内存模型

JVM 在执行 Java 程序时,会把运行时数据区 (Runtime Data Area) 划分为 6 个区域来存储运行时数据,包括:堆内存(Heap Memory)、虚拟机栈/Java栈(Java Stacks)、本地方法栈(Native Method Stack)、PC 寄存寄(PC Registers)、方法区(Method Area)、运行时常量池。

JVM 内存模型如下图所示:

JVM 内存模型

程序员写的所有程序都被加载到运行时数据区域中,根据数据类型不同分别存放在 6 个内存区域中。

这些区域分别有各自的用途、生命周期,有些依赖虚拟机进程启动而存在,有些依赖用户线程的启动和结束而建立和销毁。

1、PC 寄存寄

PC 寄存寄(PC Registers),也称为程序计数器。

每个线程都会有自己私有的程序计数器,它是一块较小的内存空间,相当于当前线程所执行的字节码程序的行号指示器。

程序计数器区域是唯一一个在 JVM 规范中没有规定任何 OOM (OutOfMemoryError)情况的区域。

2、方法区

方法区,Method Area,是各个 线程共享 的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量等数据。

很多时候,也把方法区称为 “永久代”(Permanent Generation)。

根据 JVM 规范,当方法区无法满足内存分配需求时,可能抛出 OutOfMemoryError 异常。

3、运行时常量池

运行时常量池(Runtime Constant Pool)是方法区中的一部分,其中存放的是类中固定的常量信息、方法和域的引用信息。

该区域受方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

4、Java 虚拟机栈

Java 虚拟机栈,即 Java Virtual Machine Stack,通常简称为 VM Stack 或 Java Stack 或 Stack --「栈」,它描述了 Java 方法执行的内存模型。

虚拟机栈具有如下特点:

  • 线程私有,生命周期和线程相同;
  • 每个方法在执行时都会创建一个栈帧 (Stack Frame),用于存储 局部变量表(方法中定义的变量)、操作数栈、动态链接、方法出口等信息。
  • 栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。
  • 每个方法从调用直至执行完成的过程,就是对应着一个栈帧在栈中入栈和出栈的过程。

在 JVM 规范中对这块区域规定了两种异常情况:

  • 如果线程请求的栈深度大于 JVM 允许的深度,则抛出 StackOverflowError 异常;
  • 如果虚拟机栈动态扩展时,无法申请到足够的内存,则抛出 OutOfMemoryError 异常。

5、本地方法栈

本地方法栈,即 Native Method Stack,用于执行 Native 方法(简单的说,Native 方法就是用 java 调用非 java 代码的接口,一般是针对一些系统底层的代码)。它与虚拟机栈的作用相似。

Native Method StackVM Stack 的区别:

  • VM Stack 是为执行 java 方法服务的;
  • Native Method Stack 是为执行本地方法(非 java 代码)服务的;
  • 二者其它的作用相似。

在目前最流行的 Sun HotSpot 虚拟机中,直接把虚拟机栈和本地方法栈合二为一。

与 Java 虚拟机栈一样,本地方法栈也会抛出 StackOverflowErrorOutOfMemoryError 异常。

6、堆内存

堆内存,即 Heap Memory,是 JVM 管理的内存中最大的一块。

堆内存具有如下主要特点:

  • 在虚拟机启动时被创建;
  • 被所有线程共享的一块存储区域;
  • 几乎所有的对象实例和数组都要在堆上分配。

堆中存储的对象,无需、也无法显式地被销毁。它们被自动管理内存系统(Automatic Storage Management System,也即是常说的 「Garbage Collector(垃圾回收器)」)所管理。

堆内存的大小可以是固定的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。

根据 JVM 规范,当在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出 OutOfMemoryError 异常。

由于篇幅限制,在下一篇文章中对堆内存进一步探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值