jvm的GC的一些學習總結一

一:java內存區的簡單介紹 

1、堆(Heap)

JVM管理的內存叫堆。在32Bit操作系統上有1.5G-2G的限制,而64Bit的就沒有。

JVM初始分配的內存由-Xms指定,默認是物理內存的1/64但小於1G。

JVM最大分配的內存由-Xmx指定,默認是物理內存的1/4但小於1G。

默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指定。  
默認空餘堆內存大於70%時,JVM會減少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=指定。

服務器一般設置-Xms、-Xmx相等以避免在每次GC 後調整堆的大小。   


2、分代 
    分代是Java垃圾收集的一大亮點,根據對象的生命週期長短,把堆分爲3個代:Young,Old和Permanent,根據不同代的特點採用不同的收集算法,揚長避短也。
 
Young(Nursery),年輕代。研究表明大部分對象都是朝生暮死,隨生隨滅的。因此所有收集器都爲年輕代選擇了複製算法。
    複製算法優點是隻訪問活躍對象,缺點是複製成本高。因爲年輕代只有少量的對象能熬到垃圾收集,因此只需少量的複製成本。而且複製收集器只訪問活躍對象,對那些佔了最大比率的死對象視而不見,充分發揮了它遍歷空間成本低的優點。

  1. Young(年輕代)
    年 輕代分三個區。一個Eden區,兩個Survivor區。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被複制到Survivor區 (兩個中的一個),當這個Survivor區滿時,此區的存活對象將被複制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一 個Survivor區複製過來的並且此時還存活的對象,將被複制“年老區(Tenured)”。需要注意,Survivor的兩個區是對稱的,沒先後關 系,所以同一個區中可能同時存在從Eden複製過來 對象,和從前一個Survivor複製過來的對象,而複製到年老區的只有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空 的。
  2. Tenured(年老代)
    年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象。
  3. Perm(持久代)
    用 於存放靜態文件,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等, 在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:MaxPermSize=<N>進行設置。
二:Gc的基本概念 
gc分爲full gc 跟 minor gc,當每一塊區滿的時候都會引發gc。
  1. Scavenge GC 
    一般情況下,當新對象生成,並且在Eden申請空間失敗時,就觸發了Scavenge GC,堆Eden區域進行GC,清除非存活對象,並且把尚且存活的對象移動到Survivor區。然後整理Survivor的兩個區。
  2. Full GC 
    對整個堆進行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,因此應該儘可能減少Full GC。有如下原因可能導致Full GC:
    • Tenured被寫滿
    • Perm域被寫滿
    • System.gc()被顯示調用
    • 上一次GC之後Heap的各域分配策略動態變化

三:GC的相關介紹
 

1:垃圾收集器介紹。 

# 串行收集器 
    使用單線程處理所有垃圾回收工作,因爲無需多線程交互,所以效率比較高。但是,也無法使用多處理器的優勢,所以此收集器適合單處理器機器。當然,此收集器也可以用在小數據量(100M左右)情況下的多處理器機器上。可以使用-XX:+UseSerialGC打開。
# 並行收集器 
   1. 對年輕代進行並行垃圾回收,因此可以減少垃圾回收時間。一般在多線程多處理器機器上使用。使用-XX:+UseParallelGC.打開。並行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中進行了增強--可以堆年老代進行並行收集。如果年老代不使用併發收集的話,是使用單線程進行垃圾回收,因此會制約擴展能力。使用-XX:+UseParallelOldGC打開。
   2. 使用-XX:ParallelGCThreads=<N>設置並行垃圾回收的線程數。此值可以設置與機器處理器數量相等。
   3. 此收集器可以進行如下配置:
          * 最大垃圾回收暫停:指定垃圾回收時的最長暫停時間,通過-XX:MaxGCPauseMillis=<N>指定。<N>爲毫秒.如果指定了此值的話,堆大小和垃圾回收相關參數會進行調整以達到指定值。設定此值可能會減少應用的吞吐量。
          * 吞吐量:吞吐量爲垃圾回收時間與非垃圾回收時間的比值,通過-XX:GCTimeRatio=<N>來設定,公式爲1/(1+N)。例如,-XX:GCTimeRatio=19時,表示5%的時間用於垃圾回收。默認情況爲99,即1%的時間用於垃圾回收。

