JVM調優

 1.數據類型java

    java虛擬機中,數據類型能夠分爲兩類:基本類型和引用類型。算法

    基本類型的變量保存原始值,即:它表明的值就是數值自己,而引用類型的變量保存引用值。數組

    「引用值」表明了某個對象的引用,而不是對象自己,對象自己存放在這個引用值所表示的地址的位置。緩存

    基本類型包括:byte、short、int、long、char、float、double、boolean服務器

    引用類型包括:類類型、接口類型和數組數據結構

byte多線程

1B(8位)併發

-128 ~ 127模塊化

0函數

short

2B(16位)

-215  ~ 215-1

0

Int

4B(32位)

-231 ~ 231-1

0

long

8B(64位)

-263 ~ 263-1

0

char

2B(16位)

0 ~ 216-1

\U0000

float

4B(32位)

1.4013E-45 ~3.4028E+38

0.0F

double

8B(64位)

4.9E-324 ~1.7977E+308

0.0D

boolean

1B(8位)

True, false

false

2. 堆(heap)與棧(stack)

    堆和棧是程序運行的關鍵,頗有必要它他們的關係說清楚。

    在java中,Main函數就是棧的起始點,也是程序的起始點。程序要運行老是有一個起點的(程序執行的入口)。

 

歸納:   

 1  棧是運行時的單位 , 而堆是存儲的單元

 2 棧解決程序的運行問題,即程序如何執行,或者說如何處理數據,

    堆解決的是數據存儲的問題,即數據怎麼放,放在哪兒

java中一個線程就會相應有一個線程棧與之對應,這點很容易理解,由於不一樣的線程執行邏輯有所不一樣,所以須要一個獨立的線程棧。

而堆則是全部線程共享的。

   

 疑問一:爲何要把堆和棧區分出來呢?棧中不是也能夠存儲數據嗎?

     1. 從軟件設計的角度看,棧表明了處理邏輯,而堆表明了數據。這樣分開,使得處理邏輯更爲清晰。分而治之的思想。

        這種隔離、模塊化的思想在軟件設計的方方面面都有體現。

     2.堆與棧的分離,使得堆中的內容能夠被多個棧共享(也能夠理解爲多個線程訪問同一個對象)。

        好處:  a.提供了一種有效的數據交互方式(如:共享內存)

                 b.堆中的共享常量和緩存能夠被全部棧訪問,節省了空間。

     3. 棧由於運行時的須要,好比保存系統運行的上下文,須要進行地址段的劃分。

        因爲棧只能向上增加,所以就會限制住棧存儲內容的能力,

        而堆不一樣,堆中的對象是能夠根據須要動態增加的,

        所以棧和堆的拆分使得動態增加成爲可能,相應棧中只需記錄堆中的一個地址便可。

     4. 面向對象就是堆和棧的完美結合。

        其實,面向對象方式的程序與之前結構化的程序在執行上沒有任何區別。

        可是,面向對象的引入,使得對待問題的思考方式發生了改變,而更接近於天然方式的思考。

        當咱們把對象拆開,你會發現,對象的屬性其實就是數據,存放在堆中;

        而對象的行爲(方法),就是運行邏輯,放在棧中。

        咱們在編寫對象的時候,其實就是編寫了數據結構,也編寫了處理數據的邏輯。不得不認可,面向對象的設計,確實很美。

 疑問二:  堆中存什麼?棧中存什麼?

      1. 棧存儲的信息都是跟當前線程(或程序)相關的信息。(局部變量、程序運行狀態、方法、方法返回值)等,

         棧中存的是基本數據類型和堆中對象的引用。一個對象的大小是不可估計的,或者說是能夠動態變化的,可是

         在棧中,一個對象只對應了一個4byte的引用(堆棧分離的好處)。

     2. 堆只負責存儲對象信息。

 疑問三:  爲何不把基本類型放堆中呢?

     1. 其佔用的空間通常是1~8個字節---須要空間比較少,

     2.並且由於是基本類型,因此不會出現動態增加的狀況---長度固定,所以棧中存儲就夠了,若是把它存在堆中是沒有什麼意義的(還會浪費空間,後面說明??)。

