【拒絕一問就懵】之你多少要懂點內存回收機制

背景介紹

  • Java優點之一就是其具備垃圾回收機制。在大部分狀況下,JVM的GC(垃圾回收器)可以幫助咱們回那些不可到達的對象(就是未被引用的對象)。
  • 固然,在一些狀況下,咱們仍然須要本身去釋放內存(就是把對象引用置null,把容器、數組清空),不然就會引發內存泄漏,內存泄漏嚴重時將容易引起OutOfMemoryError,詳情見 【拒絕一問就懵】之不可忽視的內存泄露
  • 此外,因爲GC會中止全部的線程,包括UI線程,因此頻繁的GC必然會致使畫面卡頓(Android中每16ms爲一幀),所以還應避免GC的頻繁發生。一個致使GC頻繁發生的緣由就 是【拒絕一問就懵】之沒據說過內存抖動吧,點擊連接看詳情。
    GC時線程被中止示意圖
  • 因此,理解Java的內存機制,有助於幫助咱們在寫代碼的過程當中避免內存泄漏。

走進內存模型

Java內存層級

JVM運行時內存劃分

JVM運行時內存劃分-詳細
;

程序計數器

程序計數器是線程私有的內存區域,這個區域是Java虛擬機中惟一一個沒有限制OutOfMemoryError的內存區域。之因此須要它是由於Java的多線程機制是經過輪流切換分配處理器執行時間來實現的,因此會涉及到線程的暫停和重啓,而在一個線程中若是正在執行Java方法的話,這個計數器就回去記錄當前正在執行的虛擬機字節碼,一旦被暫停,恢復只須要從程序計數器記錄的爲止繼續執行就能夠。程序員

可是,若是線程中執行的是一個Native方法,那麼程序計數器是不會去記錄的,因此此時的程序計數器爲空。算法

虛擬機棧Stack

Java虛擬機棧也是線程私有的。一條線程啓動就會爲它創建一個虛擬機棧。數組

在線程中每有一個Java方法被調用就會建立一個 「棧幀」 。每一個 「棧幀」 會保存執行該方法所需的局部變量表(通常Java程序員喜歡用這個部分來表明棧)、操做數棧、動態連接以及方法出口等信息。安全

若是一個線程中有過多的 「棧幀」 要入到虛擬機棧中,即短期內調用了過多的方法,就會形成 -- 棧益處 -- ,即 StackOverflowError 錯誤。bash

在這個內存區域中,若是虛擬機須要擴展內存,但沒有申請到足夠的內存,就會拋出 OutOfMemoryError 錯誤。多線程

本地方法棧

和虛擬機棧有些相似,但它是爲Native方法提供服務的。併發

Java堆Heap

Java的堆內存是Java虛擬機所管理的內存中最大的一塊。它是全部線程所共享的,用於存放對象實例和數組,Java虛擬機的GC主要就發生在這個地方。所以這塊區域也叫作"GC堆"。工具

Java堆的內存能夠按照垃圾回收算法【分代回收】分爲【新生代區】和【老年區】,進一步的,【新生代區】能夠分爲【Eden區】、【From Survivor區】和【To Survivor區】。post

從內存角度來講,Java堆內存又被劃分爲線程共享的內存區域和每一個線程私有的內存區域。spa

在Java堆區域中,若是沒有內存分配給要建立的實例,而且堆也不可以再擴展,就會拋出OutOfMemoryError錯誤。

回收算法

Java8以後,Heap Segment真正意義上的是由Young GeneriationOld Generiation組成的。對象在其中是標記複製算法來斷定一個對象是否應該被清理掉。
Heap Segment中發生的GC稱爲Major GC,只會影響Heap Segment區。

內存圖

Young Generiation中的GC變化 — 複製算法

這個區域發生的GC稱爲Minor GC

  • 當對象被建立後,首先會被加入eden區。當eden區滿了以後,就會觸發一次GC,存活下來的對象會被複制到survivor區。
  • 當不爲空的Survivor區滿了,一樣會觸發一次GC。
  • 當短期內有大量對象建立和釋放一樣會形成內存抖動,會觸發CG。
  • 如圖所示,survivor有兩個區域,其中一個老是保持爲空。
  • 現假設兩個Survivor區分別爲S0,S1,而且首次GC時,eden區中存活的對象被複制到S0中。當再次發生GC時,S0和eden中仍然存活的對象就會被複制到空的S1中,此時S0爲空;再次發生GC時,S1和eden中存活的對象將被複制到S0中,此時S1爲空;再次發生GC...就是這樣進行的。當一個對象被來回複製轉移的次數達到閥值(默認爲15次,能夠經過使用-XX:MaxTenuringThreshold該命令來調整閥值)時,這個對象將被複制到Old Generiation區中,此時該對象將會變的相對安全,由於Old Segment區的GC頻率相對較低。

Old Segment中的GC變化

這個區域發送的GC成爲Full GC

  • 該區域滿了以後會觸發一次GC,在該次GC中,一些年齡較大的對象會被清理掉。
  • 若屢次觸發GC後,該區域仍然處於滿的狀態,則會拋出OutOfMemoryError
  • 以兩種狀況下,新建對象會被直接複製到該區域中:
    • 當新建對象所須要的內存大於1/2的單個survivor區內存時。好比一些很長的對象;
    • 當新建對象被該區中的對象引用時,或者引用了該區域中的對象。

方法區

