1. 程式人生 > >java記憶體理解

java記憶體理解

一、Java記憶體的構成

  先貼原圖


 如圖可知:整塊區域分為Young Generation、Tenured Generation、Permanent Generation。(年輕代→老年代→永久代 )

咱們看下Young區:

    Young區又分為:Eden、Survivor Space。

    Survivor Space又分為 To Survivor、 From Survivor,如下圖所示:


 

Java記憶體分為 堆記憶體(heap)和 Permanent區。

1、Java堆記憶體(heap):

    --是 JVM 用於分配 Java物件的記憶體,包含活動物件和不可用物件 

    --堆大小通常是在伺服器啟動時使用 java命令中的 –Xms(最小) –Xmx(最大)標誌來定義。 

2、Permanent區:

    --指記憶體的永久儲存區域

    --是Sun JDK和HPJDK用來載入類(class)和Meta資訊的專門的記憶體區

   --這個區域不歸屬Java堆記憶體(heap)範圍

   --Class在被Loader時就會被放到此,如果Java應用很大,例如類(class)很多,那麼建議增大這個區域的大小來滿足載入這些類的記憶體需求

    --通過–XX:PermSize=***M–XX:MaxPermSize=***M調整

這裡還有一個本地記憶體的概念:

·本地記憶體(native memory): 

    --是 JVM用於其內部操作的本地記憶體(非Java記憶體) 

    --JNI 程式碼和第三方本地模組(例如,本地JDBC 驅動程式)也使用本地記憶體 

   --最大本地記憶體大小取決於以下因素:作業系統程序記憶體大小限制、已經指定用於 Java 堆的記憶體

也就是說,整個物理機的記憶體可以說由以下部分構成:

實體記憶體 = Java 記憶體 + 本地記憶體 + 作業系統保留的記憶體

二、垃圾回收(Garbage Collection,GC)

1、為什麼要垃圾回收

   --JVM自動檢測和釋放不再使用的記憶體。 

    --Java 執行時JVM會執行GC,這樣程式設計師不再需要顯式釋放物件。 

2、垃圾回收(GC)的分類

    --Minor GC

    --Full GC

3、垃圾回收(GC)的產生過程

    1)新生成的物件在Eden區完成記憶體分配

    2)當Eden區滿了,再建立物件,會因為申請不到空間,觸發minorGC,進行young(eden+1survivor)區的垃圾回收。(為什麼是eden+1survivor:兩個survivor中始終有一個survivor是空的,空的那個被標記成ToSurvivor)

    3)minorGC時,Eden不能被回收的物件被放入到空的survivor(也就是放到ToSurvivor,同時Eden肯定會被清空),另一個survivor(FromSurvivor)裡不能被GC回收的物件也會被放入這個survivor(ToSurvivor),始終保證一個survivor是空的。(MinorGC完成之後,To Survivor 和 FromSurvivor的標記互換)

    4)當做第3步的時候,如果發現存放物件的那個survivor滿了,則這些物件被copy到old區,或者survivor區沒有滿,但是有些物件已經足夠Old(通過XX:MaxTenuringThreshold引數來設定),也被放入Old區

    5)當Old區被放滿的之後,進行完整的垃圾回收,即 Full GC

     6)FullGC時,整理的是Old Generation裡的物件,把存活的物件放入到Permanent Generation裡。

4、垃圾回收的回收器

  --序列(–XX:+UseSerialGC )

    Out ofBox演算法,年輕代串行復制,年老代序列標記整理,主要用於桌面應用

  --並行(–XX:+UseParallelGC )

    年輕代暫停應用程式,多個垃圾收集執行緒並行的複製收集,年老代暫停應用程式,與序列收集器一樣,單垃圾收集執行緒標記整理。JDK6.0啟用該演算法後,預設啟用了-XX:+UseParallelOldGC,效能大為提高

  --併發(Concurrent Low Pause Collector)(–XX:+UseConcMarkSweepGC )

    啟用該引數,預設啟用了-XX:+UseParNewGC;簡單的說,併發是指使用者執行緒與垃圾收集執行緒併發,程式在繼續執行,而垃圾收集程式運行於其他CPU上。

三、Java記憶體的調優引數