疑問四:  java中的參數傳遞是傳值呢?仍是傳引用?

     對象傳遞是引用值傳遞,原始類型數據傳遞是值傳遞

     實際上這個傳入函數的值是對象引用的拷貝,即傳遞的是引用的地址值,因此仍是按值傳遞

     tips:

      堆和棧中,棧是程序運行最根本的東西。程序運行能夠沒有堆,可是不能沒有棧。

      而堆是爲棧進行數據存儲服務的,說白了堆就是一塊共享的內存。

      不過,正是由於堆和棧的分離的思想,才使得java的垃圾回收成爲可能。

      java中,棧的大小經過-Xss來設置,當棧中存儲的數據比較多時,須要適當調大這個值,不然會出現 java.lang.StackOverflowError異常。

                  常見的出現這個異常的是沒法返回的遞歸,由於此時棧中保存的信息都是方法返回的記錄點。

 疑問五:  java對象的大小如何計算?

     基本數據類型的大小是固定的,這裏就很少說了,對於非基本類型的java對象,其大小就值得商討。

     在java中,一個空Object對象的大小是8byte,這個大小隻是保存堆中一個沒有任何屬性的對象的大小。看看下面語句:

               Object  ob = new  Object();

     這樣在程序中完成了一個java對象的聲明,可是它所佔的空間爲:4byte+8byte。

   (4byte是上面部分所說的java棧中保存引用的所須要空間,而那8byte則是java堆中對象的信息)。

     由於全部的java非基本類型的對象都須要默認繼承Object對象,所以不論什麼樣的java對象,其大小都必須是大於8byte。

     有了Object對象的大小,咱們就能夠計算其餘對象的大小了。

       

       其大小爲:空對象大小(8byte)+int大小(4byte)+Boolea大小(1byte)+空Object引用的大小(4byte)=17byte。

       可是由於java在對對象內存分配時都是以8的整數倍來分的,所以大於17byte的最接近8的整數倍的是24,所以此對象的大小爲24byte。

       這裏須要注意一下基本類型的包裝類型的大小。由於這種包裝類型已經成爲對象了,所以須要把它們做爲對象來看待。

       包裝類型的大小至少是12byte(聲明一個空Object至少須要的空間),並且12byte沒有包含任何有效信息,

       同時,由於java對象大小是8的整數倍,所以一個基本類型包裝類的大小至少是16byte。

       這個內存佔用是很恐怖的,它是使用基本類型的N倍(N>2),這些類型的內存佔用更是誇張。所以,可能的話應儘可能少使用包裝類。

       在JDK5.0之後,由於加入了自動類型裝換,所以,java虛擬機會在存儲方面進行相應的優化。

 

 3. 引用類型

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

       強引用:咱們通常聲明對象時虛擬機生成的引用,

                  強引用環境下,垃圾回收時須要嚴格判斷當前對象是否被強引用,若是被強引用,則不會被垃圾回收。

              eg:  Sample sample = new Sample(); 

                    建立一個對象,new出來的對象都是分配在java堆中的

       軟引用:通常被做爲緩存來使用。

                 與強引用的區別是,軟引用在垃圾回收時,虛擬機會根據當前系統的剩餘內存來決定是否對軟引用進行回收。

                 若是剩餘內存比較緊張,則虛擬機會回收軟引用所引用的空間,若是剩餘內存相對富裕,則不會進行回收。

                 換句話說,虛擬機在發生OutOfMemory時,確定是沒有軟引用存在的。

       弱引用:弱引用與軟引用相似,都是做爲緩存來使用。

                 但與軟引用不一樣,弱引用在進行垃圾回收時,是必定會被回收掉的,

                 所以其生命週期只存在於一個垃圾回收週期內。

        虛引用 :顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。   
                   若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。
                   虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
       虛引用與軟引用和弱引用的一個區別在於:
                 虛引用必須和引用隊列(ReferenceQueue)聯合使用。
                 當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中。
