java虛擬機器介紹以及垃圾回收
一直對jvm看了又忘,忘了又看的。今天做一個筆記整理存放在這裡。
我們想象一個場景:
我們有一個class檔案,裡面有很多的類的定義是不是,類的定義放在什麼地方呢?類的定義就放在方法區裡面。
程式在執行時會產生很多的物件,比如LinkList等這些物件就存放在堆裡面。
棧:
我們的程式在執行時本質上就是啟動執行緒在執行,比如main函式就是一個主執行緒。一個main主執行緒可以有很多的子執行緒。
執行緒在工作就是執行我們的各種方法。方法裡面肯定有我們自己定義了一些區域性的變數,比如我們在方法裡面new了一個物件,物件肯定是放在堆裡面的,但是對堆的引用我們就放在棧裡面。那麼棧就有問題了,我們一個程式有很多的執行緒,如果把所有的執行緒裡面的變數存放在一起,肯定有會有變數是重複的,衝突。所有不能放在一起。所以棧裡面是分執行緒來存放的。每一個執行緒都是自己的棧空間,執行緒私有的。堆是執行緒共享的。
棧裡面有一個細節:
就是說一個棧空間是以不同的執行緒區分開來。每個執行緒有自己棧,每個執行緒裡面又會執行很多的方法,每一個方法對應一個棧幀:
每個方法執行時都會建立一個棧幀(Stack Frame)用語儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。從下圖從可以看到,每個執行緒在執行一個方法時,都意味著有一個棧幀在當前執行緒對應的棧幀中入棧和出棧。
圖中可以看到每一個棧幀中都有區域性變量表。區域性變量表存放了編譯期間的各種基本資料型別,物件引用等資訊。
好了,我們看一下具體的東西:
本地方法棧(Native Stack)
本地方法棧(Native Stack)與Java虛擬機器站(Java Stack)所發揮的作用非常相似,他們之間的區別在於虛擬機器棧為虛擬機器棧執行java方法(也就是位元組碼)服務,而本地方法棧則為使用到Native方法服務。
堆(Heap)
對於大多數應用來說,Java Heap是Java虛擬機器管理的記憶體的最大一塊,這塊區域隨著虛擬機器的啟動而建立。在實際的運用中,我們建立的物件和陣列就是存放在堆裡面。如果你聽說執行緒安全的問題,就會很明確的知道Java Heap是一塊共享的區域,操作共享區域的成員就有了鎖和同步。在程式的執行中不斷地new 物件,就存在堆裡面。 與Java Heap相關的還有Java的垃圾回收機制(GC),Java Heap是垃圾回收器管理的主要區域。程式猿所熟悉的新生代、老生代、永久代的概念就是在堆裡面,現在大多數的GC基本都採用了分代收集演算法。如果再細緻一點,Java Heap還有Eden空間,From Survivor空間,To Survivor空間等。 Java Heap可以處於物理上不連續的記憶體空間中,只要邏輯上是連續的即可。
上面這張圖就是我們新生代,老年代的圖。涉及到垃圾回收機制。
我們來介紹一下垃圾回收機制:
剛開始時建立的物件都存放在Eden Sapce裡面(伊甸園,新生代),大家都無憂無慮的。然後呢 ,垃圾回收機制來了。先對生活在伊甸園的物件檢測一次,發現還有利用價值就把物件放在Survivor空間裡面的From Space,survivior空間分為兩塊,一塊是From Space,一塊是ToSapce空間。ToSpace空間是很少用的(就是用來做垃圾回收的挪動的)。然後垃圾回收機制對From Space的物件回收比如18次,還能倖存下來,就放在老年代裡面(Old Generation)。我們很奇怪,這裡的ToSpace是幹嘛用的。
好了我們來介紹下垃圾回收演算法:
有 兩種演算法。
1:物件A有沒有引用物件B.有引用就不是垃圾,這種方法有問題。A,B相互引用,就不能區分出是不是垃圾了。
2:根節點搜尋。從根節點往下搜尋。能搜尋到了就不是垃圾,不能搜尋到的,就是垃圾。
這是由對映表記錄的,再詳細就不知道了。
物件其實就是一個方格:
如上,紅色是好的物件,黑色的是垃圾物件。
我們怎麼去回收黑色的物件呢:
方法一:標誌-清除:
直接把黑色的物件清除掉。
但是有一個問題:這樣清除掉的惡化,我的空間變得很零散。下次要放一個大的物件的話(必須是一個連續的空間)比如四個格子,就放不下去了。
方法二:標記整理:
我只移動有用的:把紅色的有用的物件移動到白色的空白地方。但是造成的問題是:對在執行的程式有影響。
方法三:
複製演算法:
預先存留一塊有用的的空間:那就是上文提到的ToSpace空間。
在FromSpace空間中,垃圾回收機制把沒用的的物件標記成黑色的,然後把紅色的有用的物件都轉移到右邊的ToSpace空間,等到垃圾回收機制把左邊的黑色和紅色全部清除之後。再把右邊的ToSpace的那些紅色有用物件再移植到左邊的FromSpace空間。這樣就整齊了。
然後我們會想一個問題。那右邊給他預留這麼大的空間不是很浪費嗎?其實實際上右邊的空間不需要那麼大,只要一點點就夠了。如下圖
因為我們的有用的物件(紅色的方塊)其實是很少的。很多物件用好一次就不用了的。
方法區(Method Area)
方法區(Method Area)與堆(Java Heap)一樣,是各個執行緒共享的記憶體區域,它用於儲存虛擬機器載入的類資訊,常量,靜態變數,即時編譯器編譯後的程式碼等資料。雖然Java虛擬機器規範把方法區描述為堆的一個邏輯部分,但是她卻有一個別名叫做非堆(Non-Heap)。分析下Java虛擬機器規範,之所以把方法區描述為堆的一個邏輯部分,應該覺得她們都是儲存資料的角度出發的。一個儲存物件資料(堆),一個儲存靜態資訊(方法區)。
在上文中,我們看到堆中有新生代、老生代、永久代的描述。為什麼我們將新生代、老生代、永久代三個概念一起說,那是因為HotSpot虛擬機器的設計團隊選擇把GC分代收集擴充套件至方法區,或者說使用永久代來實現方法區而已。這樣HotSpot的垃圾收集器就能想管理Java堆一樣管理這部分記憶體。簡單點說就是HotSpot虛擬機器中記憶體模型的分代,其中新生代和老生代在堆中,永久代使用方法區實現。根據官方釋出的路線圖資訊,現在也有放棄永久代並逐步採用Native Memory來實現方法區的規劃,在JDK1.7的HotSpot中,已經把原本放在永久代的字串常量池移出。