JVM內存分配和回收

本文內容來自《Java編程思想(第四版)》第二章《一切都是對象》和第五章《初始化與清理》。做爲一個使用了好幾年的Javaer,再次看編程思想的前面章節(不要問我爲何用再,儘管我第一遍看的啥,一點都不記得了。)編程

-----------------正文分割線---------------------微信

一個程序須要在計算機中運行,其本質是CPU操做內存中[1]的數據,進行某些運算的過程。因此這個問題是,計算機是如何操做這些數據的。網絡

要解答這個問題,必須知道1.這些數據指的是什麼?2.這些數據是如何存儲在內存中的?數據結構

  1. 這些數據指的是程序在算出指望結果過程當中,所須要的一些信息,包括數據自己(多是文本,圖像等,但這些都是毫無例外的用二進制形式存儲的),以及計算過程當中產生的中間值,包括一些內存地址,空白的信息(佔坑用)等,總之他們都是有結構的被存儲在內存中的二進制。[2]
  2. 這是個大問題,要分步來解答。

a)   數據能夠存在哪裏?this

l  寄存器。存在這裏的讀取和寫入速度都是最快的,由於寄存器是CPU的一部分,而CPU是幹活的所有力量,因此CPU和寄存器的交互速度很是快,比內存快上百來倍(未考證)。至於爲何寄存器更快呢?由於寄存器用的是SRAM,而內存是DRAM,SRAM貴多了。至於爲何SRAM比DRAM快,這個我也不知道(喂,在這麼下去,要跑題了)。spa

堆棧:堆棧是存在內存的。用的是棧的數據結構,它快的緣由是棧的空間分配速度很快,只須要將指針上移下移就好了。(這種上下其手的事情慢了怎麼行。)指針

:堆也是放在內存的,並且只比堆棧少一個字!不過它的速度慢多了,這是由於它是堆的數據結構,堆天生就比棧慢。可是堆棧太大了效率就吃不消,你去試試對樂山大佛·上上下下啊!而堆就不同了,能存的東西就多多了。對象

l  其餘:包括常量堆,流、持久化對象(這個是存在磁盤和網絡上的)。內存

b)   而後呢?(如何操做存在內存的數據)資源

對內存中數據的操做,彙編是直接操做的,所以你能夠在組成原理中看到,內存尋址是一塊很重要的內容。C/C++是採用「指針」來間接操做的,它要負責內存的分配和回收,例如malloc就是要內存的節奏。

在Java中,一切都是對象。對象有可能很龐大也可能很小。Java將對象的引用存儲在棧上,這樣能夠快速找到定位,將實際的對象存儲在堆中。引用的本質是內存地址,它直接指向堆裏相應的對象。因此你在Java中輸出this的時候,其實輸出的是Java對象的內存地址。

前面說了,堆的操做比較慢。可是實際上,Java的堆並不慢,根據編程之美的說法,Java堆的效率能夠媲美某些語言(我也不知道某些是哪些)的棧。這主要得益於JVM良好的垃圾回收機制。

c)   JVM垃圾回收機制

程序在運行過程當中,須要不斷的申請新的內存空間,而後釋放掉不用的對象。程序刪除不用的內容後,原來這些內容佔着的坑(內存空間)就會空出來。如此便會形成大量的內存碎片存在。內存碎片會下降內存使用率,並致使一些大內存對象的分配顯得困難。整合這些內存碎片,是很是消耗資源的行爲(可是又不得不整合)。JVM的內存回收機制(垃圾回收機制)乾的就是這個活。

那麼,JVM是如何進行垃圾回收的呢?

簡言之,就是根據某些策略,找到那些不用的對象,並將它們釋放掉。固然若是能順便解決內存碎片就更好了,麼麼噠。

d)   如何找到失活對象?(根據什麼策略呢)

有個理論上很是簡單的方式就是,若是一個對象被引用了,那麼給它的計數增長1,對象被釋放掉了,就減去1,那麼當一個對象的引用是0的時候,他就絕不留情的被垃圾回收機制回收了。這個方式叫作「引用計數」。可是這個方式有個致命的問題:當兩個對象相互引用的時候,計數就永遠不爲0,即使這些對象是須要被釋放掉的。

因此,JVM就不用這個方式啦。

JVM的作法是:遍歷棧裏的全部引用,有引用的對象即是活的對象,沒引用的對象,就是被當作孤魂野鬼來處理。爲了解決內存碎片的問題,JVM使用了一種叫作「中止-複製(stop-and-copy)」的作法,即暫停當前程序,而後將活的對象複製到另外一片內存區域;如此,既解決了內存碎片(新複製的對象在內存上是連續的),有解決了垃圾回收(沒有引用的對象就被拋棄了)。可是這樣子,效率上並不過高,另外,還須要一大片內存來作備胎。另外,程序穩定運行後,內存碎片可能並很少。

這時候,JVM的另外一個套機制,叫作標記-清掃(mark-and-sweep)的方式。這種方式也是從棧出發,遍歷全部引用,爲有引用的對象標記存活標記;標記以後,再開始清掃,即清理沒被標記的對象。這時候獲得的結果,其內存是不連續的。

JVM會監視內存使用狀態,在程序穩定的時候,啓動標記-清掃方式,當堆碎片過多的時候,啓動中止-複製方式。JVM管這個過程叫作自適應的垃圾回收機制。

在上面所述的過程當中,內存是以爲單位進行分配的。較大的對象會佔據一整個塊。每一個塊都有一個參數叫作代數(generation count),標記其是否存活。對於大型對象,在中止-複製過程當中,也不會被再複製一遍,只是代數增長。(這個是爲了減小複製的內容,從而減小內存佔用。)



[1] 也多是寄存器中

[2] 這個解答是我瞎編的,並不是來自編程思想一書,請你們踊躍找錯。

 
----------------------
個人微信公共帳號: DesertToOcean,歡迎關注。
相關文章
相關標籤/搜索