ReferenceQueue queue =  new ReferenceQueue ();
PhantomReference pr =  new PhantomReference (object, queue);
        程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。
        若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。
 

4.  垃圾回收算法

     a) 按照基本回收策略分

        a1. 引用計數(Reference Counting)

             比較古老的回收算法。原理是此對象有一個引用,即增長一個計數,刪除一個引用則減小一個計數。

             垃圾回收時,只收集計數爲0的對象。此算法最致命的是沒法處理循環引用的問題。

        a2. 標記-清除(Mark-Sweep)

              

                此算法執行分兩階段:

                     第一階段從引用根節點開始標記全部被引用的對象,

                     第二階段遍歷整個堆,把未標記的對象清除。

                此算法須要暫停整個應用,同時,會產生內存碎片。

        a3. 複製(Copying)

                 

             此算法把內存空間劃分爲兩個相等的區域,每次只是用其中一個區域。

             垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另一個區域中。

             此算法每次只處理正在使用中的對象,所以複製成本比較小,同時複製過去之後還能進行相應的內存整理,不會出現「碎片」問題。

             固然,此算法的缺點也是比較明顯的,就是須要兩倍內存空間。

       a4. 標記-整理(Mark-Compact)

                  

            此算法結合了「標記-清除」和「複製」兩個算法的優勢。

            也是分兩個階段,第一階段從根節點開始標記全部被引用對象,

            第二階段遍歷整個堆,清除未標記對象而且把存活對象「壓縮」到堆中的其中一塊,按順序排放。

            此算法避免了「標記-清除」的碎片問題,同時也避免了「複製」算法的空間問題。

     b) 按分區對待的方式分

            增量收集(Incremental Collecting):

                 實時垃圾回收算法,即:在應用進行的同時進行垃圾回收。不知道什麼緣由JDK5.0中的收集器沒有使用這種算法。

           分代收集(Generational Collecting):

                 基於對對象生命週期分析後得出的垃圾回收算法。把對象分爲年輕代年老代、持久代

                 對不一樣生命週期的對象使用不一樣的算法(上述方式中的一個)進行回收。如今的垃圾回收器(從J2SE1.2開始)都是使用此算法的。

    c) 按系統線程分

           串行收集:

                 使用單線程處理全部垃圾回收工做,由於無需多線程交互,實現容易,並且效率比較高。

                 可是,其侷限性也比較明顯,即沒法使用多處理器的優點,因此此收集適合單處理器機器。

                 固然,此收集器也能夠用在小數據量(100M左右)狀況下的多處理器機器上。

           並行收集:

                 並行收集使用多線程處理垃圾回收工做,於是速度快,效率高。

                 並且理論上CPU數目越多,越能體現出並行收集器的優點。

          併發收集:

                GC線程和應用線程大部分時間是併發執行,只是在初始標記(initial mark)和二次標記(remark)時須要stop-the-world,

                這能夠大大縮短停頓時間(pause time),因此適用於響應時間優先的應用,減小用戶等待時間。

                因爲GC是和應用線程併發執行,只有在多CPU場景下才能發揮其價值,在執行過程當中還會產生新的垃圾floating garbage,

                若是等空間滿了再開始GC,那這些新產生的垃圾就沒地方放了,這時就會啓動一次串行GC,等待時間將會很長,因此要在空間還未滿時就要啓動GC。

                mark和sweep操做會引發不少碎片,因此間隔一段時間須要整理整個空間,不然遇到大對象,沒有連續空間也會啓動一次串行GC。

               採用此收集器(如tenured generation),收集頻率不能大,不然會影響到cpu的利用率,進而影響吞吐量。

 

