天天看點

JVM參數配置 java記憶體區域

 java記憶體區域

JVM參數配置 java記憶體區域

  一些基本概念

       http://www.importnew.com/18694.html

       https://www.cnblogs.com/wangyayun/p/6557851.html

  1.方法區和堆是所有線程共享的記憶體區域;而java棧、本地方法棧和程式員計數器是運作時線程私有的記憶體區域。

        2.Java堆(Heap),是Java虛拟機所管理的記憶體中最大的一塊。Java堆是被所有線程共享的一塊記憶體區域,在虛拟機啟動時建立。此記憶體區域的唯一目的就是存放對象執行個體,幾乎所有的對象執行個體都在這裡配置設定記憶體。

        3.方法區(Method Area),方法區(Method Area)與Java堆一樣,是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。

        4.程式計數器(Program Counter Register),程式計數器(Program Counter Register)是一塊較小的記憶體空間,它的作用可以看做是目前線程所執行的位元組碼的行号訓示器。

        5.JVM棧(JVM Stacks),與程式計數器一樣,Java虛拟機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛拟機棧描述的是Java方法執行的記憶體模型:每個方法被執行的時候都會同時建立一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動态連結、方法出口等資訊。每一個方法被調用直至執行完成的過程,就對應着一個棧幀在虛拟機棧中從入棧到出棧的過程。

        6.本地方法棧(Native Method Stacks),本地方法棧(Native Method Stacks)與虛拟機棧所發揮的作用是非常相似的,其差別不過是虛拟機棧為虛拟機執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛拟機使用到的Native方法服務。

  堆中存的是對象。棧中存的是基本資料類型和堆中對象的引用

JVM參數配置 java記憶體區域

類的生命周期

        1.加載,查找并加載類的二進制資料,在Java堆中也建立一個java.lang.Class類的對象

        2.連接配接,連接配接又包含三塊内容:驗證、準備、解析。1)驗證,檔案格式、中繼資料、位元組碼、符号引用驗證;2)準備,為類的靜态變量配置設定記憶體,并将其初始化為預設值;3)解析,把類中的符号引用轉換為直接引用

        3.初始化,為類的靜态變量賦予正确的初始值

        4.使用,new出對象程式中使用

        5.解除安裝,執行垃圾回收

引用類型

  對象引用類型分為強引用、軟引用、弱引用和虛引用

  強引用:就是我們一般聲明對象是時虛拟機生成的引用,強引用環境下,垃圾回收時需要嚴格判斷目前對象是否被強引用,如果被強引用,則不會被垃圾回收

  軟引用:軟引用一般被做為緩存來使用。與強引用的差別是,軟引用在垃圾回收時,虛拟機會根據目前系統的剩餘記憶體來決定是否對軟引用進行回收。如果剩餘記憶體比較緊張,則虛拟機會回收軟引用所引用的空間;如果剩餘記憶體相對富裕,則不會進行回收。換句話說,虛拟機在發生OutOfMemory時,肯定是沒有軟引用存在的。

  弱引用:弱引用與軟引用類似,都是作為緩存來使用。但與軟引用不同,弱引用在進行垃圾回收時,是一定會被回收掉的,是以其生命周期隻存在于一個垃圾回收周期内。

  強引用不用說,我們系統一般在使用時都是用的強引用。而“軟引用”和“弱引用”比較少見。他們一般被作為緩存使用,而且一般是在記憶體大小比較受限的情況下做為緩存。因為如果記憶體足夠大的話,可以直接使用強引用作為緩存即可,同時可控性更高。因而,他們常見的是被使用在桌面應用系統的緩存。

JVM參數配置

JVM啟動模式

  Client模式:啟動速度較快,但運作時性能和記憶體管理效率不高

  Server模式:啟動比Client模式慢10%,但運作時性能和記憶體管理效率較高

  在JVM啟動的時候指定:-Client或者-Server來判斷啟動模式

分代收集算法 把對象分為新生代、老年代、持久代,對不同生命周期的對象使用不同的算法  http://www.importnew.com/19255.html

  試想,在不進行對象存活時間區分的情況下,每次垃圾回收都是對整個堆空間進行回收,花費時間相對會長,同時,因為每次回收都需要周遊所有存活對象,但實際上,對于生命周期長的對象而言,這種周遊是沒有效果的,因為可能進行了很多次周遊,但是他們依舊存在。是以,分代垃圾回收采用分治的思想,進行代的劃分,把不同生命周期的對象放在不同代上,不同代上采用最适合它的垃圾回收方式進行回收。