-Xmx1024m:

    設定JVM最大可用記憶體為1024M。

-Xms1024m:

   設定JVM促使記憶體為1024M。此值可以設定與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配記憶體。

-Xmn512m:

   設定年輕代大小為512M。(持久代一般固定大小為64m,所以增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。)

-Xss128k:

   設定每個執行緒的堆疊大小。這個值可以根據應用的執行緒所需記憶體大小進行調整。在相同實體記憶體下,減小這個值能生成更多的執行緒。但是作業系統對一個程序內的執行緒數還是有限制的,不能無限生成。

-XX:NewRatio=4

   設定年輕代(包括Eden和兩個Survivor區)與年老代的比值(總的大小是Xms的值)。設定為4,則年輕代與年老代所佔比值為1:4,年輕代佔整個堆疊的1/5。

    舉個例子,-Xms 設定為 1024m,-Xmx也設定為 1024m的情況下:

      ·年輕代= 1024M/5 = 204.8M

      ·年老代= 1024M/5*4 = 819.2M

    如果-Xms和-Xmx的值設定的不一樣,可以新增-XX:MinHeapFreeRatio=<minimum> 和-XX:MaxHeapFreeRatio=<maximum>引數,使記憶體的大小能夠在 大於 -Xms 和 小於 -Xmx之間的範圍內自動調整,所以記憶體中會有Virtual的空間(我是這樣理解的,不是太清楚,這裡需要大家指教)

    By default, the virtualmachine grows or shrinks the heap at each collection to try to keepthe proportion of free space to live objects at each collectionwithin a specific range. This target range is set as a percentageby the parameters-XX:MinHeapFreeRatio=<minimum> and-XX:MaxHeapFreeRatio=<maximum>, andthe total size is bounded below by -Xms and above by -Xmx .

-XX:SurvivorRatio=4:

   設定年輕代中Eden區與Survivor區的大小比值。設定為4,則兩個Survivor區與一個Eden區的比值為2:4,一個Survivor區佔整個年輕代的1/6

-XX:MaxPermSize=16m:

    設定持久代大小為16m。

-XX:MaxTenuringThreshold=0:

   設定垃圾最大年齡。如果設定為0的話,則年輕代物件不經過Survivor區,直接進入年老代。對於年老代比較多的應用,可以提高效率。如果將此值設定為一個較大值,則年輕代物件會在Survivor區進行多次複製,這樣可以增加物件再年輕代的存活時間,增加在年輕代即被回收的概論。

總結如下圖:

我對Java記憶體的認識(原創)

四、記憶體分配中會出現的錯誤

關於記憶體最常見的錯誤應該是這兩個:

  --記憶體溢位 Out Of Memory(OOM)

  --記憶體洩露 Memory Leak (ML)

1、記憶體溢位

   記憶體溢位發生在這種狀況下:Java記憶體完成Minor GC 之後想要把還存活的物件放到 Old區裡,但是這時Old區 已經滿了,同時 Permanent區也已經放不下存活的物件。這時就會產生 OOM錯誤。

2、記憶體洩露

   在Java中,記憶體洩漏就是存在一些被分配的物件,這些物件有下面兩個特點,首先,這些物件是有被引用的,即在有向樹形圖中,存在樹枝通路可以與其相連;其次,這些物件是無用的,即程式以後不會再使用這些物件。如果物件滿足這兩個條件,這些物件就可以判定為Java中的記憶體洩漏,這些物件不會被GC所回收,然而它卻佔用記憶體。

   找到一個例子:

   “這裡引用一個常看到的例子,在下面的程式碼中,迴圈申請Object物件,並將所申請的物件放入一個Vector中,如果僅僅釋放物件本身,但因為Vector仍然引用該物件,所以這個物件對GC來說是不可回收的。因此,如果物件加入到Vector後,還必須從Vector中刪除,最簡單的方法就是將Vector物件設定為null。

  1. Vector v = new Vector(10);     
  2. for (int i = 1; i < 100; i++)     
  3. {     
  4.  Object o = new Object();     
  5.  v.add(o);     
  6.  o = null;     
  7. }//此時,所有的Object物件都沒有被釋放,因為變數v引用這些物件。    

