在生產環境中我們會遇到一些問題,此文主要記錄并復盤一下當時項目中的實際問題及解決過程。
背景簡述
最初系統上線后都比較正常風平浪靜的。在系統運行了一段時間后,業務量上升后,生產上發現java應用內存占用過高,服務器總共64G,發現每個SpringBoot占用近12G的內存,我們項目采用微服務架構,有多個springboot應用。一下子內存就不夠用了,springboot出現假死了。
由于當時生產沒有截圖,我用本機模擬類似的情況。
可以看到內存基本被使用完了,為什么Java程序會占用這么大內存呢?
解決步驟
step1:jps查看進程ID或通過top
step2:jmap -heap 進程ID
可以看到Java應用的最大堆內存是4G,當時我們生產是64G的物理內存,生產Java應用的最大堆內存是12G。
- 最大堆大小(-Xmx):通常為物理內存的1/4。
- 初始堆大小(-Xms):通常為物理內存的1/64。
以下是Oracle官方對JVM默認參數的詳細說明:
以下是對應的譯文:
默認堆大小
除非在命令行中指定了初始堆大小和最大堆大小,否則它們是根據計算機上的內存量計算的。
客戶端 JVM 默認初始和最大堆大小
默認最大堆大小是物理內存的一半(物理內存大小不超過 192 兆字節 (MB)),否則為物理內存的四分之一(物理內存大小不超過 1 千兆字節 (GB))。
例如,如果您的計算機有 128 MB 物理內存,則最大堆大小為 64 MB,大于或等于 1 GB 物理內存會導致最大堆大小為 256 MB。
JVM 實際上不會使用最大堆大小,除非您的程序創建了足夠的對象來需要它。在 JVM 初始化期間分配的量要小得多,稱為初始堆大小。此量至少為 8 MB,否則為物理內存的 1/64,最大物理內存大小為 1 GB。
分配給年輕代的最大空間量是總堆大小的三分之一。
服務器 JVM 默認初始和最大堆大小
默認初始堆大小和最大堆大小在服務器 JVM 上的工作方式與在客戶端 JVM 上的工作方式類似,只是默認值可以更高。在 32 位 JVM 上,如果有 4 GB 或更多物理內存,則默認最大堆大小可達 1 GB。在 64 位 JVM 上,如果有 128 GB 或更多物理內存,則默認最大堆大小可達 32 GB。
到這里基本上可以看出是運維人員發布Java應用時并沒有設置JVM參數,而是使用默認JVM參數。導致每個Java應用占用過高。雖然是小問題,但生產上每個Java占用12G內存還是比較嚇人的。
復盤
一般內存占用過大的排查思路:
在排查內存占用過大的問題時,一般可以采取以下思路:
- 檢查JVM參數: 如果在生產環境中啟動Spring Boot沒有設置JVM參數,使用默認的JVM配置,可能會導致性能問題和資源浪費。優化JVM參數,根據應用程序的需求和服務器配置進行調整。
- 觀察內存使用情況: 使用監控工具或者操作系統提供的工具,觀察Java應用的內存使用情況,包括堆內存、非堆內存、垃圾回收等。
- 分析GC: 如果發現內存問題,可以分析GC日志以了解垃圾回收的情況,包括頻率、時間等。
- 合理設置堆內存大小: 根據應用程序的需求和服務器的物理內存,合理設置堆內存的大小,避免過大或過小導致性能問題。
- 考慮使用內存分析工具: 使用工具如VisualVM、MAT等,對應用程序進行內存分析,找出可能存在的內存泄漏或者大對象。
如果在生產環境中啟動springboot沒有設置jvm參數,使用默認的JVM配置,可能會有以下幾個危害:
- 默認的JVM配置可能不適合你的應用程序的性能需求和資源限制,導致內存溢出、垃圾回收頻繁、性能下降等問題。
- 默認的JVM配置可能會浪費服務器的內存資源,因為JVM會根據物理內存的大小來分配堆內存的大小,而不是根據應用程序的實際需求。
因此,建議在生產環境中啟動springboot時,根據應用程序的特點和服務器的配置,合理地設置JVM參數,以提高應用程序的性能和穩定性,節省服務器的資源。