5.  如何區分垃圾

        上面說到的「引用計數」法,經過統計控制生成對象和刪除對象的引用數來判斷。

        垃圾回收程序收集計數爲0的對象便可。可是這種方法沒法解決循環引用。因此,後來實現的垃圾判斷算法中,都是從程序運行的根節點出發,遍歷整個對象引用,查找存活的對象,那麼在這種方式的實現中,垃圾回收從哪兒開始的呢?即,從哪兒開始查找哪些對象是正在被當前系統使用的,上面分析的堆和棧的區別,其中棧是真正進行程序執行的地方,因此要獲取哪些對象正在被使用,則須要從java棧開始。同時,一個棧是與一個線程對應的,所以,若是有多個線程的話,則必須對這些線程對應的全部的棧進行檢查。

        同時,除了棧外,還有系統運行時的寄存器等,也是存儲程序運行數據的。這樣,以棧或寄存器中的引用爲起點,咱們能夠找到堆中的對象,又從這些對象找到對堆中其它對象的引用,這種引用逐步擴展,最終以null引用或者基本類型結束,這樣就造成了一顆以java棧中引用所對應的對象爲根節點的一顆對象樹,若是棧中有多個引用,則最終會造成多顆對象樹。在這些對象樹上的對象,都是當前系統運行所須要的對象,不能被垃圾回收,而其餘剩餘對象,則能夠視爲沒法被引用到的對象,能夠被當作垃圾進行回收。

        所以,垃圾回收的起點是一些根對象(java棧、靜態變量、寄存器...)。而最簡單的java棧就是java程序執行的main函數。這種回收方式,也是上面提到的「標記-清除」的回收方式。

        如何處理碎片

        因爲不一樣java對象存活時間是不必定的,所以,在程序運行一段時間之後,若是不進行內存整理,就會出現零散的內存碎片。碎片最直接的問題就是會致使沒法分配大塊的內存空間,以及程序運行效率下降。因此,在上面提到的基本垃圾回收算法中 ,「複製」方式和「標記-整理」方式,均可以解決碎片的問題。

        如何解決同時存在的對象建立和對象回收問題

        垃圾回收線程是回收內存的,而程序運行線程則是消耗(或分配)內存的,一個回收內存,一個分配內存,從這點看,二者是矛盾的。所以,在現有的垃圾回收方式中,要進行垃圾回收前,通常都須要暫停整個應用(即:暫停內存的分配),而後進行垃圾回收,回收完成後再繼續應用。這種實現方式是最直接,並且最有效的解決兩者矛盾的方式。

        可是這種方式有一個很明顯的弊端,就是當堆空間持續增大時,垃圾回收的時間也將會相應的持續增大,相應應用暫停的時間也會相應的增大。一些相應時間要求很高的應用,好比最大暫停時間要求是幾百毫秒,那麼當堆空間大於幾個G時,就頗有可能超過這個限制,在這種狀況下,垃圾回收將會成爲系統運行的一個瓶頸。爲解決這種矛盾,有了併發垃圾回收算法,使用這種算法,垃圾回收線程與程序運行線程同時運行。在這種方式下,解決了暫停的問題,可是由於須要在新生成對象的同時又要回收對象,算法複雜性會大大增長,系統的處理能力也會相應下降,同時,「碎片」問題將會比較難解決,之後研究的重點!!!!!!。

        爲何要分代

        分代的垃圾回收策略,是基於這樣一個事實:不一樣的對象的生命週期是不同的。所以,不一樣聲明週期的對象能夠採起不一樣的收集方式,以便提升回收效率。

        在Java程序運行的過程當中,會產生大量的對象,其中有些對象是與業務信息相關,好比Http請求中的Session對象、線程、Socket鏈接,這類對象跟業務直接掛鉤,所以生命週期比較長。可是還有一些對象,主要是程序運行過程當中生成的臨時變量,這些對象生命週期比較短,好比:String對象,因爲其不變類的特性,系統會產生大量的這些對象,有些對象甚至只用一次便可回收。

        是想,在不進行對象存活時間區分的狀況下,每次垃圾回收都是對整個堆空間進行回收,花費時間相對會長,同時,由於每次回收都須要遍歷全部存活對象,但實際上,對於生命週期長的對象而言,這種遍歷是沒有效果的,由於可能進行了不少次遍歷,可是它們依舊存在。所以,分代垃圾回收採用分治的思想,進行代的劃分,把不一樣生命週期的對象放在不一樣代上,不一樣代上採用最適合它的垃圾回收方式進行回收。

        如何分代

         如圖所示:

         虛擬機中共劃分了三個代:年輕代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。

         其中持久代主要存放的是java類的類信息,與垃圾收集要收集的java對象關係不大。年輕代年老代的劃分是對垃圾收集影響比較大的。

         年輕代

         全部新生成的對象首先都是放在年輕代的。年輕代的目標就是儘量快速的收集掉那些生命週期短的對象。年輕代分爲三個區。一個Eden區,兩個Survivor區(通常而言)。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被複制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活將被複制到另一個Survivor區,當這個Survivor區也滿了的時候,從第一個Survivor區複製過來的而且此時還存活的對象,將被複制「年老區(Tenured)」。須要注意,Survivor的兩個區是對稱的,沒前後關係,因此同一個區中可能同時存在從Eden複製過來的對象和從前一個Survivor複製過來的對象,而複製到年老區的只有從第一個Survivor區過來的對象。並且,Survivor區總有一個是空的。同時,根據程序須要,Survivor區是能夠配置爲多個的(多於兩個),這樣能夠增長對象在年輕代中的存在時間,減小被放到年老代的可能。

         年老代

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

         持久代:

         用於存放靜態文件,如java類、方法等。持久代對垃圾回收沒有顯著影響,可是有些應用可能動態生成或者調用一些class,例如Hibernate等,在這種時候須要設置一個比較大的持久空間來存放這些運行過程當中新增的類。持久代大小經過 -XX:MaxPermSize = <N> 進行設置。

        什麼狀況下觸發垃圾回收

        因爲對象進行了分代處理,所以垃圾回收區域、時間也不同。GC有兩種類型:Scavenge GC 和 Full GC

        Scavenge GC

        通常狀況下,當新對象生成,而且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,而且把尚且存活的對象移動到Survivor區。而後整理Survivor的兩個區。這種方式的GC是對年輕代的Eden區進行,不會影響到年老代。由於大部分對象都是從Eden區開始的,同時Eden區不會分配的很大,因此Eden區的GC會頻繁進行。於是,通常在這裏須要使用速度快、效率高的算法,使Eden區能儘快空閒出來。

         Full GC

         對整個堆進行整理,包括Young、Tenured 和 Perm。Full GC 由於須要對整個堆進行回收,因此比 Scavenge GC 要慢,所以應該儘量減小 Full GC 的次數。在對JVM調優的過程當中,很大一部分工做就是對於 Full GC 的調節。