實際上這些物件已經是無用的,但還被引用,GC就無能為力了(事實上GC認為它還有用),這一點是導致記憶體洩漏最重要的原因。”

3、補充一個:PermGen space Error

    因為 GC不會在主程式執行期對PermGen space進行清理,所以如果應用中有很CLASS需要Load的話,就很可能出現PermGenspace錯誤。

    另外如果WEBAPP下使用了大量的第三方jar, 其大小超過了 jvm 預設的大小那麼也會產生此錯誤資訊了。

五、總結

  上面4點的內容可以跟下面這個圖來進行融合:

我對Java記憶體的認識(原創)
 

相關推薦

java記憶體理解

一、Java記憶體的構成   先貼原圖  如圖可知:整塊區域分為Young Generation、Tenured Generation、Permanent Generation。(年輕代→老年代→永久代 ) 咱們看下Young區:     Young區又分為

讀薄《深入理解 JAVA 虛擬機器》Java記憶體分配策略

記憶體分配規則不是固定的,取決於當前使用的是哪一種垃圾收集器以及虛擬機器配置。 物件優先在 Eden 上分配 大多數情況下,物件分配在 Eden 上,當記憶體不足的時候觸發一次 Minor GC。 大物件分配進老年代 需要連續記憶體空間的物件,最典型的是很長的字串已經陣列,寫程式的時候應該避免生命週期

讀薄《深入理解 JAVA 虛擬機器》Java記憶體區域

很早之前看了《深入理解 JAVA 虛擬機器》並寫下了讀書筆記。最近在結合一些其他資料整理部落格。希望能幫助到其他人抓住書的重點。 Java執行時資料區域 Java執行時資料區域 白色為執行緒獨佔的,灰色為執行緒共享的。 Java在執行的時候會把他所管理的記憶體劃分為若干區域,經常有人把記憶體區域分為

談談我對JAVA記憶體可見性的理解 JAVA

首先要明確一點,每個執行緒都有屬於自己的工作記憶體。 出了執行緒自己擁有的工作記憶體外,還有公共記憶體。 假設我們有一個變數i,然後我們啟動兩個執行緒,這個時候i就會被拷貝成兩份副本分別給兩個執行緒的工作記憶體。 然後,這兩個執行緒如果對i進行操作,系統首先會將改變後的i先寫到執行緒的工

《深入理解 Java 記憶體模型》讀書筆記(下)(乾貨,萬字長文)

0. 前提 1. 基礎 2. 重排序 3. 順序一致性 4. Volatile 5. 鎖 6. final 7. 總結 4. Volatile 4.1 VOLATILE 特性 舉個例子: publ

《深入理解 Java 記憶體模型》讀書筆記(上)(乾貨,萬字長文)

0. 前提 1. 基礎 2. 重排序 3. 順序一致性 4. Volatile 5. 鎖 6. final 7. 總結 0. 前提 《深入理解 Java 記憶體模型》 程曉明著,該書在以前看過一

《深入理解Java虛擬機器—JVM高階特性與實踐 周志明 著》之第2章 Java記憶體區域與記憶體溢位異常

1、Java虛擬機器所管理的記憶體包括以下幾個執行時資料區域: 2、程式計數器:          1. 可以看作是當前執行緒所執行的位元組碼的行號指示器,是一塊較小的記憶體空間;  &nbs

理解Java記憶體模型

文章首發於51CTO技術棧公眾號 作者 陳彩華 文章轉載交流請聯絡 [email protected] 複製程式碼 最近重新學習一遍《深入學習Java虛擬機器》,把之前Java記憶體模型中模糊的知識重新梳理一遍,這篇文章主要介紹模型產生的問題背景,解決的問題,處理思路,相關實現規則,環環相扣,希望讀

《深入理解java虛擬機器》讀書筆記(二)---- Java記憶體區域與記憶體溢位異常

執行時資料區域 java虛擬機器所管理的記憶體將會包括以下幾個執行時資料區域: 1、程式計數器 程式計數器是一塊較小的記憶體空間,它可以看作是當前執行緒所執行位元組碼的行號指示器。在虛擬機器的概念模型裡,位元組碼直譯器的工作就是通過改變這個計數器的值來選取下一條需要執