# 併發收集器 
可以保證大部分工作都併發進行(應用不停止),垃圾回收只暫停很少的時間,此收集器適合對響應時間要求比較高的中、大規模應用。使用-XX:+UseConcMarkSweepGC打開。

   1. 併發收集器主要減少年老代的暫停時間,他在應用不停止的情況下使用獨立的垃圾回收線程,跟蹤可達對象。在每個年老代垃圾回收週期中,在收集初期併發收集器會對整個應用進行簡短的暫停,在收集中還會再暫停一次。第二次暫停會比第一次稍長,在此過程中多個線程同時進行垃圾回收工作。
   2. 併發收集器使用處理器換來短暫的停頓時間。在一個N個處理器的系統上,併發收集部分使用K/N個可用處理器進行回收,一般情況下1<=K<=N/4。
   3. 在只有一個處理器的主機上使用併發收集器,設置爲incremental mode模式也可獲得較短的停頓時間。
   4. 浮動垃圾:由於在應用運行的同時進行垃圾回收,所以有些垃圾可能在垃圾回收進行完成時產生,這樣就造成了“Floating Garbage”,這些垃圾需要在下次垃圾回收週期時才能回收掉。所以,併發收集器一般需要20%的預留空間用於這些浮動垃圾。
   5. Concurrent Mode Failure:併發收集器在應用運行時進行收集,所以需要保證堆在垃圾回收的這段時間有足夠的空間供程序使用,否則,垃圾回收還未完成,堆空間先滿了。這種情況下將會發生“併發模式失敗”,此時整個應用將會暫停,進行垃圾回收。
   6. 啓動併發收集器:因爲併發收集在應用運行時進行收集,所以必須保證收集完成之前有足夠的內存空間供程序使用,否則會出現“Concurrent Mode Failure”。通過設置-XX:CMSInitiatingOccupancyFraction=<N>指定還有多少剩餘堆時開始執行併發收集

# 小結

    * 串行處理器:
       --適用情況:數據量比較小(100M左右);單處理器下並且對響應時間無要求的應用。
       --缺點:只能用於小型應用
    * 並行處理器:
       --適用情況:“對吞吐量有高要求”,多CPU、對應用響應時間無要求的中、大型應用。舉例:後臺處理、科學計算。
       --缺點:應用響應時間可能較長
    * 併發處理器:傳說中的CMS
       --適用情況:“對響應時間有高要求”,多CPU、對應用響應時間有較高要求的中、大型應用。舉例:Web服務器/應用服務器、電信交換、集成開發環境。

2.基本收集算法 

   1. 複製:將堆內分成兩個相同空間,從根(ThreadLocal的對象,靜態對象)開始訪問每一個關聯的活躍對象,將空間A的活躍對象全部複製到空間B,然後一次性回收整個空間A。
      因爲只訪問活躍對象,將所有活動對象複製走之後就清空整個空間,不用去訪問死對象,所以遍歷空間的成本較小,但需要巨大的複製成本和較多的內存。
   2. 標記清除(mark-sweep):收集器先從根開始訪問所有活躍對象,標記爲活躍對象。然後再遍歷一次整個內存區域,把所有沒有標記活躍的對象進行回收處理。該算法遍歷整個空間的成本較大暫停時間隨空間大小線性增大,而且整理後堆裏的碎片很多。
   3. 標記整理(mark-sweep-compact):綜合了上述兩者的做法和優點,先標記活躍對象,然後將其合併成較大的內存塊。
    可見,沒有免費的午餐,無論採用複製還是標記清除算法,自動的東西都要付出很大的性能代價。  

四:調整jboss後的相關參數來查看gc 
通過在java的ops後面添加相關參數來查看gc,並輸出到日誌。
在set JAVA_OPTS=%JAVA_OPTS% -Xms128m -Xmx512m  後面加上:
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+HeapDumpOnOutOfMemoryError -Xloggc:d:/gc.log

