深入解析 JVM 内存区域及核心概念

深入解析 JVM 内存区域及核心概念

Java 虚拟机(JVM)内部划分了多个内存区域,每个区域存储不同类型的数据并承担不同的职责。本文将详细介绍以下内容:

  • 程序计数器:记录当前线程正在执行的字节码指令及其“行号”信息,附带字节码示例展示其“样子”。

  • JVM 栈:管理方法调用时产生的栈帧,包含局部变量、操作数栈等数据,代码示例展示递归调用导致栈溢出的情形。

  • 本地方法栈:支持 JNI 调用和本地方法执行,虽然一般不用直接操作,但了解其基本概念有助于理解底层实现。

  • Java 堆:所有对象实例存放的主要区域,由垃圾收集器管理。

  • 方法区与运行时常量池:存放类的结构信息、字面量常量、符号引用以及静态变量。我们将通过代码和 javap 命令生成的输出展示常量池的内容。

  • 直接内存:通过 NIO 分配的堆外内存,适用于高性能 I/O 操作。

下面我们逐一介绍各个部分,并给出直观的示例。


1. 程序计数器

1.1 理论说明

程序计数器是一块非常小的内存区域,主要功能是记录当前线程正在执行的字节码指令的地址,相当于程序中的“行号指示器”。它在以下方面起着关键作用:

  • 控制流程:在循环、分支、异常处理等场景下,指明下一条要执行的指令。

  • 线程隔离:每个线程都有独立的程序计数器,因此线程之间互不干扰。

  • 无需垃圾回收:由于体积极小,JVM 不会因程序计数器而抛出 OutOfMemoryError。

1.2 字节码示例

虽然在 Java 代码中无法直接观察程序计数器的变化,但我们可以通过查看编译后的字节码了解其作用。假设有如下简单方法:

public class BytecodeDemo {public void exampleMethod() {int a = 10;int b = 20;int c = a + b;System.out.println(c);}
}

使用 javap -c BytecodeDemo 后可能得到类似如下的字节码(部分输出):

Compiled from "BytecodeDemo.java"
public class BytecodeDemo {public BytecodeDemo();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnpublic void exampleMethod();Code:0: bipush        10        // 将数字 10 压入操作数栈2: istore_1                // 存储到局部变量 13: bipush        20        // 将数字 20 压入操作数栈5: istore_2                // 存储到局部变量 26: iload_1                 // 将局部变量 1 加载到操作数栈7: iload_2                 // 将局部变量 2 加载到操作数栈8: iadd                    // 执行加法运算9: istore_3                // 将结果存储到局部变量 310: getstatic     #2        // Field java/lang/System.out:Ljava/io/PrintStream;13: iload_3                 // 将计算结果加载到操作数栈14: invokevirtual #3        // Method java/io/PrintStream.println:(I)V17: return
}

在这个字节码中,每个数字(如 0、1、2...)就是程序计数器对应的“地址”或指令索引。虽然我们平时无法直接操作,但这正是 JVM 调度字节码时依据的依据。


2. Java 虚拟机栈

2.1 概念解析

当 Java 程序调用方法时,JVM 为该方法分配一个“栈帧(Stack Frame)”。每个栈帧包含:

  • 局部变量表:存储方法参数和局部变量。变量的存储位置称为“槽”(Slot)。

  • 操作数栈:用于存放运算过程中的中间结果。

  • 动态链接信息:用于支持方法调用过程中的符号解析。

  • 返回地址:指明调用结束后应返回的指令位置。

2.2 代码示例:递归调用导致栈溢出

下面的代码展示了递归调用时不断分配栈帧的过程,最终可能导致 StackOverflowError

public class JVMStackDemo {public static void recursiveCall(int depth) {System.out.println("递归深度:" + depth);recursiveCall(depth + 1);}public static void main(String[] args) {try {recursiveCall(1);} catch (StackOverflowError e) {System.err.println("错误:栈溢出,递归调用太深!");}}
}

每次递归调用都会在 JVM 栈中创建一个新的栈帧,当递归深度超过 JVM 所分配的栈内存时,就会抛出栈溢出错误。


3. 本地方法栈

3.1 概念解析

