Java內(nèi)存優(yōu)化之POI Excel(一)

結(jié)算系統(tǒng)上線后盛末,每到月初月末弹惦,都有點(diǎn)膽戰(zhàn)心驚,最怕聽到“某某某悄但,我這個(gè)下載又不行”棠隐、“我這個(gè)都下載了20分鐘了,怎么還不行八隳宵荒!”...... 我能怎么辦哇,停下來把鍋撿起來唄净嘀。

撿鍋記之檢鍋

撿鍋了报咳,然后呢?當(dāng)然是查一查問題出在哪了挖藏。ssh上服務(wù)器暑刃,先說說服務(wù)器配置吧。這臺(tái)服務(wù)器是在某離職大神的建議下購(gòu)買的膜眠,配置還不錯(cuò)岩臣。單核SSD硬盤,其他配置如下:

[web@monitor ~]$ free -m
              total        used        free      shared  buff/cache   available
Mem:           3790        2467         173           0        1150        1021
Swap:             0           0           0

[web@monitor ~]$ cat /etc/redhat-release 
CentOS Linux release 7.3.1611 (Core) 

查看服務(wù)狀態(tài)

記得當(dāng)初車險(xiǎn)系統(tǒng)剛升級(jí)SpringBoot的時(shí)候宵膨,經(jīng)常發(fā)現(xiàn)系統(tǒng)掛掉架谎,Java進(jìn)程也被kill掉,還糾結(jié)了好久辟躏,排查了很久發(fā)現(xiàn)是被由于Linux的OOM Killer機(jī)制殺掉的谷扣。

Linux OOM_killer是Linux自我保護(hù)的方式,當(dāng)內(nèi)存不足時(shí)不至于出現(xiàn)太嚴(yán)重問題捎琐,有點(diǎn)壯士斷腕的意味会涎。在kernel 2.6,內(nèi)存不足將喚醒oom_killer瑞凑,挑出/proc/<pid>/oom_score最大者并將之kill掉末秃,可以把/proc/<pid>/oom_score_adj值改小(最小-17)來臨時(shí)避免此種情況籽御。

所以出現(xiàn)服務(wù)停止后练慕,我第一反應(yīng)就是查看Java進(jìn)程還在不在惰匙。

[web@monitor ~]$ jcmd
19335 settlement.jar --spring.profiles.active=prod
30142 sun.tools.jcmd.JCmd

居然還在,唯一的借口都不給我贺待!

檢查Java進(jìn)程棧

我第一個(gè)想到的是死鎖徽曲,導(dǎo)致進(jìn)程假死◆锶看看Java棧:

[web@monitor ~]$ jstack -l 19335
2017-11-03 21:44:07
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):

"http-nio-8087-exec-10" #1017 daemon prio=5 os_prio=0 tid=0x00007f2cd0016000 nid=0x5c31 waiting on condition [0x00007f2c9d21a000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000920abcf8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)
    at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
    - None
..... 

太長(zhǎng)了,此處截取部分涧衙。我記得jstack 有時(shí)候可以幫我們檢測(cè)出死鎖的哪工,可以直接

jstack -l <pid> | grep deadlock

如果有結(jié)果,則表示有死鎖弧哎,當(dāng)然也不排除其他情況(ps: 也遇到過數(shù)據(jù)庫(kù)鏈接失效雁比,導(dǎo)致某任務(wù)執(zhí)行了3個(gè)多小時(shí)后失敗的。)jstack的其他情況可以參考下:http://blog.csdn.net/wanglha/article/details/51133819

檢查Java堆

Java對(duì)象大部分情況實(shí)在堆上創(chuàng)建的(有時(shí)候會(huì)在棧上或者有塊叫做TLAB的空間)撤嫩,那么來檢查下堆的情況吧偎捎。

[web@monitor ~]$ jmap -heap 19335
Attaching to process ID 19335, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.144-b01

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 1879048192 (1792.0MB)
   NewSize                  = 625999872 (597.0MB)
   MaxNewSize               = 625999872 (597.0MB)
   OldSize                  = 1253048320 (1195.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 5
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 569901056 (543.5MB)
   used     = 105560864 (543.5MB)
   free     = 464340192 (0MB)
   100% used
From Space:
   capacity = 27262976 (26.0MB)
   used     = 6621032 (6.314308166503906MB)
   free     = 20641944 (19.685691833496094MB)
   24.28580064039964% used
To Space:
   capacity = 26214400 (25.0MB)
   used     = 0 (0.0MB)
   free     = 26214400 (25.0MB)
   0.0% used
PS Old Generation
   capacity = 1253048320 (1195.0MB)
   used     = 55013328 (1195.0MB)
   free     = 1198034992 (0MB)
  100% used

納尼!P蛉痢茴她!Eden區(qū)和老年代都耗盡了(這些數(shù)據(jù)是后來假造的,當(dāng)時(shí)的確看到的是兩個(gè)100%)程奠。

看看gc情況

[web@monitor ~]$ jstat -gcutil 19335
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00  99.79  99.89   98.99  97.96  96.07    901    3.654     287    1.160    4.814

介紹下這幾個(gè)參數(shù)丈牢,S0、S1表示兩個(gè)Survivor區(qū)(復(fù)制GC算法使用)瞄沙,E表示Eden區(qū)己沛,大部分對(duì)象在此創(chuàng)建,O老年代距境,多次GC后對(duì)象存活區(qū)域申尼,M表示元數(shù)據(jù)區(qū)(JAVA8后取代永久代,存放一些class元數(shù)據(jù)垫桂,我們不用太關(guān)心它师幕,基本上有JVM幫我們管理),YGC和FGC分別表示Eden區(qū)GC次數(shù)和Full GC的次數(shù)伪货,正常情況下Full GC次數(shù)比較少们衙,跑個(gè)幾天Full GC幾次差不多了,如果很多次就不正常了(當(dāng)然排除哪個(gè)二貨手動(dòng)調(diào)用System.gc())碱呼,F(xiàn)ull GC比較多蒙挑,你可能會(huì)看到如下錯(cuò)誤:java.lang.OutOfMemoryError: GC overhead limit exceeded,這個(gè)表示JVM試圖通過GC回收內(nèi)存愚臀,但是什么也沒有回收到忆蚀。默認(rèn)情況下,JVM花費(fèi)了98%的時(shí)間在GC上,但是GC過之后只有不到2%的堆內(nèi)存被回收馋袜。

甩鍋記之內(nèi)存的鍋

我去看下這群貨到底要下載什么數(shù)據(jù)男旗,一看嚇一跳,兩個(gè)月全國(guó)所有的保單數(shù)據(jù)欣鳖。這是什么概念呢察皇??jī)蓚€(gè)月差不多20多萬數(shù)據(jù)吧,如果不做關(guān)聯(lián)單泽台,那差不多是30多萬到40萬左右什荣,30萬行的Excel什么概念?自己想想吧怀酷。