JVM參數配置 java記憶體區域

  新生代

    所有新生成的對象首先都是放在年輕代的。年輕代的目标就是盡可能快速的收集掉那些生命周期短的對象。年輕代分三個區。一個Eden區,兩個Survivor區(預設,可改),大部分對象在Eden區中生成。當Eden區滿時,還存活的對象将被複制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象将被複制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複制過來的并且此時還存活的對象,将被複制“年老區(Tenured)”。需要注意,Survivor的兩個區是對稱的,沒先後關系,是以同一個區中可能同時存在從Eden複制過來 對象,和從前一個Survivor複制過來的對象,而複制到年老區的隻有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空的。同時,根據程式需要,Survivor區是可以配置為多個的(多于兩個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能

  老年代

    在年輕代中經曆了N次垃圾回收後仍然存活的對象,就會被放到年老代中。是以,可以認為年老代中存放的都是一些生命周期較長的對象。

  持久代

    用于存放靜态檔案,如今Java類、方法等。持久代對垃圾回收沒有顯著影響

垃圾回收算法 http://www.importnew.com/18740.html

  引用計數(Reference Counting):

    比較古老的回收算法。原理是此對象有一個引用,即增加一個計數,删除一個引用則減少一個計數。垃圾回收時,隻用收集計數為0的對象。此算法最緻命的是無法處理循環引用的問題。

  标記-清除(Mark-Sweep):

    此算法執行分兩階段。第一階段從引用根節點開始标記所有被引用的對象,第二階段周遊整個堆,把未标記的對象清除。此算法需要暫停整個應用,同時,會産生記憶體碎片。 碎片太多可能引發另一次GC

JVM參數配置 java記憶體區域

  複制(Copying):

    此算法把記憶體空間劃為兩個相等的區域,每次隻使用其中一個區域。垃圾回收時,周遊目前使用區域,把正在使用中的對象複制到另外一個區域中。次此法每次隻處理正在使用中的對象,是以複制成本比較小,同時複制過去以後還能進行相應的記憶體整理,不會出現“碎片”問題。當然,此算法的缺點也是很明顯的,就是需要兩倍記憶體空間。

JVM參數配置 java記憶體區域

  标記-整理(Mark-Compact):

    此算法結合了“标記-清除”和“複制”兩個算法的優點。也是分兩階段,第一階段從根節點開始标記所有被引用對象,第二階段周遊整個堆,把清除未标記對象并且把存活對象“壓縮”到堆的其中一塊,按順序排放。此算法避免了“标記-清除”的碎片問題,同時也避免了“複制”算法的空間問題。

JVM參數配置 java記憶體區域

串行GC

  Serial收集器

  1)特點: 

    –僅僅使用單線程進行記憶體回收;

    –它是獨占式的記憶體回收 ;

    –進行記憶體回收時, 暫停所有的工作線程(“Stop-The-World”) ;

    –使用複制算法;

    –适合CPU等硬體一般的場合;

    –到JDK1.7為止,是JVM Client模式下預設的新生代收集器;

  2)設定參數: 

    -XX:+UseSerialGC        指定使用新生代Serial和老年代SerialOld

  SerialOld收集器

JVM參數配置 java記憶體區域

    –同新生代Serial收集器一樣,單線程、獨占式的垃圾收集器;

    –使用“标記-整理”算法;

    –通常老年代記憶體回收比新生代記憶體回收要更長時間,是以可能會使應用程式停頓較長時間;

    -XX:+UseSerialGC        新生代、老年代都使用串行GC; 

    -XX:+UseParNewGC        新生代使用ParNew,老年代使用SerialOld;

    -XX:+UseParallelGC      新生代使用Parallel,老年代使用SerialOld;

