深入解析 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.pswp.cn/news/899166.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899166.shtml
英文地址,請注明出處:http://en.pswp.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)。是一種運行在現代網絡瀏覽器中的…