1. 程式人生 > >快樂程式設計師的專欄

快樂程式設計師的專欄

1、JVM記憶體區域模型

執行的基本流程:

Class檔案—》類裝載器—》記憶體區—》執行區—》本地庫介面—》本地方法庫

記憶體區(大體上分為5塊):重點是堆

1.方法區

也稱"永久代” 、“非堆”,  它用於儲存虛擬機器載入的類資訊、常量、靜態變數、是各個執行緒共享的記憶體區域。預設最小值為16MB,最大值為64MB,可以通過-XX:PermSize 和 -XX:MaxPermSize 引數限制方法區的大小,還可以選擇不實現垃圾回收在這裡,設定了也很少來這裡回收。所以,一般設定為64M最大值比較好。

2.虛擬機器棧

描述的是java 方法執行的記憶體模型:每個方法被執行的時候 都會建立一個“棧幀”用於儲存區域性變量表(包括引數)、操作棧、方法出口等資訊。每個方法被呼叫到執行完的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。宣告週期與執行緒相同,是執行緒私有的。

 區域性變量表存放了編譯器可知的各種基本資料型別(boolean、byte、char、short、int、float、long、double)、物件引用(引用指標,並非物件本身),其中64位長度的long和double型別的資料會佔用2個區域性變數的空間,其餘資料型別只佔1個。區域性變量表所需的記憶體空間在編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的區域性變數是完全確定的,在執行期間棧幀不會改變區域性變量表的大小空間。

3.本地方法棧

 與虛擬機器棧基本類似,區別在於虛擬機器棧為虛擬機器執行的java方法服務,而本地方法棧則是為Native方法服務。

4.堆 (垃圾收集器管理的主要區域,所以記憶體優化最大的地方就在這,以下介紹的都是32位的系統,64位系統可以吃更大的記憶體)

也叫做java 堆、GC堆是java虛擬機器所管理的記憶體中最大的一塊記憶體區域,也是被各個執行緒共享的記憶體區域,在JVM啟動時建立。該記憶體區域存放了物件例項及陣列(所有new的物件)。其大小通過-Xms(最小值)和-Xmx(最大值)引數設定,-Xms為JVM啟動時申請的最小記憶體,預設為作業系統實體記憶體的1/64但小於1G(32位的系統),-Xmx為JVM可申請的最大記憶體,預設為實體記憶體的1/4但小於1G(如果你的電腦記憶體小於1G,那麼佔用的最大記憶體是實體記憶體的80%),預設當空餘堆記憶體小於40%時,JVM會增大Heap到-Xmx指定的大小,可通過-XX:MinHeapFreeRation=來指定這個比列;當空餘堆記憶體大於70%時,JVM會減小heap的大小到-Xms指定的大小,可通過XX:MaxHeapFreeRation=來指定這個比列,對於執行系統,為避免在執行時頻繁調整Heap的大小,通常

-Xms與-Xmx的值設成一樣

堆又可以分為2塊 (年輕代 + 年老代)以下就介紹 年輕代和老年代↓

由於現在收集器都是採用分代收集演算法,堆被劃分為新生代和老年代。新生代主要儲存新建立的物件和尚未進入老年代的物件。老年代儲存經過多次新生代GC(Minor GC)任然存活的物件。

新生代:

 程式新建立的物件都是從新生代分配記憶體,新生代由Eden Space和兩塊相同大小的Survivor Space(通常又稱S0和S1或From和To)構成,可通過-Xmn引數來指定新生代的大小,也可以通過-XX:SurvivorRation來調整Eden Space及Survivor Space的大小。

所有新生成的物件首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的物件。年輕代分三個區。一個Eden區,兩個 Survivor區(一般而言)。

大部分物件在Eden區中生成。當Eden區滿時,還存活的物件將被複制到Survivor區(兩個中的一個),當一個Survivor區滿時,此區的存活物件將被複制到另外一個Survivor區,當另一個Survivor區也滿了的時候,從前一個Survivor區複製過來的並且此時還存活的物件,將被複制“年老區(Tenured)”。