深入理解Java虛擬機器筆記——Java記憶體模型與併發程式設計

  當程式在執行過程中,會將運算需要的資料從主存複製一份到CPU的快取記憶體中,那麼CPU進行計算時就可以直接從它的快取記憶體讀取資料和向其中寫入資料,當運算結束後,再將告訴快取中的資料重新整理到主存中。   如果一個變數在多個CPU中都存在快取,那麼就存在快取一致性

Java記憶體模型與執行緒 深入理解Java虛擬機器總結

在許多情況下,讓計算機同時去做幾件事情,不僅是因為計算機的運算能力強大了,還有一個很重要的原因是計算機的運算速度與它的儲存和通訊子系統速度的差距太大, 大量的時間都花費在磁碟I/O、網路通訊或者資料庫訪問上。 如果不希望處理器在大部分時間裡都處於等待其他資源的狀態,就必須使用一些手段去把處理器

Java記憶體模型原理,你真的理解透徹了嗎?

記憶體模型產生背景 在介紹 Java 記憶體模型之前,我們先了解一下物理計算機中的併發問題,理解這些問題可以搞清楚記憶體模型產生的背景。 物理機遇到的併發問題與虛擬機器中的情況有不少相似之處,物理機的解決方案對虛擬機器的實現有相當的參考意義。 物理機的併發問題 硬體的效率問題 計

深入理解Java虛擬機器(5)Java記憶體模型

深入理解Java虛擬機器(5)Java記憶體模型 Java記憶體模型 主記憶體和工作記憶體 volatile關鍵字 long與double型別的特殊規則 synchronized關鍵字 Java記憶體模

讀書筆記 ---- 《深入理解Java虛擬機器》---- 第11篇:Java記憶體模型與執行緒

上一篇:晚期(執行期)優化:https://blog.csdn.net/pcwl1206/article/details/84642835 目  錄: 1  概述 2  Java記憶體模型 2.1  主記憶體與工作記憶體 2.2 

理解JVM之Java記憶體區域

Java虛擬機器執行時資料區分為以下幾個部分: 方法區、虛擬機器棧、本地方法棧、堆、程式計數器。如下圖所示: 程式計數器 程式計數器可看作當前執行緒所執行的位元組碼行號指示器,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令。Java虛擬機器的多執行緒是通過執行緒輪流切換

讀《深入理解JVM兩遍》總結(純手打) 第二章 Java記憶體區域

深入理解JVM總結 第二章 Java記憶體區域 2.2 執行時資料區域 2.2.1 程式計數器 可以看作當前執行緒所執行的位元組碼行號指示器。 位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令。 每一個執行緒都需要有一個獨立的程式計數

深入理解JVM(十一)——Java記憶體模型與執行緒

計算機運算的速度,與它的儲存和通訊子系統相差太大,大量的時間花費在磁碟IO,網路通訊和資料庫上。 衡量一個服務效能的高低好壞,每秒事務處理數TPS是最重要的指標。 對於計算量相同的任務,程式執行緒併發協調的越有條不紊,效率越高;反之,執行緒之間頻繁阻塞或是死鎖,將大大降低併發能力。

讀書筆記《深入理解Java虛擬機器》 (一)JAVA記憶體區域

  .結構圖來自原書 執行緒私有區域 程式計數器 虛擬機器棧 本地方法棧 所有執行緒共享 方法區 堆   程式計數器(執行緒私有) 是一塊較小的記憶體空間,可以看做是當前執行緒所執行的位元組碼的行號

深入理解Java記憶體模型(六)——final

與前面介紹的鎖和volatile相比較,對final域的讀和寫更像是普通的變數訪問。對於final域,編譯器和處理器要遵守兩個重排序規則: 在建構函式內對一個final域的寫入,與隨後把這個被構造物件的引用賦值給一個引用變數,這兩個操作之間不能重排序。 初次讀一個包含

深入理解 Java 虛擬機器(十二)Java 記憶體模型與執行緒

執行緒安全 Java 語言中的執行緒安全 根據執行緒安全的強度排序,Java 語言中各種操作共享的資料可以分為 5 類:不可變、絕對執行緒安全、相對執行緒安全、執行緒相容、執行緒對立。 不可變 不可變的物件一定是執行緒安全的,如果共享資料是一個基本資料型別,那麼