Java內存

Java 虛擬機在執行 Java 程序的過程當中會把他所管理的內存劃分爲若干個不一樣的數據區域。Java 虛擬機規範將 JVM 所管理的內存分爲如下幾個運行時數據區:程序計數器、Java 虛擬機棧、本地方法棧、Java 堆、方法區。html

一,內存區域劃分java

  1.線程共享區域:算法

    (1)Java堆(對象實例),GC的主要區域,會出現OutOfMemoryError數組

    (2)方法區(加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼)會出現OutOfMemoryError服務器

  2.線程私有區域:數據結構

    (1)虛擬機棧(操做數棧,動態連接,方法返回地址,局部變量) 多線程

      用於支持虛擬機進行方法方法調用和方法執行的數據結構。生命週期與線程相同,每一個方法執行時會建立一個棧幀併入棧,對於執行引擎,活動線程中,只有棧頂的棧幀是有效的,稱爲當前棧幀,所關聯的方法稱爲當前方法。併發

      若是申請的棧深度大於虛擬機容許的棧深度則拋出StackOutflowErrorapp

      若是在動態擴展時,沒法申請到足夠的內存則拋出OutOfMemoryError工具

      單線程:不管是棧空間太大,仍是虛擬機內存過小,拋出的都是StackOutflowError

      多線程:拋出OutOfMemoryError

        局部變量表:是一組變量值存儲空間,用於存放方法參數和方法內部定義的局部變量。最小單位爲變量槽(Slot)大小爲32字節,對於超過32字節的變量,使用連續的槽進行存儲

        操做數棧:又被稱爲操做棧,最大深度在編譯時肯定,32位數據類型佔用容量爲1,64爲數據類型佔用容量爲2。在方法執行過程當中,根據字節碼指令,進行入棧出棧操做。

        動態鏈接:棧幀中存在一個指向運行時常量池的引用,對常量池中符號的引用若是在類加載階段轉換爲直接引用則稱爲靜態解析。在每一次運行期間轉換爲直接引用,稱爲動態鏈接。

        方法返回地址:方法正常退出時,調用者的計數器指則爲返回地址。方法異常退出時,返回地址是經過異常處理器來肯定的。

    (2)本地方法棧(和虛擬機棧概念幾乎相同,不一樣的是服務於本地操做系統(Native)方法)

    (3)程序計數器:一塊內存較小的內存空間,是當前字節碼執行的行號指示器,根據該指示器來肯定下一條須要執行的指令。

  <注>Object obj = new Object();這段代碼的執行會涉及到Java棧,Java堆,方法區三個重要的內存區域。假設該語句出如今方法體中,obj會做爲引用類型存儲到Java虛擬機棧的局部變量表中,實例對象則保存在java堆中。而該對象的地址信息(如對象類型,父類,實現的接口等)則保存在方法區中。

 