并行GC(吞吐量優先)

  ParNew收集器

    1)特點: 

      –Serial的多線程版本;

      –使用複制算法 ;

      –垃圾回收時,應用程式仍會暫停,隻不過由于是多線程回收,在多核CPU上,回收效率會高于串行GC。反之在單核CPU,效率會不如串行GC;

    2)設定參數: 

      -XX:+UseParNewGC               新生代使用ParNew,老年代使用SerialOld;

      -XX:+UseConcMarkSweepGC  新生代使用ParNew,老年代使用CMS;

      -XX:ParallelGCThreads=n      指定ParNew收集器工作時的收集線程數,當CPU核數小于8時,預設開啟的線程數等于CPU數量,當高于8時,可使用公式:3+((5*CPU_count)/8) 。

      ParNew收集器其實就是Serial收集器的多線程版本,除了多條線程收集之外,其餘行為包括Serial收集器可用的設定參數、收集算法、Safepoint、對象配置設定規則、回收政策等都與Serial收集器完全一樣,并沒有太多特别之處。但它卻是運作在JVM Service模式下首選的新生代收集器,其中一個很重要的原因是:除了Serial收集器外,目前隻有它能于CMS收集器(并發GC)配合工作。

         ParNew收集器在單CPU環境中絕對不會有比Serial收集器更好的效果,甚至由于存線上程切換的開銷,ParNew收集器在兩個CPU環境中都不能100%保證優于Serial收集器。随着可以使用的CPU數量的增加,它對于GC時系統資源的有效利用還是有利的。

  Parallel收集器

    1)特點: 

      –同ParNew回收器一樣, 不同的地方在于,它非常關注系統的吞吐量(通過參數控制) ;

      –使用複制算法;

      –支援自适應的GC調節政策;

    2)設定參數:

      -XX:+UseParallelGC         新生代使用Parallel,老年代使用SerialOld;

      -XX:+UseParallelOldGC   新生代使用Parallel,老年代使用ParallelOld;

      -XX:MaxGCPauseMillis=n  設定記憶體回收的最大停頓時間,機關ms ; 

      -XX:GCTimeRatio=n         設定吞吐量的大小,假設值為n(在0-100之間),那麼系統将花費不超過1/(n+1)的時間用于記憶體回收。預設值為99,就是允許最大1%的垃圾收集時間;

      -XX:+UseAdaptiveSizePolicy  自适應GC政策的開關參數。

     吞吐量(Throughput) = 運作使用者代碼時間 /(運作使用者代碼時間 + 垃圾收集時間)。

   Parallel收集器支援“GC自适應的調節政策(GC-Ergonomics)”,這也是Parallel收集器與ParNew收集器的一個重要差別。

  設定參數-XX:+UseAdaptiveSizePolicy,這是一個開關參數,當這個參數打開後,不需要手工指定新生代的大小(-Xmn)、Eden與Survivor區比例(-XX:SurvivorRatio)、晉升老年代對象年齡(-XX:PretenureSizeThreshold)等細節參數,虛拟機會根據目前系統的運作情況收集性能監控資訊,動态調整這些參數以提供最适合的停頓時間或者最大的吞吐量,這種調節方式稱為“GC自适應的調節政策”。

  你也可以在使用Parallel收集器自适應調節政策時,把基本的記憶體資料設定好(如-Xmx 最大堆),然後使用MaxGCPauseMillis參數(更關注最大停頓時間)或GCTimeRatio(更關注吞吐量)參數給虛拟機設立一個優化目标,具體細節參數的調節任務就交由虛拟機去完成。

  ParallelOld收集器

JVM參數配置 java記憶體區域

    1)特點: 

      –關注吞吐量的老年代并發收集器;

      –使用“标記-整理”算法;

    2)設定參數: 

      -XX:+UseParallelOldGC        新生代使用Parallel,老年代使用ParallelOld。

      這個收集器是在JDK1.6中才開始提供,在此之前,如果新生代選擇了Parallel收集器,老年代除了SerialOld收集器外别無選擇。在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel加ParallelOld收集器。

并發GC

  CMS收集器

JVM參數配置 java記憶體區域

      –非獨占式的老年代并發收集器,大部分時候應用程式不會停止運作;

      –使用“标記-清除”算法,是以回收後會有記憶體碎片,可設定參數進行記憶體碎片的壓縮整理 ;

      –與Parallel和ParallelOld不同,CMS主要關注系統停頓時間;

    2) 缺點:

      對CPU資源敏感;

      無法處理浮動垃圾(Floating Garbage);

      記憶體碎片問題

其中初始标記、重新标記這兩個步驟仍需“Stop-The-World”,這兩個階段是獨占的,不能與使用者線程一起執行,而其它階段則可以與使用者線程一起執行。

初始标記僅僅隻是标記一下GC Roots能直接關聯到的對象,速度很快;

并發标記階段就是進行GC Roots Tracing的過程;

