iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0

一般來說,我們在執行Java程式碼時,主要會經過以下階段:

  1. 開發程式碼,按下執行。
  2. 編寫的.java檔案,被編譯器(javac)編譯成位元碼(byteCode),檔名是.class。
  3. 轉換成.class後JVM才能使用,JVM會讀取.class檔並轉換成可以在各種作業系統上可執行的機器語言。
  4. 印出結果。

除此之外,JVM在讀取byteCode,會自動去偵測程式中哪些方法頻繁被使用的,並針對這點去優化生成本機碼,這段是由JIT去執行的,JIT會針對被使用到多次的程式碼片段進行效能上的優化,這也是為什麼常常Java被人說需要先熱機。

講到JVM,我們就不得不提到Java的垃圾回收機制。

垃圾回收器的功用是為了將”沒被使用到”的資源回收,但怎麼去定義沒有被使用到的資源呢?最簡單的概念是沒有被引用到的資源,但怎樣算是沒有被引用到呢?這部分各程式語言都有不同的做法與認定的方式。以下僅簡單地用Python與Java來舉例:

Python的GC,是在Python內部建立一張判斷對象被引用的計數表,如果沒有被引用到 (count=0)時,GC就會把資源回收。它的好處是簡單直覺,效能高。( reachable vs unreachable )
但這種作法有兩個壞處,一個是計數器必須要找一個地方去存放(有多少份資源就有多少存入多少計數器),另一個問題是無法解決互相參照的情況。(ex:a = b, b = a,Python判斷有被參照到所以不回收)

Java的GC則比較不一樣, Java會自動挑出GC root(根節點),然後針對底下的物件去遍歷,若是沒有被遍歷到的則被視作Grabage,詳細可以參考以下這張圖。(referencable vs referencable )

https://ithelp.ithome.com.tw/upload/images/20240918/20168791aqGR4LnrY6.png

這很大的一個程度上解決了Python GC的第二項缺點,但也代表一件事,Java在啟動時間會從根節點遍歷所有關連到的程式碼,這有一部分導致了Java沒辦法像其他程式一樣快。另外要提醒的是,GC清除資料的時間點是由JVM自己決定的,若是需要手動清除,則要使用 System.gc(); 手動清除垃圾。

而JVM針對清除垃圾的方式,也有不同的策略,請見以下說明:

  1. 標記清除算法 (Mark-sweep) GC 掃描過後,將要被回收的資源加入標記,記憶體資源會在後續被JVM自動回收。

https://ithelp.ithome.com.tw/upload/images/20240918/20168791ZwpPEnlJty.png

  1. 標記並整理(Mark-Compact):
    做法與上一個一樣,只是會將不連續的空間整理起來,避免內存零散。
  2. 標記並複製(Mark-Copy):
    把儲存空間分成兩份,一半用完後放到另外一半,優點是很快,缺點是空間少了一半,必須要經常GC。 可以想成 標記並整理 + 移動到另外一個區域。

https://ithelp.ithome.com.tw/upload/images/20240918/20168791Fcak5G57YH.png

以上,是有關GC內容,我的一些吸收與理解。希望能幫助其他人更好理解Java的運作。

參考資料:

https://www.gss.com.tw/eis/271-eis109/3513-eis109_9

https://www.jyt0532.com/2020/03/12/garbage-collector/


上一篇
[Day 3] 所以我說那個hashCode()到底是甚麼鬼?
下一篇
[DAY 5] Java Stream&Optional 介紹與使用
系列文
週日時在做什麼?有沒有空?可以來寫SpringBoot嗎?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言