二.垃圾回收(GC)

  垃圾回收(GC)指回收掉沒用的內存數據,將空間釋放出來以便存儲新的數據。垃圾回收能夠有效的防止內存泄漏。

  1.GC類型

    根據不一樣區域發生的GC,可主要將GC分爲YGC和FullGC

      YGC:當新生代滿了以後,會觸發minor collection(YGC)。

      FullGC:當整個堆內存達到閾值時,會觸發major collection(Full GC),致使整個heap的回收。(應儘可能避免出現FullGC,由於該GC收集時間較長,頻繁的FullGC會致使應用性能受到嚴重影響)

  2.Java堆內存

    新生代

      Eden區:新生區,新建立的對象都存儲在該區域

      Survior區:倖存區,主要存儲既未到達進入老年代的條件又位於To Survior的對象 Survior區由兩部分組成

        From區:新生代發生minorGC時,該區域內存會根據年齡決定去To區仍是老年代

        To區:新生代發生minorGC時,該區域內存數據不會被清除

    老年代:年齡達到老年的對象數據會從From Survior區進入該區域

    永久代(java8以後叫作元空間,不屬於堆):主要存儲的是JVM運行時須要的類和方法,元空間的類的對象是在進行FullGC時才進行垃圾收集

  3.內存申請過程

    JVM會試圖爲java對象在Eden中初始化一塊內存區域

    當Eden空間足夠時,內存申請結束,不然進入下一步

    JVM試圖釋放在Eden中全部的不活躍對象(YGC),釋放後若仍不足以放入新對象,則試圖將Eden中部分活躍對象放入Survivor的To區,

    若JVM的From Survivor區內存不足,則JVM會判斷對象的年齡,而後選擇性的將對象移動到To Survivor區或老年代。

    若是老年代的內存不足,JVM會在老年代進行major collection(FULL GC)

    完成垃圾回收後,若是Survivor和老年代仍然沒法存放eden區中的數據,致使JVM沒法在Eden區爲新建對象分配內存區域,則出現OutOfMemeryError

  4.內存的衰老過程

    新建對象的內存都分配自eden區,MinorCollection(YGC)的過程就是將eden中的對象移動到空閒的To Survivor區中,將From survivor區中經歷過必定次數YGC(次數能夠i經過參數配置)的對象移動到老年代

  5.經常使用的垃圾回收方法

    是否進行垃圾回收,須要知道一個對象是否可用。

    (1)引用計數算法:每當又一個地方引用一個對象時,計數器就加1,當引用失效時,計數器減一,任什麼時候刻計數器爲0的對象就是再也不被使用的對象(不能解決循環引用的問題)

    (2)可達性分析算法:用於判斷對象是否存活,基本思想是經過GC Roots對象做爲根節點,從這些節點向下搜索,搜索的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈時就認爲對象屬於不可達,證實此對象時不可用的。

    可做爲GC Roots的對象:

      虛擬機棧中引用的對象

      方法區中類靜態屬性引用的對象或常量引用的對象。

      本地方法棧中JNI(Native方法)引用對象

    (3)不管是經過引用計數法,仍是可達性分析算法,都是爲了判斷對象是否存在引用。而引用又分爲強引用和弱引用,軟引用,虛引用

        強引用:代碼中廣泛存在的,相似Object obj = new Object(),垃圾回收器永遠不會回收這部分對象

        軟引用:用來描述一些有用可是非必需的對象,在內存即將發生內存溢出時,會回收這部分對象

        弱引用:也是用來描述非必需的對象,可是比軟引用更弱一些,被弱引用關聯的對象只能生存道下一次垃圾回收發生以前

        虛引用:也叫幽靈引用或幻影引用,是最弱的一種引用關係,不會影響到垃圾回收

    (4)垃圾算法

        標記-清除法:最基礎的收集算法,分爲標記和清除兩個階段,首先標出全部須要回收的對象,在標記完成以後統一回收標記對象。這種算法會產生大量不連續的內存碎片,堆內存使用率低

        標記-整理法:首先標出全部須要回收的對象,而後將這部分對象清除,再將全部存活的對象向一端移動,這種算法不會出現內存碎片

        分代收集算法:將內存劃分爲多塊,而後再根據不一樣區域的特色選擇使用不一樣的垃圾回收算法例如新生代每次垃圾回收後只會有少許對象存活,則使用複製算法,而老年代的存活率高,則可使用標記清理或者標記整理法

  6.經常使用垃圾收集器

    (1)串行收集器(Seiral Collector):最簡單的垃圾收集器,基本上都是涉及單核環境下工做,幾乎不會使用該收集器,由於它在收集時會暫停整個應用的運行。

      使用方法:-XX:+UseSerialGC

    (2)並行/吞吐優先收集器(Parallel/Throughput Collector):這是JVM默認的收集器,跟它的名字顯示的同樣,它最大的優勢就是使用多個線程來掃描和壓縮堆,缺點是在minorGC和fullGC時會暫停應用的運行,並行收集器適合能夠容忍程序停滯的環境使用,它佔用較低的CPU於是能夠提升應用的吞吐

      使用方法:-XX:UseParallelGC

    (3)CMS收集器(CMS Collector):CMS使用的是併發的標記與清除,這個算法使用多個線程併發的掃描堆,標記不使用的對象,而後清除它們回收內存。

      使用方法:-XX:UseConcMarkSweepGC,此時可同時使用-XX:UseParNewGC將並行收集做用於年輕代,新的JVM自動打開這個配置

      原理:併發標記清除算法

      流程:初始化標記->併發標記->併發預清理->從新標記->併發清理->併發重置

      優勢:減小了回收的停頓時間,回收效率高

      缺點:產生的空間碎片多,須要更多的CPU資源,須要更大的堆空間,下降了堆空間的利用率

      使用場景:程序對停頓比較敏感,而且應用程序運行時能夠提供更大的內存和更多的CPU

    (4)G1收集器(Garbage First Collector):G1收集器適用於堆內存大於4G的JVM,它會將堆分紅多個區域,大小從1MB-32MB,並使用多個後臺線程來掃描這些區域,優先會掃描最多垃圾的區域

      使用方法:-XX:+UseG1GC

          Java8和G1收集器:G1收集器在Java8上最好的優化是String去重,String對象和它內部使用的char[]數組會佔用比較多的內存,由於優化過的G1收集器會把重複的String對象指向同一個char[]數組,避免多個副本存在堆裏

      使用方法:-XX:UseStringDeduplication

      原理:標記整理算法

      流程:初始標記->併發標記->最終標記->篩選回收

      優勢:並行於併發,分帶收集,空間整合,可預測停頓

      缺點:須要更大的內存和更多的CPU

      使用場景:對堆內存的使用率要求較高,對停頓時間容忍性較低,而且堆內存足夠大

    (5)G1和CMS的區別

      CMS是以獲取最短回收停頓時間爲目標的收集器,基於標記-清除算法實現,比較佔用CPU資源,容易產生內存碎片

      G1是面向服務器端的垃圾收集區,是JDK9的默認收集器,基於標記-整理算法實現,可利用多核,多CPU,保留分帶,實現可預測可控停頓

  7.一次完整的GC

    當新生代發生minorGC時,Eden區中存活的對象都被複制到To Survivor區,而後From Survivor區中的對象根據年齡決定區老年代仍是To survivor區,而後清空Eden區和From Survivor區,最後再交換From區和To區。

    當老年代中若是對象到達了回收的年齡,則會根據對象的垃圾回收器使用對應的算法進行內存Full GC。

    永久代(元空間)在進行FullGC時進行垃圾回收

三.JVM內存模型

  1.內存屏障:爲了保障順序和可見性的一條CPU指令

  2.重排序:爲了提升性能,編譯器和處理器會對執行語句進行重排序

  3.happen-before:操做間執行的順序關係,有些操做優先於有些操做發生

  4.主內存:共享變量存儲的區域

  5.工做內存:每一個線程copy的本地內存,存儲了該線程已讀/已寫的共享變量的副本

 

四.JVM經常使用參數

  -server -Xms512m -Xmx512m -Xss1024k

  -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20

  -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly

  server模式啓動

  -Xms512m:最小堆內存512M

  -Xmx512m:最大堆內存512M

  -Xss1024k:每一個線程棧空間1M

  -XX:PermSize=256M:永久代256M

  -XX:MaxPermSize=512m:永久代最大512M

  -XX:MaxTenuringThreshold=20:最大轉爲老年代檢查次數20

  XX:CMSInitiatingOccupancyFraction:CMS回收開啓時間,內存佔用80%

 

五.JVM經常使用工具

  http://www.javashuo.com/article/p-nrqxnuwc-bm.html

相關文章
相關標籤/搜索