1.jvm內(nèi)存模型
問題:
“你有沒有處理過 JVM 相關(guān)的問題?”
“如何排查內(nèi)存溢出 OOM?”
“堆內(nèi)存溢出的常見原因和解決辦法?”
“JVM 內(nèi)存模型中,堆內(nèi)存的作用是什么?”
答:之前項(xiàng)目啟動(dòng)時(shí)出現(xiàn)過 OOM,我通過添加 JVM 參數(shù)讓它自動(dòng)生成內(nèi)存快照,分析后發(fā)現(xiàn)是初始化階段加載了大量醫(yī)囑靜態(tài)數(shù)據(jù),導(dǎo)致堆內(nèi)存不足。后來(lái)優(yōu)化了初始化邏輯,只加載核心數(shù)據(jù),啟動(dòng)就正常了。
細(xì)節(jié)補(bǔ)充:
堆內(nèi)存是 JVM 中存儲(chǔ)對(duì)象實(shí)例的區(qū)域
JVM 內(nèi)存結(jié)構(gòu)分線程私有和線程共享兩部分。
線程私有包括程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧,線程共享包括堆和方法區(qū)。
堆是最大的區(qū)域,存對(duì)象實(shí)例,分新生代和老年代,新生代有 Eden 區(qū)和 Survivor 區(qū),短期對(duì)象先在 Eden 區(qū),多次 GC 后存活的進(jìn)老年代。
方法區(qū)存類信息、常量等,比如護(hù)理模塊的類定義就在這里。虛擬機(jī)棧記錄方法調(diào)用,調(diào)用方法時(shí)會(huì)創(chuàng)建棧幀入棧。
當(dāng)時(shí)排查 OOM 時(shí),可能用了 jmap 命令導(dǎo)出堆內(nèi)存快照,然后用 MAT 工具分析,發(fā)現(xiàn)大量護(hù)理記錄對(duì)象堆積在老年代,追溯到查詢方法沒有分頁(yè)。也可能通過 JVM 參數(shù) - XX:+HeapDumpOnOutOfMemoryError,讓 OOM 時(shí)自動(dòng)生成快照,再定位到具體代碼行。
歷史知識(shí)回顧:
OutofMemerryError是堆內(nèi)存空間不足,但是最終拋出錯(cuò)誤的地方,還是在棧里面
經(jīng)典案例——
1、分頁(yè)查詢 沒有加分頁(yè)參數(shù) 一次性查詢1000萬(wàn)條數(shù)據(jù);(確實(shí)空間不足)
2、頻繁操作string 沒有 用 stringBuffer 或者 sringBuilder;(內(nèi)存泄漏)
3、讀寫的stream 流、連接...開辟了資源沒有釋放,只管開辟,不管回收;
4.微服務(wù)啟動(dòng),springcloud 很多組件,IOC很多bean實(shí)例;
StackOverflow是棧溢出 方法棧溢出,方法里面的引用、深度、太多了
經(jīng)典案例——
1、無(wú)限遞歸;
設(shè)置大小
-Xmx=2G(設(shè)置最大內(nèi)存) -Xmn500m(設(shè)置新生代內(nèi)存,一般為3/8),
-xms=2G(設(shè)置初始內(nèi)存,與最大內(nèi)存一樣,避免每次垃圾回收完成后重新分配JVM內(nèi)存),-xss=128k(堆棧大小,jdk1.5之后為1M,這個(gè)值越大,創(chuàng)建線程越少)