重新标記階段則是為了修正并發标記期間因使用者程式繼續運作而導緻标記産生變動的那一部分對象的标記記錄,這個階段的停頓時間一般會比初始标記階段稍長一些,但遠比并發标記的時間短。

由于整個過程中耗時最長的并發标記和并發清除過程,收集線程都可以與使用者線程一起工作,是以,從總體上來說,CMS收集器的記憶體回收過程是與使用者線程一起并發執行的。

浮動垃圾: 并發清理階段使用者線程還在運作着,伴随程式運作自然就還會有新的垃圾不斷産生

      -XX:-CMSPrecleaningEnabled  關閉預清理,預設在并發标記後會有一個預清理的操作;

      -XX:+UseConcMarkSweepGC    老年代使用CMS,新生代使用ParNew; 

      -XX:ConcGCThreads=n             設定并發線程數;

      -XX:ParallelCMSThreads=n        同上,設定并發線程數;

      -XX:CMSInitiatingOccupancyFraction=n 指定老年代回收閥值,預設值為68;

      -XX:+UseCMSCompactAtFullCollection 開啟記憶體碎片整理;

      -XX:CMSFullGCsBeforeCompation=n  指定進行多少次CMS垃圾回收後再進行一次記憶體壓縮;

      -XX:+CMSParallelRemarkEnabled      在使用UseParNewGC參數的情況下,盡量減少 mark(标記)的時間;

      -XX:+UseCMSInitiatingOccupancyOnly  表示隻有達到閥值時才進行CMS垃圾回收

JVM參數配置 java記憶體區域

對象何時進入老年代

長期存活的對象将進入老年代

     -XX:MaxTenuringThreshold=n  假設值為n,則新生代的對象最多經曆n次GC,就能晉升老年代,但這個必不是晉升的必要條件

   -XX:TargetSurvivorRatio=n      用于設定Survivor區的目标使用率,即當Survivor區GC後使用率超過這個值,就可能會使用較小的年齡作為晉升年齡。預設為50

    虛拟機采用分代收集的思想管理記憶體,那記憶體回收時就必須能識别那些對象該放到新生代,那些該到老年代中。為了做到這點,虛拟機為每個對象定義了一個對象年齡(Age,由GC的次數決定)。每經過一次新生代GC後仍然存活,将對象的Age增加1歲,當年齡到一定程度(預設為15)時,将會被晉升到老年代中。

    如果将 -XX:MaxTenuringThreshold 參數設定為0的話,則新生代對象不經過Survivor區,直接進入老年代,對于需要大量常駐記憶體的應用,這樣做可以提高效率;如果将此值設定為一個較大值,則新生代對象會在Survivor區進行多次複制,這樣做可以增加對象在新生代的存活時間,增加對象在新生代被垃圾回收的機率,減少Full GC的頻率,可以在某種程度上提高服務穩定性。

适齡對象也可能進入老年代

    為了能更好地适應不同程式的記憶體狀況,虛拟機并不是永遠地要求對象年齡必須達到 MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 空間中相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無須等到 MaxTenuringThreshold 中要求的年齡。

大對象直接進入老年代

    -XX:PretenureSizeThreshold    即對象的大小大于此值,就會繞過新生代,直接在老年代配置設定(即所謂“大對象直接進入老年代”)。

    除年齡外,對象體積也會影響對象的晉升的。若對象體積太大,新生代無法容納這個對象,則這個對象可能就會直接晉升至老年代。特别是,如果程式中經常出現“短命的大對象”,容易發生記憶體還有不少空間卻不得不提前觸發Full GC以擷取足夠的連續空間,導緻GC效率低下。可通過以下參數使用對象直接晉升至老年代的門檻值,機關是byte 

PretenureSizeThreshold 參數的意義在于,若遇上述情況時,能避免在 Eden 及兩個 Survivor 之間發生大量的記憶體複制。此參數隻對串行GC以及ParNew有效,而Parallel并不認識這個參數。Parallel收集器一般并不需要特别設定。如果遇到必須使用此參數的場合,也可以考慮 ParNew加CMS的收集器組合。

******************************************************************************************************************************************************************************************************************************************************************************************************

常見配置彙總 http://www.importnew.com/19264.html