Java的方法區和Java的堆內存同樣是被線程所共有的。它主要存放虛擬機加載的類信息、常量、靜態變量、即時編譯產生的代碼等。

一些地方會將方法區合併到Java堆中一塊兒去說。把它做爲「永久代」。這在Hot-Spot虛擬機而言成立,可是通常來講是不成立的。

Java的方法區若是內存不夠分配的話,也是會拋出OutOfMemoryError錯誤的。也就是若是加載過多類到方法區的話,可能會形成方法區內存益處。

對象的可到達性

在GC檢查對象的是否能夠回收時,是根據對象是否可到達引用練頂端的GC Roots對象來判斷的。GC Roots對象通常是虛擬機棧中變量表中引用的對象、類靜態屬性引用的對象、常量對象、JNI傳到底層的對象。就是說,一個對象若是溯源不到這幾種類型的對象的話,就認爲它是沒法到達的,那麼它將會在GC時被回收。

新的引用類型

在JDK 1.2以後,Java擴充了4種引用類型定義:

強應用類型

即咱們平時經過new關鍵字建立出來的的對象的引用,只要強引用還存在,那麼這些對象就必定不回被回收,即便時拋出OutOfMemoryError。何時強引用會不存在呢?當一個方法執行完,棧幀中的變量表將會被清理,在該方法中建立使用的臨時強引用就會被清理掉,以後,本來它指向的對象就被變的不可到達。

軟引用類型

用來描述一些有用但不是必須的對象,即經過SoftReference建立的對象,它們將會在本來肯定要發生內存溢出前的一次GC中被回收,若是回收完內存仍是不夠,Java堆就會拋出OutOfMemoryError錯誤。就是說,在觸發內存溢出發生前,這些對象是和強引用同樣,只要引用還在,就不會被回收。

弱引用類型

用來描述一些沒必要須的對象,即經過WeakReference建立的對象。弱引用對象的生命週期只有一次GC。

虛引用類型

一個對象的存在與否徹底不受虛引用的影響,它惟一的用處就是能夠用來監測一個對象是否被回收。

方法區中的-運行時常量池

運行時常量池主要存放類中編譯時期生成的常量,固然也能夠動態的往裏面添加。

好比:

"abc".intern();
複製代碼

這個方法首先會檢查運行時常量池中是否有這個字符串,有的話取出來用,沒有的話生成一個並存到常量池中。

再好比,運行過程當中生成經過static修飾的String時,也會加入到常量池中。對於String而言,常量 + 常量 生成的也是常量,可是常量 + 變量 生成的就是變量了。

關於Dalvik虛擬機

Dalvik虛擬機是Google按照JVM虛擬機規範定製的虛擬機,它更符合移動設備的環境要求。與標準虛擬機不一樣:

  • Dalvik編譯生成的是.dex文件,這種格式的文件體積更小。而JVM規範的是.class文件。
  • Dalvik虛擬機是基於寄存器的,而JVM規範是基於棧的,因此速度方面會有優點。好比上面的說的標準Java虛擬機中,它的虛擬機棧就爲線程的運行提供了服務。而Dalvik虛擬機中,使用寄存器去儲存運行指令,同時寄存器也提供了程序計數器。寄存器是處理器的一部分哦!
  • Dalvik虛擬機容許在內存中建立都個實例,以隔離不一樣的應用程序。這樣,當一個應用程序在本身的進程中崩潰後,不會影響其它進程的運行。

關於ART虛擬機

ART虛擬機在Android 5.0之後是被默認開啓的,此時Dalvik已經被Google放棄維護了。它與Dalvik虛擬機的不一樣:

  • ART虛擬機在應用程序安裝時就會把字節碼經過dex2oat工具直接轉成機器碼儲存,這個過程叫作AOT(Ahead-Of-Time)。而Dalvik是在每次啓動應用程序時,經過傳統的JIT(JUST IN TIME)模式將字節碼轉成機器碼。顯然,這樣速度會慢很多。固然,ART虛擬機的佔用內存也會更大些。
  • ART虛擬機在進行GC時採用了並法的模式。
    • 在傳統的GC模式下,當虛擬機觸發一次GC,會先暫停全部線程,而後檢查全部對象,將符合回收條件的對象進行標記,而後進行回收,最後再恢復線程,這樣的話gc速度會快些,可是遇到內存抖動,就會卡頓了。同時,傳統的GC算法致使了【內存碎片化】嚴重,在一次回收後,不少內存塊都會出現不連續的狀況,這樣會致使尋址變得困難,從而拖慢程序運行速度。
    • 而ART虛擬的垃圾回收算法容許GC時對對象的標記和一些對象的清理工做併發進行。同時,ART引入了【移動垃圾回收器】技術,使得碎片化內存可以被對齊,從而能稍微節約一些內存空間。

總結

  • Heap Segment被劃分爲兩塊:Young GeneriationOld Generiation
  • Young Genertiation中又被劃分爲Eden區和兩個Survivor區,對象在其中採用標記複製算法來斷定一個對象是應該清理仍是移到Old Generiation中。該內存區域發生GC的頻率較高。
  • Old Generiation發生GC的頻率相對較低。當有大對象被建立,或者和該區域有關的對象被建立時,它將會被直接移動到該區域中。

看到這裏的童鞋快獎勵本身一口辣條吧!

想要看CoorChice更多的文章,能夠加個關注哦!

相關文章
相關標籤/搜索