需要注意,兩個Survivor區是對稱的,沒先後關係,所以同一個Survivor區中可能同時存在從Eden區複製過來物件,和從另一個 Survivor區複製過來的物件;而複製到年老區的只有從前一個Survivor區(相對的)過來的物件。而且,Survivor區總有一個是空的。特殊的情況下,根據程式需要,Survivor區是可以配置為多個的(多於兩個),這樣可以增加物件在年輕代中的存在時間,減少被放到年老代的可能。

老年代:

用於存放經過多次新生代GC任然存活的物件,例如快取物件,新建的物件也有可能直接進入老年代,主要有兩種情況:①.大物件,可通過啟動引數設定-XX:PretenureSizeThreshold=1024(單位為位元組,預設為0)來代表超過多大時就不在新生代分配,而是直接在老年代分配。②.大的陣列物件,切陣列中無引用外部物件。

在年輕代中經歷了N(可配置)次垃圾回收後仍然存活的物件,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命週期較長的物件。

老年代所佔的記憶體大小為-Xmx對應的值減去-Xmn對應的值。

持久代 :(這個應該在方法區記憶體中,拿到這裡說明,是為了更好的瞭解記憶體區的個個功能。)

用於存放靜態資料,如 Java Class, Method 等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者呼叫一些Class,例如 Hibernate 等,在這種時候需要設定一個比較大的持久代空間來存放這些執行過程中動態增加的型別。持久代大小通過 -XX:MaxPermSize= 進行設定。

5.程式計數器 

是最小的一塊記憶體區域,它的作用是當前執行緒所執行的位元組碼的行號指示器,在虛擬機器的模型裡,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、迴圈、異常處理、執行緒恢復等基礎功能都需要依賴計數器完成。

Tomcat 5常用優化和配置

1、聯結器優化:

在tomcat配置檔案server.xml中的配置中,和連線數相關的引數有:

maxThreads:

Tomcat使用執行緒來處理接收的每個請求。這個值表示Tomcat可建立的最大的執行緒數。預設值200。 

acceptCount:

指定當所有可以使用的處理請求的執行緒數都被使用時,可以放到處理佇列中的請求數,超過這個數的請求將不予處理。預設值10。 

minSpareThreads:

Tomcat初始化時建立的執行緒數。預設值4。 

maxSpareThreads:

一旦建立的執行緒超過這個值,Tomcat就會關閉不再需要的socket執行緒。預設值50。

enableLookups:

是否反查域名,預設值為true。為了提高處理能力,應設定為false connnectionTimeout:

網路連線超時,預設值60000,單位:毫秒。設定為0表示永不超時,這樣設定有隱患的。通常可設定為30000毫秒。

maxKeepAliveRequests:

保持請求數量,預設值100。

bufferSize:

輸入流緩衝大小,預設值2048 bytes。

compression:

壓縮傳輸,取值on/off/force,預設值off。

其中和最大連線數相關的引數為maxThreads和acceptCount。如果要加大併發連線數,應同時加大這兩個引數。web server允許的最大連線數還受制於作業系統的核心引數設定,通常Windows是2000個左右,Linux是1000個左右。

加大tomcat連線數:

在tomcat配置檔案server.xml中的配置中,和連線數相關的引數有:

minProcessors:最小空閒連線執行緒數,用於提高系統處理效能,預設值為10

maxProcessors:最大連線執行緒數,即:併發處理的最大請求數,預設值為75

acceptCount:允許的最大連線數,應大於等於maxProcessors,預設值為100

enableLookups:是否反查域名,取值為:true或false。為了提高處理能力,應設定為false

connectionTimeout:網路連線超時,單位:毫秒。設定為0表示永不超時,這樣設定有隱患的。通常可設定為30000毫秒。

其中和最大連線數相關的引數為maxProcessors和acceptCount。如果要加大併發連線數,應同時加大這兩個引數。

web server允許的最大連線數還受制於作業系統的核心引數設定,通常Windows是2000個左右,Linux是1000個左右。

如何調優JVM

-Xmx1024m:設定JVM最大堆記憶體 為1024M。 

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

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

-Xmn384m:設定堆記憶體年輕代 大小為384m。整個堆記憶體大小 = 年輕代大小 + 年老代大    小 所以增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,Sun    官方推薦配置為整個堆的3/8。 

-XX:PermSize=64M:設定堆記憶體持久代 初始值為64M。

-XX:MaxNewSize=size:新生成的物件能佔用記憶體的最大值。 

-XX:MaxPermSize=64M:設定持久代最大值為64M。 

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

-XX:SurvivorRatio=4:設定堆記憶體年輕代中Eden區與Survivor區大小的比值 。設定為4, 則兩個Survivor區(JVM堆記憶體年輕代中預設有2個Survivor區) 與一個Eden區的比值為2:4,一個Survivor區佔整個年輕代的1/6。 

-XX:MaxTenuringThreshold=7:表示一個物件如果在救助空間(Survivor區)移動7次還沒 有被回收就放入年老代。 如果設定為0的話,則年輕代物件不經過Survivor區,直接 進入年老代,對於年老代比較多的應用,這樣做可以提高效率。如果將此值設定為一個較大值,則年輕代物件會在Survivor區進行多次複製,這樣可以增加物件在年輕代存活時間,增加物件在年輕代即被回收的概率。 

回收器選擇

JVM給了三種選擇:序列收集器、並行收集器、併發收集器,但是序列收集器只適用於小資料量的情況,所以這裡的選擇主要針對並行收集器和併發收集器。

預設情況下,JDK5.0以前都是使用序列收集器,如果想使用其他收集器需要在啟動時加入相應引數。JDK5.0以後,JVM會根據當前系統配置進行智慧判斷。

序列收集器 

-XX:+UseSerialGC:設定序列收集器 

並行收集器(吞吐量優先) 

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

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

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

-XX:MaxGCPauseMillis=100:設定每次年輕代垃圾回收的最長時間(單位毫秒),如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值。 

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

此引數建議使用並行收集器時,一直開啟。 

併發收集器(響應時間優先) 

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

CMS,全稱Concurrent Low Pause Collector,是jdk1.4後期版本開始引入的新gc演算法,在jdk5和jdk6中得到了進一步改進,它的主要適合場景是對響應時間的重要性需求大於對吞吐量的要求,能夠承受垃圾回收執行緒和應用執行緒共享處理器資源,並且應用中存在比較多的長生命週期的物件的應用。CMS是用於對tenured generation的回收,也就是年老代的回收,目標是儘量減少應用的暫停時間,減少FullGC發生的機率,利用和應用程式執行緒併發的垃圾回收執行緒來標記清除年老代。 

-XX:+UseConcMarkSweepGC:設定年老代為併發收集。測試中配置這個以後,-XX:NewRatio=4的配置失效了。所以,此時年輕代大小最好用-Xmn設定。 

-XX:CMSFullGCsBeforeCompaction=:由於併發收集器不對記憶體空間進行壓縮、整理,所以執行一段時間以後會產生“碎片”,使得執行效率降低。此引數設定執行次FullGC以後對記憶體空間進行壓縮、整理。 

-XX:+UseCMSCompactAtFullCollection:開啟對年老代的壓縮。可能會影響效能,但是可以消除記憶體碎片。 

-XX:+CMSIncrementalMode:設定為增量收集模式。一般適用於單CPU情況。 

-XX:CMSInitiatingOccupancyFraction=70:表示年老代空間到70%時就開始執行CMS,確保年老代有足夠的空間接納來自年輕代的物件。 

注:如果使用 throughput collector 和 concurrent low pause collector 這兩種垃圾收集器,需要適當的挺高記憶體大小,為多執行緒做準備。 

其它

-XX:+ScavengeBeforeFullGC:新生代GC優先於Full GC執行。 