堆設定

  -Xms:初始堆大小

  -Xmx:最大堆大小

  -XX:NewSize=n:設定年輕代大小

  -XX:NewRatio=n:設定年輕代和年老代的比值。如:為3,表示年輕代與年老代比值為1:3,年輕代占整個年輕代年老代和的1/4

  -XX:SurvivorRatio=n:年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區占整個年輕代的1/5

  -XX:MaxPermSize=n:設定持久代大小

收集器設定

  -XX:+UseSerialGC:設定串行收集器            新生代Serial和老年代SerialOld

  -XX:+UseParallelGC:設定并行收集器         新生代使用Parallel,老年代使用SerialOld

  -XX:+UseParNewGC:設定并行收集器              新生代使用ParNew,老年代使用SerialOld;

  -XX:+UseParalledlOldGC:設定并行年老代收集器      新生代使用Parallel,老年代使用ParallelOld;

  -XX:+UseConcMarkSweepGC:設定并發收集器         新生代使用ParNew,老年代使用CMS;

垃圾回收統計資訊

    -XX:+PrintGC                                 簡單GC日志模式

    -XX:+PrintGCDetails      就開啟了詳細GC日志模式。在這種模式下,日志格式和所使用的GC算法有關

    -XX:+PrintGCTimeStamps    可以将時間和日期也加到GC日志中。表示自JVM啟動至今的時間戳會被添加到每一行中

    -Xloggc:filename

并行收集器設定

  -XX:ParallelGCThreads=n:設定并行收集器收集時使用的CPU數。并行收集線程數。

  -XX:MaxGCPauseMillis=n:設定并行收集最大暫停時間

  -XX:GCTimeRatio=n:設定垃圾回收時間占程式運作時間的百分比。公式為1/(1+n)

并發收集器設定

  -XX:+CMSIncrementalMode:設定為增量模式。适用于單CPU情況。

  -XX:ParallelGCThreads=n:設定并發收集器年輕代收集方式為并行收集時,使用的CPU數。并行收集線程數。

java -Xmx3550m -Xms3550m -Xmn2g –Xss128k

-Xmx3550m:設定JVM最大可用記憶體為3550M。

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

-Xmn2g:設定年輕代大小為2G。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,是以增大年輕代後,将會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。

-Xss128k:設定每個線程的堆棧大小。JDK5.0以後每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應用的線程所需記憶體大小進行調整。在相同實體記憶體下,減小這個值能生成更多的線程。但是作業系統對一個程序内的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。

java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

-XX:NewRatio=4:設定年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設定為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5

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

-XX:MaxPermSize=16m:設定持久代大小為16m。

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

調優總結

年輕代大小選擇

  響應時間優先的應用:盡可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇)。在此種情況下,年輕代收集發生的頻率也是最小的。同時,減少到達年老代的對象。

  吞吐量優先的應用:盡可能的設定大,可能到達Gbit的程度。因為對響應時間沒有要求,垃圾收集可以并行進行,一般适合8CPU以上的應用。

年老代大小選擇

  響應時間優先的應用:年老代使用并發收集器,是以其大小需要小心設定,一般要考慮并發會話率和會話持續時間等一些參數。如果堆設定小了,可以會造成記憶體碎片、高回收頻率以及應用暫停而使用傳統的标記清除方式;如果堆大了,則需要較長的收集時間。最優化的方案,一般需要參考以下資料獲得:

  1. 并發垃圾收集資訊

  2. 持久代并發收集次數

  3. 傳統GC資訊

  4. 花在年輕代和年老代回收上的時間比例

  減少年輕代和年老代花費的時間,一般會提高應用的效率

  吞吐量優先的應用

  一般吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代。原因是,這樣可以盡可能回收掉大部分短期對象,減少中期的對象,而年老代盡存放長期存活對象。

tomcat設定JVM參數

在Windows下,在$TOMCAT_HOME/bin/catalina.bat,Linux下,在$TOMCAT_HOME/bin/catalina.sh的前面,增加如下設定:

JAVA_OPTS=”-Xms [min heap size]-Xmx[max heap size]”

例如:

JAVA_OPTS="-Xms1024m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m"


           

    -XX:MaxTenuringThreshold=n  假設值為n,則新生代的對象最多經曆n次GC,就能晉升老年代,但這個必不是晉升的必要條件

 -XX:TargetSurvivorRatio=n  用于設定Survivor區的目标使用率,即當Survivor區GC後使用率超過這個值,就可能會使用較小的年齡作為晉升年齡。預設為50

 

轉載請注明出處;