我當(dāng)然不能說你們別下這么多數(shù)據(jù)啊稻爬,反正我一個(gè)打工小弟說了也沒人聽啊蜕依!好吧桅锄,升級(jí)內(nèi)存總可以吧,這鍋你背样眠。

java -Xms1536m -Xmx1536m -jar service.jar

初始化內(nèi)存和最大內(nèi)存設(shè)置一樣是為了減少GC次數(shù)友瘤,為啥要減少GC呢?簡(jiǎn)單說就是GC比較霸道吹缔,它工作的時(shí)候STOP THE WORLD商佑,所有的工作都得停下,等待GC完成厢塘。話說什么時(shí)候開始GC呢茶没?你可以使用如下命令觀察下:

jstat -gcutil <pid> [intervalTime] [invokeCount]
其中intervalTime表示多久執(zhí)行一次(單位毫秒),invokeCount表示總共會(huì)執(zhí)行多少次晚碾。如:jstat -gcutil 19335 1000

大概當(dāng)Eden區(qū)打到100%時(shí)會(huì)發(fā)生一次Young GC抓半,F(xiàn)ull GC類似。

堆內(nèi)存的各個(gè)區(qū)

改完內(nèi)存繼續(xù)觀察(后面還會(huì)升內(nèi)存格嘁,畢竟這鍋給內(nèi)存背比較簡(jiǎn)單??)笛求,發(fā)現(xiàn)其實(shí)Eden、Survivor糕簿、Old區(qū)使用情況有很大差異探入,有的區(qū)很快就滿了,有的還不到才20幾懂诗。不行啊蜂嗽,得改,平均點(diǎn)Q旰恪(這里說的是使用情況植旧,不是都一樣大)辱揭。

各個(gè)區(qū)大小比例

可以通過jmap命令查看

[jarvan4dev@Macbook] ~ $ jmap -heap 34890
Attaching to process ID 34890, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.74-b02

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 44564480 (42.5MB)
   MaxNewSize               = 715653120 (682.5MB)
   OldSize                  = 89653248 (85.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

默認(rèn)情況下(JDK8,不同版本可能不一樣)病附,NewRatio=2问窃,表示新生代和老年代比例為1:2,SurvivorRatio=8完沪,表示一個(gè)Survivor區(qū)和新生代比例為1:8域庇,兩個(gè)Survivor區(qū)(S0和S1),即每個(gè)Survivor區(qū)占堆內(nèi)存的1/10丽焊,調(diào)節(jié)這兩個(gè)參數(shù)可以調(diào)節(jié)各區(qū)大小比例较剃,來達(dá)到最優(yōu)的使用情況。

java -Xms1792m -XmX1792m -XX:NewRatio=2 -XX:SurvivorRatio=5 -jar service.jar
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末技健,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惰拱,更是在濱河造成了極大的恐慌雌贱,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偿短,死亡現(xiàn)場(chǎng)離奇詭異欣孤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昔逗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門降传,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勾怒,你說我怎么就攤上這事婆排。” “怎么了笔链?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵段只,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我鉴扫,道長(zhǎng)赞枕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任坪创,我火速辦了婚禮炕婶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘莱预。我一直安慰自己柠掂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布锁施。 她就那樣靜靜地躺著陪踩,像睡著了一般杖们。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肩狂,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天摘完,我揣著相機(jī)與錄音,去河邊找鬼傻谁。 笑死孝治,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的审磁。 我是一名探鬼主播谈飒,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼态蒂!你這毒婦竟也來了杭措?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤钾恢,失蹤者是張志新(化名)和其女友劉穎手素,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘩蚪,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泉懦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疹瘦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崩哩。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖言沐,靈堂內(nèi)的尸體忽然破棺而出邓嘹,到底是詐尸還是另有隱情,我是刑警寧澤呢灶,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布吴超,位于F島的核電站,受9級(jí)特大地震影響鸯乃,放射性物質(zhì)發(fā)生泄漏鲸阻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一缨睡、第九天 我趴在偏房一處隱蔽的房頂上張望鸟悴。 院中可真熱鬧,春花似錦奖年、人聲如沸细诸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽震贵。三九已至利赋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猩系,已是汗流浹背媚送。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寇甸,地道東北人塘偎。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拿霉,于是被迫代替她去往敵國(guó)和親吟秩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容