-XX:-DisableExplicitGC:禁止呼叫System.gc(),但JVM的gc仍然有效。 

-XX:+MaxFDLimit:最大化檔案描述符的數量限制。 

-XX:+UseThreadPriorities:啟用本地執行緒優先順序API,即使 java.lang.Thread.setPriority() 生效,反之無效。 

-XX:SoftRefLRUPolicyMSPerMB=0:“軟引用”的物件在最後一次被訪問後能存活0毫秒(預設為1秒)。 

-XX:TargetSurvivorRatio=90:允許90%的Survivor空間被佔用(預設為50%)。提高對於Survivor的使用率——超過就會嘗試垃圾回收。 

輔助資訊 

-XX:-CITime:列印消耗在JIT編譯的時間 

-XX:ErrorFile=./hs_err_pid.log:儲存錯誤日誌或者資料到指定檔案中 

-XX:-ExtendedDTraceProbes:開啟solaris特有的dtrace探針 

-XX:HeapDumpPath=./java_pid.hprof:指定匯出堆資訊時的路徑或檔名 

-XX:-HeapDumpOnOutOfMemoryError:當首次遭遇記憶體溢位時匯出此時堆中相關資訊 

-XX:OnError=";":出現致命ERROR之後執行自定義命令 

-XX:OnOutOfMemoryError=";":當首次遭遇記憶體溢位時執行自定義命令 

-XX:-PrintClassHistogram:遇到Ctrl-Break後列印類例項的柱狀資訊,與jmap -histo功能相同 

-XX:-PrintConcurrentLocks:遇到Ctrl-Break後列印併發鎖的相關資訊,與jstack -l功能相同 

-XX:-PrintCommandLineFlags:列印在命令列中出現過的標記 

-XX:-PrintCompilation:當一個方法被編譯時列印相關資訊 

-XX:-PrintGC:每次GC時列印相關資訊 

-XX:-PrintGC Details:每次GC時列印詳細資訊 

-XX:-PrintGCTimeStamps:列印每次GC的時間戳 

-XX:-TraceClassLoading:跟蹤類的載入資訊 

-XX:-TraceClassLoadingPreorder:跟蹤被引用到的所有類的載入資訊 

-XX:-TraceClassResolution:跟蹤常量池 

-XX:-TraceClassUnloading:跟蹤類的解除安裝資訊 

-XX:-TraceLoaderConstraints:跟蹤類載入器約束的相關資訊 

以下是找的jvm 伺服器優化及說明(比較簡潔,值得一看)

JVM服務調優

伺服器:8 cup, 8G mem

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

調優方案:

-Xmx5g:設定JVM最大可用記憶體為5G。 

-Xms5g:設定JVM初始記憶體為5G。此值可以設定與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配記憶體。 

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

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

-XX:ParallelGCThreads=8:配置並行收集器的執行緒數,即:同時多少個執行緒一起進行垃圾回收。此值最好配置與處理器數目相等。 

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

-XX:MaxTenuringThreshold=30:設定垃圾最大年齡(次數)。如果設定為0的話,則年輕代物件不經過Survivor區直接進入年老代。對於年老代比較多的應用,可以提高效率。如果將此值設定為一個較大值,則年輕代物件會在Survivor區進行多次複製,這樣可以增加物件再年輕代的存活時間,增加在年輕代即被回收的概率。設定為30表示一個物件如果在Survivor空間移動30次還沒有被回收就放入年老代。 

-XX:+UseConcMarkSweepGC:設定年老代為併發收集。測試配置這個引數以後,引數-XX:NewRatio=4就失效了,所以,此時年輕代大小最好用-Xmn設定,因此這個引數不建議使用。 

參考資料 - JVM堆記憶體的分代 

虛擬機器的堆記憶體共劃分為三個代:年輕代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java類的類資訊,與垃圾收集器要收集的Java物件關係不大。所以,年輕代和年老代的劃分才是對垃圾收集影響比較大的。