kyle shanks
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: 'Java 自動管理內存(HotSpot)、創建物件' disqus: kyleAlien --- Java 自動管理內存(HotSpot)、創建物件 === ## OverView of Content 高級語言 Java 跟 C、C++ 的一大差異就在於對於記憶體的管理方式,Java 透過 JVM 自動幫你管理記憶體,而 C、C++ 則需要手動申請 (new)、釋放 (delete) [TOC] ## Java 運行時數據區域 JVM 在執行 Java 應用時會將內存分為多個區塊,每個區域都各有用途 (請注意顏色,綠色為 thread 私有) > ![](https://i.imgur.com/6mQrXyB.png) ### 程式計數器 - Program Counter Register * **每個線程都`私有`**,Java 天生為多線程程序所以必須紀錄每個 Thread 當前運行到哪裡,並且只需要較小的記憶體空間(記憶當前 thread 的行號 `Line`) :::info * **何為指令**? 在 JVM 模型概念 (類似協定) 中,字解碼解釋器改變計數器,來決定要讀取的字解碼指令 > 字解碼指令像是:分支、循環、異常處理... 等等 > > 該指令類似於 CPU 指令,不過該指令只針對 JVM Runtime 給虛擬機使用! ::: * 此區塊是 **唯一不受 Java 虛擬機規範所以 ++不會有 OutOfMemoryError 錯誤產生++** > Thread 正在執行 Java 方法,計數器記錄的是 ++正在執行的虛擬機器位元組碼指令地址++ :::success * **Native method 是否紀錄?** 如果 **執行的是Native方法,計數器值則為空(`Undefined`)**,因為其不是由 Java 呼叫,而是本地呼叫(所有 JVM 管不著) ::: ### 虛擬機棧 - VM Stack * **每個線程都有各自 `私有` 的 Stack**,其 **空間遠小於堆** > 這種規劃分式類似於 C、C++ 對於內存規劃的方式(有堆區、棧區) * 每呼叫一個 Java 方法,JVM 就會產生一個 **棧禎 (Stack Frame)** 放入 Stack 中,棧禎 (Stack Frame) 方法如下 | 棧禎 紀錄類型 | 說明 | 補充 | | -------- | -------- | - | | 區域性變量表 | Java 基礎八大數據、Object 引用 | 儲存局部變量的一張表 | | 數據操作棧 | 存放分法執行的操作數據,**做出入棧的動作** | 操作的元素可以是任意的 Java 數據 | | 動態連結 | **Java 的多態**,動態特性等等(這樣 Java 這種靜態語言擁有動態語言的特性) | 在編譯期間不確定的程式跳轉(多型) | | 返回出口 | 調用計數器的地址,返回其結果 | 調用程序計數器中的地址作為返回 | :::info * **調整 Stack 大小** > 可用 `-Xss` 調整大小,Ex: -Xss512k (預設取決於系統) ::: :::success * **區域性變量槽 (Slot)** 1. 在儲存局部量量時,是使用 **局部變量槽 (Slot)**,這個槽的大小是 JVM 決定(有可能是 32、64 bit),像是 `double`、`long`... 等等 就是使用兩個槽來儲存 2. 局部變量表所需要的內存空間是在 **++編譯完成後就決定++**,也就是說 **局部變量表的大小是不能動態調整的** > 局部變量表在編譯期間就決定 ::: * JVM 規範中,VM Stack 操作失敗如下: | 產生異常 | 原因 | 發生點 | | -------- | -------- | - | | **StackOverflowError** | **Stack 深度** 大於 JVM 規範的深度 | 容易發生在 **遞迴** | | **OutOfMemoryError** | JVM 容量是可以動態拓展的,但動態拓展失敗 | Java 應用申請記憶超出規範大小 | ### 本地方法棧 - Native Method Stack * **每個 Thread 都有各自`私有`**,與 VM Stack 類似,不過一個是為 Java(`Byte Code`) 服務,一個是為 Native 方法服務 > ![](https://i.imgur.com/ux60Sgb.png) :::info * **Native Method Stack 儲存的訊息** Java 對於本地方法棧的 `語言`、`數據結構` 並沒有硬性規定,因此具體由 Java 虛擬機自己實現它 ::: * 本地方法棧與虛擬機器棧所發揮的作用非常類似,最大的不同在於 **虛擬機器棧用於 Java方法** 的呼叫,而 **本地方法棧則用於本地方法** 的呼叫,也就是由不同的方式呼叫 ```java= // Java 方法 public void showJavaVersion(); // Java 呼叫 Native 方法 (Native 關鍵字) public native void showNativeVersion(); ``` * JVM 規範中,**Native Method Stack 操作失敗如下**:(同 VM Stack) | 產生異常 | 原因 | 發生點 | | -------- | -------- | - | | **StackOverflowError** | **Stack 深度** 大於 JVM 規範的深度 | 容易發生在 **遞迴** | | **OutOfMemoryError** | JVM 容量是可以動態拓展的,但動態拓展失敗 | Java 應用申請記憶超出規範大小 | ### 堆 - Heap * **各執行緒 `共享` 其內容**,在 **虛擬機起動時建立** **此區塊的目的是==存放物件實例==**,此區塊使用的記憶體容量也是最大,**==GC 的主要區塊==** :::info * **JVM Heap 分配可通過以下 Options 調整 JVM 堆區的設置** | Optioins 指令 | 調整目標 | | -------- | -------- | | -Xmx | 堆的最大值 | | -Xms | 堆的最小值 | | -Xmn | 新生代的大小 | | -XX:NewSize | 新生代最小值 | | -XX:MaxNewSize | 新生代最大值 | ::: :::warning * Java 對象 **全部都放置在堆區 ?** **No!** 只能說 **大多數的對象都放置在堆區**,由於編譯技術的進步,有部分對象可以放置到 Stack 區塊 (逃逸分析) ::: * JVM 規範中,**Heap 操作失敗如下**: | 產生異常 | 原因 | 發生點 | | -------- | -------- | - | | **OutOfMemoryError** | JVM 容量是可以動態拓展的,但動態拓展失敗 | Java 應用申請記憶超出規範大小 | :::success * **堆區是一整塊大記憶體**? Java 為了要更有效率的回收對象,會 **將 Heap 劃分為多個區塊**,但不論怎麼劃分,Heap 仍是所有 thread 共用區域 > 對於堆區的規劃以 JVM 實現為主;像是 HopSpot VM 來說就使用了經典分代 (其他小節會說) 重點是 **堆區是共用區、可以不連續(但邏輯上是連續的)的特性**! ::: ### 方法區 - Method Area * **所有 thread `共享` 內容**,也稱為 **非堆區 (`Non-Heap`)**,方法區儲存的目標有如下 | 儲存目標 | | - | | 加載的類資訊 class (二進制) | | 常量 final | | 靜態變數 static | | 即時編譯程式 | * 在 JVM 的規範中,是 **++可選擇++ 不回收 `方法區` 的記憶體,如果要回收,方法區的回收條件也是相當嚴苛的**,像是對於類的卸載 > 對方法區的規範相對式寬鬆的 :::info * Class 檔案訊息 Class 檔案中除了有類的 `版本`、`欄位`、`方法`、`介面` ... 等描述資訊外,還有一項資訊是 `常量池`,用於**存放編譯期生產的各種字面量和符號引用**,這部分內容將在類載入後進入方法區的執行時常量池 ::: ==**運行時常量區**==:**它用來存放編譯期間生成的 ++字面量、符號引用++,並非在編譯時才能產生,也 ++可以在運行時產生++** > 類加載時編譯時的鏈結 :::warning * **方法區 & 永久代 一樣**? **有許多人會習慣把分法區稱為永久代,但其實兩者並不相等**,**永久代是 HotSpot 團隊對於方法區的一種實現** > 像是 JRockit 就不存在永久代。 1. JVM 類的加載過程是,**`加載`->`驗證`->`準備`->`解析`->`初始化`->`使用`** 2. 上面可以分析出類的版本、字段、方法、接口等等的描述,還包括 **常量池 (`constant pool table`),用於存放編譯期間生成的各種字面常量、符號引用** ```java= // 以下是編譯期間生成的常量 String a = "b"; final String c = "d"; // 符號引用 (類、方法的全限定名) Java/lang/String; ``` 3. 在解析接段,JVM 會把符號引用轉為直接引用,多個類共用運行時常池,在常量池中只會存在一份 4. 方法區 & 堆 都是共享空間 ::: * JVM 規範中,**Method Area 操作失敗如下**: | 產生異常 | 原因 | 發生點 | | -------- | -------- | - | | **OutOfMemoryError** | JVM 容量是可以動態拓展的,但動態拓展失敗 | 方法區無法分配新的記憶體空間時 | :::info * **元空間**:方法區永久代的更新 Java7 版本:中已經 **將永久代的靜態變量 & 運行時常量轉移到堆中** Java8 版本:已經將方法區中的永生代去除,並改用 **元空間 (class matadata) 儲存並且位置在本地** ::: ### 直接記憶體 (直接內存) - Direct Memory * 直接記憶體並**不是虛擬機器執行時資料區**的一部分,**也不是 Java 虛擬機器規範中定義的記憶體區域,==NDK 就屬於這一區塊==** * 此區塊記憶體的分配 **++不會受到 Java 堆 大小的限制++**,但是 **會受到本機總記憶體**以及處理器定址空間的**限制**,**超出時導致 OutOfMemoryError 異常** * 直接記憶體的 **速度會優於 Java堆** > 可以避免 Java 堆、Native 堆中來回複製數據 * 雖然該區塊不受 JVM 堆大小限制,但 **會受到裝置本身內存的限制**;**當直接記憶體操作失敗**: | 產生異常 | 原因 | 發生點 | | -------- | -------- | - | | **OutOfMemoryError** | 由系統產生 | 系統的記憶體區無法分配新的記憶體空間時 | ### 類加載子系統 * 類載入子系統 **負責從檔案系統中載入 Class 資訊**,載入的類資訊**存放於`方法區`的記憶體空間** ### 回收 GC * 垃圾回收器可以對方法區、Java堆和直接記憶體進行回收 * Java 堆是垃圾收集器的工作重點 ### 執行引擎 * 不同於類加載系統,它 **負責執行虛擬機器的位元組碼** ## HotSpot 創建對象 以下使用最常用(經典)的 HotSpot 虛擬機、內存 Java 堆為例,討論 Java 對象創建在 JVM 運作 :::warning 這裡限於 Java 普通對象 (new 關鍵字),不包括 Array、Class 對象 ::: ### JVM 檢察加載 > 也就是 **ClassLoader 階段**,在這個階段會完成類的載入、連結、初始化 * **檢查常量池**:當 ClassLoader 加載類時,JVM 會收到字節碼,首先先去檢查 **常量池**,**定位類的符號引用**,檢查的過程包括 1. 是否被加載過 2. 是否被解析過 3. 是否初始化過(這會以 ClassLoader 為基準檢查) :::success 經過檢查後,就 **可以確定類的大小** ::: ### JVM 分配內存 - 策略 > 這個階段 ClassLoader 已經加載完類,並 **使用 `new` 關鍵字創建物件** * 內存分配基本有兩種狀態 ^1.^ **內存規整**、^2.^ **內存不規整** 1. **內存規整**:**使用 `指針碰撞` (`Bump The Pointer`)**,使用指標的移動,指針移動一個對象的大小 > ![](https://i.imgur.com/h8JN3pk.png) > > 綠色: 已用 / > > 橘色: 新對象 / > > 白色: 目前無使用(可用) 2. **內存不規整**:JVM 會維護一個 **空閒列表 (`Free List`)**,並在 **需要時搜尋到足夠空間讓其使用**(JVM 就像是停車場管理員一樣) > ![](https://i.imgur.com/aPdQz3S.png) > > 綠色: 已用 / > > 橘色: 新對象 / > > 白色: 目前無使用(可用) :::info * JVM 記憶體到底採用哪個呢?**內存是否規整與 GC 策略有關**! 1. 如果使用的 GC 包含 `空間壓縮 Compact` 的能力,那 JVM 就會使用 `指針碰撞` > Eg. Serial、ParNew 2. 如果 GC 使用 CMS `標記清除 Sweep` 算法,就必須使用 `空閒列表` > ![](https://i.imgur.com/V0yADMU.png) ::: ### 內存空間初始化、JVM 設定對象 * 把 **規劃完的記憶體空間全部賦值為 `0`、`false`**... 等等預設值,這也就是為何 Class member 不初始化的參數也會為 0,但是 ==**對象頭尚未初始化**== > 這個階段是記憶體的初始化,**尚未與類相關訊息連接** :::warning * 如果使用 TLAB 機制,那初始化行為就會移動到分配內存的階段 ::: * 在分配完內存、初始化後,JVM 就會 **對該對象設定必要的數據** 1. 該對象(記憶體)是 **哪個類的實例** 2. **類的元數據類型**、對象 HashCode (會延後設定)、對象的 GC 分代 3. 是否有使用偏向鎖... 等等 :::info * 到該這個步驟後,對象在 JVM 中就算創建完成 (對於 Java 來說尚未完成) ::: * 當上述的行為都結束後,**執行該對象的 Constructor 建構方法** (也就是 Class 文件中的 `<init>()` 方法),並且且所有 Field 莫認為 0 ### JVM 分配內存 - 併發競爭 > 這裡細節提及一下內存是如何分配的,而在多線程併發中又會有哪些問題、JVM 如何解決 * 由於 Java 有多線程特性,所以在分配內存時需要特別注意 **==併發問題==**,JVM 也有多線程的內存分配方案 1. **CAS (`Compare and swap`) 同步機制**: 操作時會輸入兩個值 (`Old_A` & `New_B`),**在操作其間會比較 A & B 值有沒有變化**,直到 A == B 才會交換成 B 值 > JVM 採用 CAS + 失敗重試的方式保證更新操作的 **原子性** ```java= // 概念程式 int cas(int oldVal, int newVal) { int result = oldVal; while(true) { if(oldVal == newVal) { // 比較真正設定完數據才跳出 while result = newVal; break; } } return result; } ``` 2. **TLAB (`Thread Local Allocation Buffer`)** 本地線程分配緩衝: 在 **JVM Heap** 中先分配一塊內存給 Thread,只有當 Thread 使用完堆空間時才需要同步 :::info * JVM 是否使用 TLAB 機制可以使用 `-XX:+UseTLAB` 參數來控制 ::: * **HotSpot 解釋器對於創建新物件的指令的解釋**: 一般來說使用 `new` 關鍵字會產生兩條指令字節碼(`new` & 呼叫建構函數的 `<init>()`);接下來我們看看 JDK12 中的字節碼解釋器(`bytecodeInterpreter.cpp`) ```c= // bytecodeInterpreter.cpp CASE(_new): { u2 index = Bytes::get_Java_u2(pc+1); ConstantPool* constants = istate->method()->constants(); // 判斷 Class 已經被加載 if (!constants->tag_at(index).is_unresolved_klass()) { // Make sure klass is initialized and doesn't have a finalizer Klass* entry = constants->resolved_klass_at(index); InstanceKlass* ik = InstanceKlass::cast(entry); // 判斷類是否已經初始化 if (ik->is_initialized() && ik->can_be_fastpath_allocated() ) { // 已初始化 size_t obj_size = ik->size_helper(); oop result = NULL; // 是否需要將物件所有字段至為 0 // If the TLAB isn't pre-zeroed then we'll have to do it bool need_zero = !ZeroTLAB; if (UseTLAB) { // 是否在 TLAB 分配物件記憶體 result = (oop) THREAD->tlab().allocate(obj_size); } #ifndef CC_INTERP_PROFILE if (result == NULL) { // 不使用 TLAB need_zero = true; // Try allocate in shared eden retry: HeapWord* compare_to = *Universe::heap()->top_addr(); HeapWord* new_top = compare_to + obj_size; // 尚未分配完成 if (new_top <= *Universe::heap()->end_addr()) { // 使用 CAS 機制 if (Atomic::cmpxchg(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) { // 如果尚未分配記憶體,跳回 retry goto retry; } result = (oop) compare_to; } } #endif // 物件記憶體分配成功! if (result != NULL) { // 判斷是否需要初始化 if (need_zero ) { HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize; obj_size -= sizeof(oopDesc) / oopSize; if (obj_size > 0 ) { // 出使化物件記憶體空間為 0 memset(to_zero, 0, obj_size * HeapWordSize); } } // 判斷是否有偏向鎖 if (UseBiasedLocking) { result->set_mark(ik->prototype_header()); } else { result->set_mark(markOopDesc::prototype()); } result->set_klass_gap(0); result->set_klass(ik); // Must prevent reordering of stores for object initialization // with stores that publish the new object. OrderAccess::storestore(); // 儲存物件到 Stack SET_STACK_OBJECT(result, 0); UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); } } } // Slow case allocation CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index), handle_exception); // Must prevent reordering of stores for object initialization // with stores that publish the new object. OrderAccess::storestore(); SET_STACK_OBJECT(THREAD->vm_result(), 0); THREAD->set_vm_result(NULL); UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); } ``` ## 物件 - 內存設計 **物件在 JVM 堆中持有的內存分配策略**:在 HotSpot 實現的虛擬機中分為 **3 個部分** 1. **對象頭 `Header`** 2. **實例數據 `Instance Data`** 3. **對齊填充 `Padding`** > ![](https://i.imgur.com/zuc8p7H.png) ### 對象頭 Header * Header 又分兩個部份 > ![](https://i.imgur.com/asmlJm1.png) 1. **對象運行時數據**:又稱 **Mark World** (`HashCode`, `GC`, `Lock Flag`, `thead ID`, `time Stamp` ... 等等數據) 大小為 32、64 bit :::success * 物件運行時數據大小 1. **Header 中的運行時數據,與物件真實儲存的數據無關** 2. 對象運行時數據其實很容易大於 32、64 bit,所以 Mark Word 被設定為與 **動態定義的數據結構**,每個狀態儲存的數據都不同 > 假設 32 bit 空間中:25 bit 為儲存物件的 hashcode、4 bit 儲存分代年齡、2 bit 儲存鎖訊息 | Header 內容 (動態) | 標誌位 (2 bit) | 鎖的類型 | | -------- | -------- | -------- | | HashCode、對象分代年齡 | 01 | 未鎖定 | | 指向所記錄的指針 | 00 | 輕量級 | | 指向重量級鎖的指針 | 10 | 重量級鎖定 | | 空、不須記錄訊息 | 11 | GC 標記 | | 偏向線程 ID、偏向時間戳、分代年齡 | 01 | 可偏向鎖 | ::: 2. **類行指針**:**指向類數據的 ++元數據++ 指針**,**JVM 透過這個指針來確定該對象來自哪個類**,如果對象是數組則會儲存該數組的大小 (如果長度不確定則長度則無法確定) > 指向方法區(物件的 Metadata) :::danger * 類行指針 是一個物件必須 ? 這並不一定,這要看 **JVM 的實現而定**,**查找對象的元數據類型,並不一定需要透過對象本身 !** ::: 在 `markOop.hpp` 的註解如下 > ![](https://i.imgur.com/8KEkb4w.png) ### 實例數據 Instance * Instance 則是儲存該對象的真正實例數據 > ![](https://i.imgur.com/3OxKvqQ.png) 1. **父類**:儲存 Parent Class 的所有訊息 (這其中也包括私有數據,這也就是繼承 `extends` 為何在設計中,代價較為大的原因) 2. **自身**:該類自身的數據 * **儲存數據的順序**:順序會 **依照 Java source code 中的順序而定**,HotSpot 預設儲存順序為 1. `long`/`doubles` 2. `ints` 3. `shorts`/`chars` 4. `bytes`/`boolean` 5. `oop` (Ordinary Object Pointer) 從上面的策略來看,**相同長度的類型會放置在一起** (在 Parent Class 中的數據會在 Child Class 之前出現) :::info * 順序可以透過 `-XX:FieldAllocationStyle` 參數設置 > 此設置會將相同寬度的字段放置在一起 ::: ### 對齊填充 Padding > Padding 並非必須 * **Padding 主要是在填充 `Instance` 數據**,對齊數據 (8 Bit 倍數) 對於 CPU 來說相當重要,因為這有關於 CPU 讀取數據的效率 :::success * 由於 Header 自身設計就是 32、64 bit 所以不需要 Padding > ![](https://i.imgur.com/RwU8nAe.png) ::: ## 物件的訪問定位 通過 **在 JVM Heap 上搜尋對象 `reference`**,透過 `reference` 來操作 Heap 上的數據,目前主流的方式是 `句柄`、`直接指針` > Java 虛擬機規範只有定義 `reference` 是指向對象的引用,實際實現由 JVM 自己決定 | 訪問方式 | 好處 | | -------- | -------- | | 句柄 | 對象被移動時,只會改變句柄池的指向,不會影響 Stack 上的指針 | | 直接指針 | 速度快 | ### 類引用 - 句柄 * [**句柄**](https://baike.baidu.com/item/%E5%8F%A5%E6%9F%84): 在 Heap 中分配一個區塊作為 **句柄池**,而 Stack 中的 `reference` 就是 **指向句柄池**。句柄一般來說是指向一處資源的指針,是一種 **間接指向** > ![](https://i.imgur.com/ygc66Kk.png) ### 類引用 - 直接指針 * **直接指針**: **Stack 中直接存取 JVM Heap 中 `reference` 指針**,可以直接操作對象,其特色是速度較快 > ![](https://i.imgur.com/14wVC1M.png) :::success * HotSpot 大部分選用這種方式訪問對象 ::: ## Appendix & FAQ :::info ::: ###### tags: `JVM`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully