前因:
因為REST規范,定義資源獲取接口使用GET請求,參數拼接在url上。
如果按上述定義,當參數過長,超過tomcat默認配置 max-http-header-size :8kb
會報一下錯誤信息:
Request header is too large
可以修改springboot配置,調整請求頭大小
server: max-http-header-size: xxx
后果:
如果max-http-header-size設置過大,會導致接口吞吐下降,jvm oom,內存泄漏。
因為tomcat 會用HeapByteBuffer 預分配請求頭內存大小,在堆上分配。
請求和響應都是一樣的配置,每次請求處理預先分配,2倍配置值內存大小在 jvm 堆中
請求過多,導致線上內存暴漲,老年代有3GB多。使用jmap dump線上內存數據,使用 JProfiler 分析。
符合配置大小和源碼對象
數組有3GB,和老年代和eden區總和大小相近。
-XX:PretenureSizeThreshold jvm參數用來設置默認值,當數組或對象大小超過這個設定值,直接在 Old Gen 老年代分配;默認值0,當超過eden區的大小的時候,直接分配到old區。
使用 java -XX:+PrintCommandLineFlags -version
發現并沒有使用
-XX:PretenureSizeThreshold參數,所以是
max-http-header-size設置過大,eden區分配不夠,直接分配到old區,堆區內存不夠,自動擴容,導致old區數據越來越多,頻繁觸發FullGC。
JVM初始分配的內存由-Xms指定,默認是物理內存的1/64;JVM最大分配的內存由-Xmx指定,默認是物理內存的1/4。默認空余堆內存小 于40%時,JVM就會增大堆直到-Xmx的最大限制;空余堆內存大于70%時,JVM會減少堆直到-Xms的最小限制。因此服務器一般設置-Xms、 -Xmx相等以避免在每次GC后調整堆的大小。
其中http-nio-9005-exec-線程有146個,和前面配置需要分配的20M內存請求,相乘的數據與3000MB相近。
tomcat任務線程池 最大線程數200,存活時間60s
因為TaskQueue 重寫了offer方法,在線程池大小小于最大線程數時,任務不會放入任務隊列,只會交給現有線程執行;存活時間60s,只有當線程空閑60s才會被回收,也就是**60秒內請求要小于當前線程數,**才會有空閑線程。這就導致了線程不能及時被回收。請求數下降,但是內存還是居高不下。
解決方案:
max-http-header-size修改為默認值,接口請求方式修改為POST,請求參數放置于body
到此這篇關于Java詳解線上內存暴漲問題定位和解決方案的文章就介紹到這了,更多相關Java 內存暴漲內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/weixin_60707895/article/details/121013297