其中的PrintGCDetails 打印gc的詳細信息。
PrintGCTimeStamps 打印gc的時間戳
-Xloggc:d:/gc.log 將日誌信息輸出到log,如果不指定就直接在jboss控制檯輸出了。
-XX:+HeapDumpOnOutOfMemoryError   可加可不加,特殊需要,我也不曉得是什麼意思。

jvm有關參數設置,可以查看http://blogs.sun.com/watt/resource/jvm-options-list.html

gc打印出來的詳情一般爲:
[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]  
這表示新生代大小整理前8614K,整理後還有781K(有可能爲0,表示都清掉了),後面的9088表示總大小。
後面的就表示對整個堆空間而言。整理前118250K,整理後113543K,總大小爲130112K。

五:常見jvm的參數設置 
一般設置爲:(參考江南白衣。。。)
-server -Xms<heapsize></heapsize>M -Xmx<heapsize></heapsize>M -XX: + UseConcMarkSweepGC  -XX:+PrintGC Details  -XX:+PrintGCTimeStamps
除此之外擴展的有一些典型設置: 
1:堆大小設置 

JVM 中最大堆大小有三方面限制:相關操作系統的數據模型(32-bt還是64-bit)限制;系統的可用虛擬內存限制;系統的可用物理內存限制。32位系統下,一般限制在1.5G~2G;64爲操作系統對內存無限制。我在Windows Server 2003 系統,3.5G物理內存,JDK5.0下測試,最大可設置爲1478m。
典型設置:

    * 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區進行多次複製,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概論。
2:相關輔助參數 
JVM提供了大量命令行參數,打印信息,供調試使用。主要有以下一些:
    * -XX:+PrintGC 
      輸出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]

                      [Full GC 121376K->10414K(130112K), 0.0650971 secs]
    * -XX:+PrintGCDetails 
      輸出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
                      [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]
    * -XX:+PrintGCTimeStamps  -XX:+PrintGC :PrintGCTimeStamps 可與上面兩個混合使用
      輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
    * -XX:+PrintGCApplicationConcurrentTime :打印每次垃圾回收前,程序未中斷的執行時間。可與上面混合使用
      輸出形式:Application time: 0.5291524 seconds
    * -XX:+PrintGCApplicationStoppedTime :打印垃圾回收期間程序暫停的時間。可與上面混合使用
      輸出形式:Total time for which application threads were stopped: 0.0468229 seconds
    * -XX:PrintHeapAtGC :打印GC前後的詳細堆棧信息
      輸出形式:
      34.702: [GC {Heap before gc invocations=7:
       def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,  99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
      from space 6144K,  55% used [0x221d0000, 0x22527e10, 0x227d0000)
        to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
       tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
       def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
        from space 6144K,  55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
        to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)
       tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      }
      , 0.0757599 secs]
    * -Xloggc:filename:與上面幾個配合使用,把相關日誌信息記錄到文件以便分析。


六:threadDump和檢測內存泄露 
在windwos下對這jboss用CTRL+BreaK 可以dump信息。
linux下的採用:
 
killall -3 java(總算弄成功了。。。) 
jdk6.0可以用jmp來進行分析。
首先用
jps -lv 查出jdk的pid,進程id。
然後用
jmap -dump:file=heap_file_name pid
接着來解析dump文件
jhat -J-mx1024m  heap_file_name
其中的-J-mx 1024 必須的 不然會報錯說 stackOverFlow.
等待提示說server is ready
然後打開http://localhost:7000就可以查看了,
提醒一下這個頁面最先面有個連接
shou heap histogram ” 點這個可以看每個對象的數量和大小。
另外還提供QQL 查詢語言。沒搞過。



參考資料:
http://calvin.iteye.com/blog/91905    
http://calvin.iteye.com/blog/91903
http://pengjiaheng.spaces.live.com/blog/cns!2DAA368B386E6AEA!770.entry

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章