有以下緣由可能致使Full GC:
         . 年老代(Tenured)被寫滿

         . 持久代(Perm)被寫滿

         . System.gc()被顯式調用

         . 上一次GC以後Heap的各域分配策略動態變化

         分代垃圾回收流程示意

        選擇合適的垃圾收集算法

        串行收集器

        用單線程處理全部垃圾回收工做,由於無需多線程交互,全部效率比較高。可是,也沒法使用多處理器的優點,因此此收集器適合單處理器機器。固然,此收集器也能夠用在小數據量(100M左右)狀況下的多處理器機器上。可使用 -XX:+UseSerialGC打開。

         並行收集器

        對年輕代進行並行垃圾回收,所以能夠減小垃圾回收時間。通常在多線程多處理器機器上使用。使用 -XX:+UseParallelGC 打開。並行收集器在 J2SE5.0第六6更新上引入,在java SE6.0中進行了加強 --- 能夠對年老代進行並行收集。若是年老代不使用並行收集的話,默認是使用單線程進行垃圾回收,所以會制約擴展能力。使用 -XX:+UseParallelOldGC打開。

        使用 -XX:ParallelGCThreads = <N> 設置並行垃圾回收的線程數。此值能夠設置與機器處理器數量相等。

        此收集器能夠進行以下配置:

                最大垃圾回收暫停:指定垃圾回收時的最長暫停時間,經過-XX:MaxGCPauseMillis = <N>指定。<N> 爲毫秒,若是指定了此值的話,堆大小和垃圾回收相關參數會進行調整以達到指定值。設定此值可能會減小應用的吞吐量。

                吞吐量:吞吐量爲垃圾回收時間與非垃圾回收時間的比值,經過-XX:GCTimeRatio = <N> 來設定,公式爲 1/(1 + N)。例如,-XX:GCTimeRatio = 19時,表示5%的時間用於垃圾回收。默認狀況爲99,即1%的時間用於垃圾回收。

        併發收集器

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

        併發收集器主要減小年老代的暫停時間,它在應用不中止的狀況下使用獨立的垃圾回收線程,跟蹤可達對象。在每一個年老代垃圾回收週期中,在收集初期併發收集器會對整個應用進行簡短的暫停。在收集中還會再暫停一次。第二次暫停會比第一次稍長,在此過程當中多個線程同時進行垃圾回收工做。

        併發收集器使用處理器換來短暫的停頓時間。在一個N個處理器的系統上,併發收集部分使用 k/N 個可用處理器進行回收,通常狀況下 1 <= k <= N / 4。

        在只有一個處理器的主機上使用併發收集器,設置爲 incremental mode 模式也可得到較短的停頓時間。

        浮動垃圾:因爲在應用運行的同時進行垃圾回收,因此有些垃圾可能在垃圾回收進行完成時產生,這樣就形成了「Floating Garbage」,這些垃圾須要在下次垃圾回收週期時才能回收掉。因此,併發收集器通常須要20%的預留空間用於這些浮動垃圾。

        Concurrent Mode Failure:併發收集器在應用運行時進行收集,因此須要保證堆在垃圾回收的這段時間有足夠的空間供程序使用,不然,垃圾回收還未完成,堆空間先滿了。這種狀況下將會發生「併發模式失敗」,此時整個應用將會暫停,進行垃圾回收。

        啓動併發收集器:由於併發收集在應用運行時進行收集,因此必須保證收集完成以前有足夠的內存空間供程序使用,不然會出現「Concurrent Mode Failure」。經過設置 -XX:CMSInitiatingOccupancyFraction = <N> 指定還有多少剩餘堆是開始執行併發收集。

        小結

         串行處理器:

         -- 適用狀況:數據量比較小(100M左右),單處理器下而且對相應時間無要求的應用。

         -- 缺點:只能用於小型應用。

         並行處理器:

          -- 適用狀況:「對吞吐量有高要求」,多CPU,對應用過響應時間無要求的中、大型應用。舉例:後臺處理、科學計算。

          -- 缺點:垃圾收集過程當中應用響應時間可能加長。

         併發處理器:

          -- 適用狀況:「對響應時間有高要求」,多CPU,對應用響應時間有較高要求的中、大型應用。舉例:Web服務器/應用服務器、電信交換、集成開發環境。

     

        如下配置主要針對分代垃圾回收算法而言。

        堆大小設置

        年輕代的設置很關鍵

        JVM中最大堆大小有三方面限制:相關操做系統的數據模型(32-bit 仍是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區進行屢次複製,這樣能夠增長對象在年輕代的存活時間,增長在年輕代被回收的機率。

 

         回收器選擇

         JVM給了三種選擇:串行收集器、並行收集器、併發收集器,可是串行收集器只適用於小數據量的狀況,因此這裏的選擇主要針對並行收集器和併發收集器。默認狀況下,JDK5.0之前都是使用串行收集器,若是想使用其餘收集器須要在啓動的時候加入相應參數。JDK5.0之後,JVM會根據當前系統配置進行判斷。

        吞吐量優先的並行收集器

        如上文所述,並行收集器主要以到達必定的吞吐量爲目標,適用於科學計算和後臺處理等。

        典型配置:

            java  -Xmx3800m  -Xms3800m  -Xmn2g  -Xss128k  -XX:+UseParallelGC  -XX:ParallelGCThreads=20

          -XX:+UseParallelGC:選擇垃圾收集器爲並行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用併發收集,而年老代仍舊使用串行收集

          -XX:+ParallelGCThreads=20:配置並行收集器的線程數,即:同時多少個線程一塊兒進行垃圾回收。此值最好配置與處理器數目相等。

           

            java  -Xmx3550m  -Xms3550m  -Xmn2g  -Xss128k  -XX:+UseParallelGC  -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

          -XX:+UseParallelOldGC:配置年老代垃圾收集方式爲並行收集。JDK6.0支持對年老代並行收集。

 

            java  -Xmx3550m  -Xms3550m  -Xmn2g  -Xss128k  -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100

           -XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間,若是沒法知足此時間,JVM會自動調整年輕代大小,以知足此值。

 

            java  -Xmx3550m  -Xms3550m  -Xmn2g  -Xss128k  -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100  -XX:+UseAdaptiveSizePolicy

           -XX:+UseAdaptiveSizePolicy:設置此選項後,並行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低響應時間或者收集頻率等,此值建議使用並行收集器時,一直打開。 

 

        響應時間優先的併發收集器

        如上文所述,併發收集器主要是保證系統的響應時間,減小垃圾收集時的停頓時間。適用於應用服務器、電信領域等。

        典型配置:

        java  -Xmx3550m  -Xms3550  -Xmn2g  -Xss128k  -XX:ParallelGCThreads=20  -XX:+UseConcMarkSweepGC  -XX:+UseParNewGC

        -XX:+UseConcMarkSweepGC:設置年老代爲併發收集。測試中配置這個之後,-XX:NewRatio=4的配置失效了,緣由不明。因此,此時年輕代大小最好用-Xmn設置。

        -XX:+UseParNewGC:設置年輕代爲並行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,因此無需再設置此值。

            

        java  -Xmx3550m  -Xms3550  -Xmn2g  -Xss128k  -XX:+UseConcMarkSweepGC  -XX:CMSFullGCsBeforeCompaction=5  -XX:+UseCMSCompactAtFullCollection 

        -XX:CMSFullGCsBeforeCompaction:因爲併發收集器不對內存空間進行壓縮、整理,因此運行一段時間後會產生「碎片」,使得運行效率下降。此值設置運行多少次GC之後對內存空間進行壓縮、整理。

        -XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,可是能夠消除碎片。

 

       常見配置彙總

       堆設置

           -Xms:初始堆大小

           -Xmx:最大堆大小

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

           -XX:NewRatio=n:設置年輕代年老代的比值。如:爲3,表示年輕代年老代比值爲1:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5。

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

        收集器設置

           -XX:+UseSerialGC:設置串行收集器

           -XX:+UseParallelGC:設置並行收集器

           -XX:+UseParalledlOldGC:設置並行年老代收集器

           -XX:+UseConcMarkSweepGC:設置併發收集器

        垃圾回收統計信息

           -XX:+PrintGC

           -XX:+PrintGCDetails

           -XX:+PrintGCTimeStamps

           -Xloggc:filename

         並行收集器設置

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

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

           -XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比。公式爲1/(1+N)

         併發收集器設置

           -XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU狀況。

           -XX:+ParallelGCThreads=n:設置併發收集器年輕代收集方式爲並行收集時,使用的CPU數。並行收集線程數。

相關文章
相關標籤/搜索