本地方法栈主要用于执行 JNI(Java Native Interface)调用和本地方法(如 C/C++ 代码)。其工作方式与 JVM 栈相似,不同点在于:

  • 服务对象:本地方法栈专门处理本地代码,而 JVM 栈用于执行 Java 字节码。

  • 实现自由:《Java 虚拟机规范》允许各个 JVM 自行实现本地方法栈,部分 JVM 可能将其与 JVM 栈合并。

了解这一部分有助于调试与 JNI 相关的问题,但一般情况下开发者无需直接干预。


4. Java 堆

4.1 概念解析

Java 堆是 JVM 内存中最大的区域,所有通过 new 关键字创建的对象都分配在堆中。堆内存由垃圾收集器管理,当对象不再被引用时,系统会自动回收这些内存。

4.2 代码示例:对象的分配与垃圾回收

public class HeapDemo {static class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}@Overrideprotected void finalize() throws Throwable {System.out.println("回收 Person 对象:" + name);super.finalize();}}public static void main(String[] args) {// 创建大量对象,促使垃圾回收器启动for (int i = 0; i < 100000; i++) {new Person("Person" + i, i);}// 请求垃圾回收(仅建议,实际执行由 JVM 决定)System.gc();System.out.println("对象创建完毕,请查看垃圾回收日志。");}
}

在此示例中,大量 Person 对象被创建并分配在堆中,当它们不再被引用时,垃圾回收器会回收相应内存。


5. 方法区与运行时常量池

5.1 概念解析

方法区存储了 JVM 加载的类信息,包括:

  • 类的结构信息:类的全限定名、父类、接口、字段、方法及修饰符等。

  • 常量:编译期间确定的字面量(如字符串、数字、布尔值)会存入运行时常量池中。

  • 静态变量:类变量在类加载时初始化,并在整个应用中共享。

运行时常量池是方法区的一部分,它保存了:

  • 字面量常量:例如 "HelloWorld"、数字 100 等。

  • 符号引用:在编译期间以符号形式存在,类加载时会解析成直接引用(例如类名、方法名、字段名)。

5.2 代码示例:类结构、常量与静态变量

package com.example;public class MyClass {// 实例字段private int instanceField;// 静态字段(类变量)public static String staticField = "静态变量示例";// 常量(在编译期间确定,并存入运行时常量池)public static final double PI = 3.14159;public MyClass(int instanceField) {this.instanceField = instanceField;}public void display() {System.out.println("实例字段:" + instanceField);}public static void printStatic() {System.out.println("静态字段:" + staticField);}
}

在这个示例中:

  • 类的结构信息:包括类名 com.example.MyClass、字段 instanceFieldstaticField 以及方法。

  • 常量PI 作为 final 修饰的常量,其值在编译期确定,并存入运行时常量池中。

  • 静态变量staticField 在类加载时分配,所有实例共享该变量。

5.3 运行时常量池的直观展示

你可以使用 javap -v MyClass 命令查看类的详细信息,其中会列出常量池的内容。部分输出示例如下:

Constant pool:#1 = Methodref          #7.#17         // java/lang/Object."<init>":()V#2 = String             "静态变量示例"#3 = Float              3.14159f#4 = Utf8               MyClass#5 = Utf8               instanceField...

这些条目显示了类中存在的各种字面量、符号引用等信息,是 JVM 在加载类时用来解析并建立直接引用的重要依据。


6. 直接内存

6.1 概念解析

直接内存并非 JVM 内部数据区的一部分,但常用于高性能 I/O 操作。它通过 NIO 类库直接从操作系统分配内存,可以减少数据在 Java 堆和本地内存之间的复制开销。

说明:

NIO 是 “New I/O” 的缩写,它是 Java 在 JDK 1.4 中引入的一套全新的 I/O API,相对于传统的基于流(Stream)的 I/O 模型,NIO 提供了以下几个显著的特点:

  • 缓冲区(Buffer):NIO 采用缓冲区来处理数据,数据的读写都是通过缓冲区进行的,这样可以更高效地管理内存。

  • 通道(Channel):与传统的 I/O 流不同,通道可以用于读写数据,它支持双向传输,可以同时进行读和写操作。

  • 选择器(Selector):支持非阻塞 I/O,允许单个线程同时监控多个通道的状态,从而实现高效的 I/O 多路复用。

  • 内存映射文件(Memory-mapped File):可以将文件直接映射到内存,进一步提高 I/O 性能。

