引言:java
今天,事情終於發生了。Java6(Mustang),是2006年早些時候出來的,至今仍然應用在衆多生產環境中,如今終於走到了盡頭。已經沒有什麼理由阻止遷移到Java7(Dolphin)上了。算法
這也促使我想寫一篇關於在ElasticSearch上配置Java6和7的細微差別的博文。編程
Elasticsearch對Java虛擬機進行了預先的配置。一般狀況下,由於這些配置的選擇仍是很謹慎的,因此你不須要太關心,而且你能馬上使用ElasticSearch。數組
可是,當你監視ElasticSearch節點內存時,你可能嘗試修改一些配置。這些修改是否會改善你的處境?緩存
這篇博文嘗試揭開Elasticsearch配置的神祕面紗,而且討論最多見的調整。最終,會給出一些推薦的配置調整。性能優化
Elasticsearch JVM 配置概覽:多線程
這些是Elasticsearch 0.19.11版本的默認配置。架構
JVM參數 | Elasticsearch默認值 | Environment變量 |
-Xms | 256m | ES_MIN_MEM |
-Xmx | 1g | ES_MAX_MEM |
-Xms and -Xmx | ES_HEAP_SIZE | |
-Xmn | ES_HEAP_NEWSIZE | |
-XX:MaxDirectMemorySize | ES_DIRECT_SIZE | |
-Xss | 256k | |
-XX:UseParNewGC | + | |
-XX:UseConcMarkSweepGC | + | |
-XX:CMSInitiatingOccupancyFraction | 75 | |
-XX:UseCMSInitiatingOccupancyOnly | + | |
-XX:UseCondCardMark | (commented out) |
首先你注意到的是,Elasticsearch預留了256M到1GB的堆內存。併發
這個設置適用於開發和演示環境。開發人員只須要簡單的解壓發行包,再執行./bin/elasticsearch -f就完成了Elasticsearch的安裝。固然這點對於開發來講很是棒,而且在不少場景下都能工做,可是當你須要更多內存來下降Elasticsearch負載的時候就不行了,你須要比2GB RAM更多的可用內存。框架
ES_MIN_MEM/ES_MAX_MEM是控制堆大小的配置。新的ES_HEAP_SIZE變量是一個更爲便利的選擇,由於將堆的初始大小和最大值設爲相同。也推薦在分配堆內存時儘量不要用內存的碎片。內存碎片對於性能優化來講很是不利。
ES_HEAP_NEWSIZE是可選參數,它控制堆的子集大小,也就是新生代的大小。
ES_DIRECT_SIZE控制本機直接內存大小,即JVM管理NIO框架中使用的數據區域大小。本機直接內存能夠被映射到虛擬地址空間上,這樣在64位的機器上更高效,由於能夠規避文件系統緩衝。Elasticsearch對本機直接內存沒有限制(可能致使OOM)。
因爲歷史緣由Java虛擬機有多個垃圾收集器。能夠經過如下的JVM參數組合啓用:
JVM parameter | Garbage collector |
-XX:+UseSerialGC | serial collector |
-XX:+UseParallelGC | parallel collector |
-XX:+UseParallelOldGC | Parallel compacting collector |
-XX:+UseConcMarkSweepGC | Concurrent-Mark-Sweep (CMS) collector |
-XX:+UseG1GC | Garbage-First collector (G1) |
UseParNewGC和UseConcMarkSweepGC組合啓用垃圾收集器的併發多線程模式。UseConcMarkSweepGC自動選擇UseParNewGC模式並禁用串行收集器(Serial collector)。在Java6中這是默認行爲。
CMSInitiatingOccupancyFraction提煉了一種CMS(Concurrent-Mark-Sweep)垃圾收集設置;它將舊生代觸發垃圾收集的閥值設爲75.舊生代的大小是堆大小減去新生代大小。這告訴JVM當堆內容達到75%時啓用垃圾收集。這是個估計的值,由於越小的堆可能須要越早啓動GC。
UseCondCardMark將在垃圾收集器的card table使用時,在marking以前進行額外的判斷,避免冗餘的store操做。UseCondCardMark不影響Garbage-First收集器。強烈推薦在高併發場景下配置這個參數(規避card table marking技術在高併發場景下的下降吞吐量的負面做用)。在ElasticSearch中,這個參數是被註釋掉的。
有些配置能夠參考諸如Apache Cassandra項目,他們在JVM上有相似的需求。
總而言之,ElastciSearch配置上推薦:
1. 不採用自動的堆內存配置,將堆大小默認最大值設爲1GB
2.調整觸發垃圾收集的閥值,好比將gc設爲75%堆大小的時候觸發,這樣不會影響性能。
3.禁用Java7默認的G1收集器,前提是你的ElasticSearch跑在Java7u4以上的版本上。
JVM進程的內存結果
JVM內存由幾部分組成:
Java代碼自己:包括內部代碼、數據、接口,調試和監控代理或者字節碼指令
非堆內存:用於加載類
棧內存:用於爲每一個線程存儲本地變量和操做數
堆內存:用於存放對象引用和對象自己
直接緩衝區:用於緩衝I/O數據
堆內存的大小設置很是重要,由於Java的運行依賴於合理的堆大小,而且JVM須要從操做系統那獲取有限的堆內存,用於支撐整個JVM生命週期。
若是堆過小,垃圾回收就會頻繁發生,發生OOM的概率會很大。
若是堆太大,垃圾回收會延遲,可是一旦回收,就須要處理大量的存活堆數據。而且,操做系統的壓力也會變大,由於JVM進程須要更大的堆,產生換頁的可能性就會提升。
注意,使用CMS垃圾收集器,Java不會把內存還給操做系統,所以配置合理的堆初始值和最大值就很是重要。
非堆內存由Java應用自動分配。沒有什麼參數控制這裏的大小,這是由Java應用程序代碼本身決定的。
棧內存在每一個線程中分配,在Elasticsearch中,每一個線程大小必須由128K增長到256K,由於Java7比Java6須要更大的棧內存 ,這是因爲Java7支持新的編程語言特徵來利用棧空間。好比,引入了continuations模型,編程語言的一個著名概念。Continuations模型對於
協同程序、綠色線程(green thread)、纖程(fiber)很是有用 。當實現非阻塞I/O時,一個大的優點是,代碼能夠根據線程實際使用狀況編寫,可是運行時仍然在後臺採用非阻塞I/O。Elasticsearch使用了多個線程池,由於Netty I/O框架和Guava是Elasticsearch的基礎組件,所以在用Java7時,能夠考慮進一步挖掘優化線程的特性。
發揮增長棧空間大小的優點仍是有挑戰的,由於不一樣的操做系統、不一樣的CPU架構,甚至在不一樣的JVM版本之間,棧空間的消耗不是容易比較的。取決於CPU架構和操做系統,JVM的棧空間大小是內建的。他們是否在全部場景下都適合?例如Sloaris Sparc 64位的JVM Xss默認爲512K,由於有更大地址指針,Sloaris X86爲320K。Linux降爲256K。Windows 32位Java6默認320K,Windows 64位則爲1024K。
大堆的挑戰
今天,幾GB的內存是很常見的。可是在不久之前,系統管理員還在爲多幾G的內存需求淚流滿面。
Java垃圾收集器是隨着2006年的Java6的出現而顯著改進的。從那之後,能夠併發執行多任務,而且減小了GC停頓概率: stop - the - world階段。CMS算法是革命性的,多任務,併發, 不須要移動的GC。可是不幸的是,對於堆的存活數據量來講,它是不可擴展的。Prateek Khanna 和 Aaron Morton給出了CMS垃圾收集器可以處理的堆規模的數字。
避免Stop-the-world階段
咱們已經學習了Elasticsearch如何配置CMS垃圾收集器。但這並不能組織長時間的GC停頓,它只是下降了發生的概率。CMS是一個低停頓概率的收集器,可是仍然有一些邊界狀況。當堆上有MB級別的大數組,或者其餘一些特殊的場景,CMS可能比預期要花費更多的時間。
MB級別數組的建立在Lucene segment-based索引合併時是很常見的。若是你但願下降CMS的額外負載,就須要調整Lucene合併階段的段數量,使用參數index.merge.policy.segments_per_tier
減小換頁
大堆的風險在於內存壓力上。注意,若是Java JVM在處理大堆時,這部份內存對於系統其它部分來講是不可用的。若是內存吃緊,操做系統會進行換頁,而且,在緊急狀況下,當全部其餘方式回收內存都失敗時,會強制殺掉進程。若是換頁發生,整個系統的性能會降低,天然GC的性能也跟着降低。因此,不要給堆分配太多的內存。
垃圾收集器的選擇
從Java JDK 7u4開始,Garbage-First(G1)收集器是Java7默認的垃圾收集器。它適用於多核的機器以及大內存。它一方面下降了停頓時間,另外一方面增長了停頓的次數。整個堆的操做,例如全局標記,是在應用線程中併發執行的。這會防止隨着堆或存活數據大小的變化,中斷時間也成比例的變化。
G1收集器目標是獲取更高的吞吐量,而不是速度。在如下狀況下,它能運行的很好:
1. 存活數據佔用了超過50%的Java堆
2. 對象分配比例或者promotion會有明顯的變化
3. 不但願gc或者compaction停頓時間長(超過0.5至1s)
注意,若是使用G1垃圾收集器,堆再也不使用的內存可能會被歸還給操做系統
G1垃圾收集器的不足是CPU使用率越高,應用性能越差。所以,若是在內存足夠和CPU能力通常的狀況下,CMS可能更勝一籌。
對於Elasticsearch來講,G1意味着沒有長時間的stop-the-world階段,以及更靈活的內存管理,由於buffer memory和系統I/O緩存能更充分的利用機器內存資源。代價就是小成本的最大化性能,由於G1利用了更多CPU資源。
性能調優策略
你讀這篇博文由於你但願在性能調優上獲得一些啓示:
1. 清楚瞭解你的性能目標。你但願最大化速度,仍是最大化吞吐量?
2. 記錄任何事情(log everything),收集統計數據,閱讀日誌、分析事件來診斷配置
3. 選擇你調整的目標(最大化性能仍是最大化吞吐量)
4. 計劃你的調整
5. 應用你的新配置
6. 監控新配置後的系統
7. 若是新配置沒有改善你的處境,重複上面的一系列動做,反覆嘗試
Elasticsearch垃圾收集日誌格式
Elasticsearch長時間GC下warns級別的日誌以下所示:
[2012-11-26 18:13:53,166][WARN ][monitor.jvm ] [Ectokid] [gc][ParNew][1135087][11248] duration [2.6m], collections [1]/[2.7m], total [2.6m]/[6.8m], memory [2.4gb]->[2.3gb]/[3.8gb], all_pools {[Code Cache] [13.7mb]->[13.7mb]/[48mb]}{[Par Eden Space] [109.6mb]->[15.4mb]/[1gb]}{[Par Survivor Space] [136.5mb]->[0b]/[136.5mb]}{[CMS Old Gen] [2.1gb]->[2.3gb]/[2.6gb]}{[CMS Perm Gen] [35.1mb]->[34.9mb]/[82mb]}
JvmMonitorService類中有相關的使用方式:
Logfile | Explanation |
gc | 運行中的gc |
ParNew | new parallel garbage collector |
duration 2.6m | gc時間爲2.6分鐘 |
collections [1]/[2.7m] | 在跑一個收集,共花2.7分鐘 |
memory [2.4gb]->[2.3gb]/[3.8gb] | 內存消耗, 開始是2.4gb, 如今是2.3gb, 共有3.8gb內存 |
Code Cache [13.7mb]->[13.7mb]/[48mb] | code cache佔用內存 |
Par Eden Space [109.6mb]->[15.4mb]/[1gb] | Par Eden Space佔用內存 |
Par Survivor Space [136.5mb]->[0b]/[136.5mb] | Par Survivor Space佔用內存 |
CMS Old Gen [2.1gb]->[2.3gb]/[2.6gb] | CMS Old Gen佔用內存 |
CMS Perm Gen [35.1mb]->[34.9mb]/[82mb] | CMS Perm Gen佔用內存 |
JvmMonitorSer
一些建議
1. 不要在Java 6u22以前的發佈版本中跑Elasticsearch。有內存方面的bug。那些超過兩三年的bug和缺陷會妨礙Elasticsearch的正常運行。與舊的OpenJDK 6相比,更推薦Sun/Oracle的版本,由於後者修復了不少bug。
2. 放棄Java6,轉到Java7。Oracle宣稱Java6更新到2013年2月結束。考慮到Elasticsearch仍是一個相對新的軟件,應該使用更新的技術來提高性能。儘可能從JVM中擠壓性能。檢查操做系統的版本。在最新版本的操做系統中運行,有助於你的Java運行環境達到最佳性能。
3. 按期更新Java運行環境。平均一個季度一次。告訴sa你須要及時更新Java版本,以獲取Java性能的提高。
4. 從小到大。先在Elasticsearch單節點上進行開發。可是不要忘了Elasticsearch分佈式的強大功能。單節點不能模擬生產環境的特徵,至少須要3個節點進行開發測試。
5. 在調整JVM以前先作一下性能測試。對你的系統創建性能基線。調整測試時候的節點數量。若是索引時候負載很高,你可能須要下降Elasticsearch索引時候佔用的堆大小,經過index.merge.policy.segments_per_tierparameter參數調整段的合併。
6. 調整前清楚你的性能目標,而後決定是調整速度仍是吞吐量。
7. 啓用日誌以便更好的進行診斷。在優化系統前進行當心的評估。
8. 若是使用CMS垃圾收集器,你可能須要加上合理的 -XX:CMSWaitDuration 參數。
9. 若是你的堆超過6-8GB,超過了CMS垃圾收集器設計容量,你會遇到長時間的stop-the-world階段,你有幾個方案:調整CMSInitiatingOccupancyFraction參數下降長時間GC的概率減小最大堆的大小;啓用G1垃圾收集器。
10. 學習垃圾收集調優藝術。若是你想精通的話,列出可用的JVM選項,在java命令中加入java-XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version,而後調優。