6.2 代码示例:使用 NIO 分配直接内存

import java.nio.ByteBuffer;public class DirectMemoryDemo {public static void main(String[] args) {// 分配 1024 字节的直接内存ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);System.out.println("直接内存容量:" + directBuffer.capacity() + " 字节");// 向直接内存写入数据for (int i = 0; i < 10; i++) {directBuffer.put((byte) i);}// 翻转缓冲区以便读取directBuffer.flip();System.out.print("直接内存数据:");while (directBuffer.hasRemaining()) {System.out.print(directBuffer.get() + " ");}System.out.println();}
}

以上示例展示了如何分配并操作直接内存,从而避免频繁在 Java 堆和本地内存之间复制数据。


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

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

相关文章

Java操作RabbitMQ

文章目录 Spring集成RabbitMQ1. AMQP&SpringAMQP2. SpringBoot集成RabbitMQ3. 模型work模型 4.交换机Fanout交换机Direct交换机Topic交换机 5.声明式队列和交换机基于API声明基于注解声明 6.消息转换器 Spring集成RabbitMQ 1. AMQP&SpringAMQP AMQP&#xff08;高级消…

Kotlin泛型: 协变|逆变|不变

引言 无论java 通配符上限还是下限&#xff0c;都多少存在缺陷&#xff0c;要么存不安全&#xff0c;要么取不安全。而kotlin就解决这个问题。让out 纯输出&#xff0c; 让in纯输入。 java这块知识&#xff1a; java泛型的协变、逆变和不变-CSDN博客 协变 生产者out T 协变…

【Excel使用技巧】某列保留固定字段或内容

目录 ✅ 方法一&#xff1a;使用 Excel 公式提取 body 部分 &#x1f50d; 解释&#xff1a; ✅ 方法二&#xff1a;批量处理整列数据 &#x1f6a8; 注意事项 &#x1f6a8; 处理效果 我想保留Excel某一列的固定内容&#xff0c;比如原内容是&#xff1a; thread entry i…

C# System.Text.Encoding 使用详解

总目录 前言 在C#编程中&#xff0c;处理字符串和字节数组之间的转换是一个常见的任务。System.Text.Encoding类及其派生类提供了丰富的功能&#xff0c;帮助开发者实现不同字符编码之间的转换。本文将详细讲解System.Text.Encoding类的使用方法&#xff0c;包括常用编码的介绍…

Pre-flash和Main flash

在相机拍照过程中&#xff0c;Pre-flash&#xff08;预闪光&#xff09; 和 Main flash&#xff08;主闪光&#xff09; 是常见的两种闪光灯使用模式&#xff0c;通常用于提高低光环境下的拍摄质量&#xff0c;尤其在自动曝光&#xff08;AE&#xff09;和自动对焦&#xff08;…

Kafka 4.0 发布:KRaft 替代 Zookeeper、新一代重平衡协议、点对点消息模型、移除旧协议 API

KRaft 全面替代 ZooKeeper Apache Kafka 4.0 是一个重要的里程碑&#xff0c;标志着第一个完全无需 Apache ZooKeeper 运行的主要版本。 通过默认运行在 KRaft 模式下&#xff0c;Kafka 简化了部署和管理&#xff0c;消除了维护单独 ZooKeeper 集群的复杂性。 这一变化显著降…

SFT实验报告

大模型微调实验报告* 实验目标 梳理大模型微调方法&#xff0c;评估各种基座和微调方法的实验效果。 基础模型 \1.Llama \2.Qwen \3.Chatglm4 \4. 微调策略 LoRA系列 低秩适配&#xff08;LoRA&#xff09;的核心思想是冻结原始参数&#xff0c;通过低秩分解引入可训…

LLM - R1 强化学习 DRPO 策略优化 DAPO 与 Dr. GRPO 算法 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/146533892 在强化学习算法中&#xff0c;DAPO (Decoupled Clip and Dynamic Sampling Policy Optimization)&#xff0c;通过解耦裁剪和动态采样策…

美摄科技智能汽车视频延迟摄影解决方案,开启智能出行新视界

在智能汽车时代&#xff0c;车载影像技术正以前所未有的速度发展&#xff0c;成为提升驾乘体验和满足用户多样化需求的关键因素。美摄科技凭借其卓越的技术实力和创新精神&#xff0c;推出了智能汽车视频延迟摄影解决方案&#xff0c;为智能汽车行业带来了一场视觉盛宴。 一、…

[250325] Claude AI 现已支持网络搜索功能!| ReactOS 0.4.15 发布!

目录 Claude AI 现已支持网络搜索功能&#xff01;ReactOS 0.4.15 发布&#xff01; Claude AI 现已支持网络搜索功能&#xff01; 近日&#xff0c;Anthropic 公司宣布&#xff0c;其 AI 助手 Claude 现在可以进行网络搜索&#xff0c;为用户提供更及时、更相关的回复。这项新…

代码规范之Variable Names变量名

代码规范之Variable Names变量名 golang中 官方文档&#xff1a;https://go.dev/wiki/CodeReviewComments#variable-names Variable names in Go should be short rather than long. This is especially true for local variables with limited scope. Prefer c to lineCoun…

Mybatis_plus

前言 Mybatis_plus 是在 mybatis 的基础上进行了增强&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。本文章只做简单的使用介绍&#xff0c;更加详细的内容大家可以参考官网。 下面是mybatis_plus 官网地址&#xff1a; mybatis_plu…

深圳问顶安全科技有限公司asktopsec是做什么的?

深圳问顶安全科技有限公司&#xff0c;是一家专业的AI与应用安全公司。 全球领先的AI、Android、IOS应用安全解决方案提供商&#xff0c;官网&#xff1a;https://asktopsec.com 问顶安全主要为企业提供AI和应用安全服务 移动应用安全检测、移动应用安全加固、AI智能体安全、AI…

鸿蒙OS 5 架构设计探秘:从分层设计到多端部署

文章目录 鸿蒙OS架构设计探秘&#xff1a;从分层设计到多端部署一、鸿蒙的分层架构设计二、模块化设计的精髓三、智慧分发设计&#xff1a;资源的动态调度四、一次开发&#xff0c;多端部署的实践总结与思考 鸿蒙OS架构设计探秘&#xff1a;从分层设计到多端部署 最近两年来&a…

idea 没有 add framework support(添加框架支持)选项

在 IntelliJ IDEA 2023 中&#xff0c;若需通过设置手动添加 “添加框架支持” 菜单项&#xff0c;可按照以下步骤操作&#xff1a; 手动添加 “添加框架支持” 菜单项 打开设置 点击顶部菜单栏的 File&#xff08;文件&#xff09; -> Settings&#xff08;设置&#xff09…

计算机网络--传输层(2)

传输层核心机制深度解析 一、可靠传输实现机制 1. 校验和机制 技术原理&#xff1a; 使用16位二进制反码求和算法&#xff0c;计算范围包括TCP伪首部&#xff08;12字节&#xff09;、TCP首部&#xff08;20字节&#xff09;和数据部分接收端重新计算校验和&#xff0c;若与…

再探带权并查集

典型例题 Acwing 权值 故名思义&#xff0c;在带权并查集中&#xff0c;我们需要让每个节点携带一个**“权值”**。 那么这个权值应该是什么呢&#xff1f;其实答案就在并查集当中。 由于在并查集当中我们可以在 O ( 1 ) O(1) O(1) 时间内找到一个节点的根节点&#xff0c;那…

Vala编成语言教程-构造函数和析构函数

构造函数 Vala支持两种略有不同的构造方案&#xff1a;我们将重点讨论Java/C#风格的构造方案&#xff0c;另一种是GObject风格的构造方案。 Vala不支持构造函数重载的原因与方法重载不被允许的原因相同&#xff0c;这意味着一个类不能有多个同名构造函数。但这并不构成问题&…

本地部署Stable Diffusion生成爆火的AI图片

直接上代码 Mapping("/send") Post public Object send(Body String promptBody) { JSONObject postSend new JSONObject(); System.out.println(promptBody); JSONObject body JSONObject.parseObject(promptBody); List<S…

python爬虫WASM

WASM 一.WASM简介 1.1 WASM定义 ​ WebAssembly(简称wasm)是一个虚拟指令集体系架构(virtual ISA),整体架构包括核心的ISA定义、二进制编码、程序语义的定义与执行,以及面向不同的嵌入环境(如Web)的应用编程接口(WebAssembly API)。是一种运行在现代网络浏览器中的…