一、記憶體溢位記憶體溢位的原因:程式在申請記憶體時,沒有足夠的空間。1. 棧溢位方法死迴圈遞迴呼叫(StackOverflowError)、不斷建立執行緒(OutOfMemoryError)。2. 堆溢位不斷創建
2021-06-15 14:46:33
一、記憶體溢位
記憶體溢位的原因:程式在申請記憶體時,沒有足夠的空間。
1. 棧溢位
方法死迴圈遞迴呼叫(StackOverflowError)、不斷建立執行緒(OutOfMemoryError)。
2. 堆溢位
不斷創建物件,分配物件大於最大堆的大小(OutOfMemoryError)。
3. 直接記憶體
JVM 分配的本地直接記憶體大小大於 JVM 的限制,可以通過-XX:MaxDirectMemorySize 來設定(不設定的話預設與堆記憶體最大值一樣,也會出現OOM 異常)。
4. 方法區溢位
一個類要被垃圾收集器回收掉,判定條件是比較苛刻的,在經常動態生產大量 Class 的應用中,CGLIb 位元組碼增強,動態語言,大量 JSP(JSP 第一次運行需要編譯成 Java 類),基於 OSGi 的應用(同一個類,被不同的載入器載入也會設為不同的類),都可能會導致OOM。
二、記憶體洩露
程式在申請記憶體後,無法釋放已申請的記憶體空間,導致這一部分的原因主要是程式碼寫的不合理,比如以下幾種情況。
1. 長生命週期的物件持有短生命週期物件的引用
例如將 ArrayList 設定為靜態變數,然後不斷地向ArrayList中新增物件,則 ArrayList 容器中的物件在程式結束之前將不能被釋放,從而造成記憶體洩漏。
2. 連線未關閉
如資料庫連線、網路連線和 IO 連線等,只有連線被關閉後,垃圾回收器才會回收對應的物件。
3. 變數作用域不合理
例如:
一個變數的定義的作用範圍大於其使用範圍。如果沒有及時地把物件設定為 null。4. 內部類持有外部類
Java 的 非靜態內部類的這種創建方式,會隱式地持有外部類的引用,而且預設情況下這個引用是強引用,因此,如果內部類的生命週期長於外部類的生命週期,程式很容易就產生記憶體洩露(可以理解為:垃圾回收器會回收掉外部類的例項,但由於內部類持有外部類的引用,導致垃圾回收器不能正常工作)。
解決辦法:將非靜態內部類改為 靜態內部類,即加上 static 修飾,例如:
5. Hash值改變
在集合中,如果修改了物件中的那些參與計算雜湊值的欄位,會導致無法從集合中單獨刪除當前物件,造成記憶體洩露。
使用例子來說明。
三、記憶體溢位和記憶體洩漏辨析
記憶體溢位:實實在在的記憶體空間不足導致。記憶體洩漏:該釋放的物件沒有釋放,常見於使用容器儲存元素的情況下。如何避免:
記憶體溢位:檢查程式碼以及設定足夠的空間。記憶體洩漏:一定是程式碼有問題,往往很多情況下,記憶體溢位往往是記憶體洩漏造成的。四、瞭解MAT
mat是一個記憶體洩露的分析工具。
1. 淺堆和深堆
淺堆(Shallow Heap):是指一個物件所消耗的記憶體。深堆(Retained Heap):這個物件被 GC 回收後,可以真實釋放的記憶體大小,也就是隻能通過物件被直接或間接訪問到的所有物件的集合。通俗地說,就是一個物件包含(引用)的所有物件的大小,如圖:
2. MAT的使用
1、下載MAT工具:下載地址
2、記憶體溢位例子演示
參數說明:
-Xms5m 堆初始大小5M-Xmx5m 堆最大大小5M-XX:+PrintGCDetails 列印gc日誌詳情-XX:+HeapDumpOnOutOfMemoryError 輸出記憶體溢位檔案-XX:HeapDumpPath=D:/oomDump/dump.hprof 記憶體溢位檔案儲存位置,此檔案用於MAT分析
設定參數運行後,記憶體溢位,程式結束,然後我們就可以用下載好的MAT來分析了,當然MAT也只是分析猜想,並不代表一定是這個原因導致記憶體溢位。
開啟我們儲存的檔案目錄進行分析。
分析結果。
此時可以檢視詳情檢視具體原因,當然這個原因也只是一種猜想。
五、JDK提供的一些工具
六、GC調優
1. GC調優重要參數
生產環境推薦開啟
-XX:+HeapDumpOnOutOfMemoryError輸出記憶體溢位檔案-XX:HeapDumpPath=D:/oomDump/dump.hprof記憶體溢位檔案儲存位置,此檔案用於MAT分析當然,一般Linux伺服器可以設定為 ./java_pid<pid>.hprof 預設為Java程序啟動位置調優之前開始,調優之後關閉
-XX:+PrintGC偵錯跟蹤之 列印簡單的 GC 資訊參數:-XX:+PrintGCDetails和-XX:+PrintGCTimeStamps列印詳細的 GC 資訊-Xlogger:logpath:log/gc.log設定 gc 的日誌路,將 gc.log 的路徑設定到當前目錄的 log 目錄下. 應用場景: 將 gc 的日誌獨立寫入日誌檔案,將 GC 日誌與系統業務日誌進行了分離,方便開發人員進行追蹤分析考慮使用
-XX:+PrintHeapAtGC列印推資訊,獲取 Heap 在每次垃圾回收前後的使用狀況-XX:+TraceClassLoading在系統控制檯資訊中看到 class 載入的過程和具體的 class 資訊,可用以分析類的載入順序以及是否可進行精簡操作-XX:+DisableExplicitGC禁止在運行期顯式地呼叫 System.gc()2. GC調優的原則(很重要)
大多數的 java 應用不需要 GC 調優大部分需要 GC 調優的的,不是參數問題,是程式碼問題在實際使用中,分析 GC 情況優化程式碼 比 優化 GC 參數 要多得多GC 調優是最後的手段調優的目的
GC 的時間夠小GC 的次數夠少發生Full GC 的週期足夠的長,時間合理,最好是不發生注: 如果滿足下面的指標,則一般不需要進行 GC調優
Minor GC 執行時間不到 50msMinor GC 執行不頻繁,約 10 秒一次Full GC 執行時間不到 1sFull GC 執行頻率不算頻繁,不低於 10 分鐘 1 次3. GC調優步驟
1、監控 GC 的狀態使用各種 JVM 工具,檢視當前日誌,分析當前 JVM 參數設定,並且分析當前堆記憶體快照和 gc 日誌,根據實際的各區域記憶體劃分和 GC 執行時間,覺得是否進行優化。
2、分析結果,判斷是否需要優化如果各項參數設定合理。
系統沒有超時日誌出現,GC 頻率不高,GC 耗時不高,那麼沒有必要進行 GC 優化。如果 GC 時間超過 1 秒,或者頻繁 GC,則必須優化。3、調整 GC 類型和記憶體分配如果記憶體分配過大或過小,或者採用的 GC 收集器比較慢,則應該優先調整這些參數,並且先找 1 臺或幾臺機器進行 測試,然後比較優化過的機器和沒有優化的機器的效能對比,並有針對性的做出最後選擇。
4、不斷的分析和調整通過不斷的試驗和試錯,分析並找到最合適的參數5,全面應用參數如果找到了最合適的參數,則將這些參數應用到所有伺服器,並進行後續跟蹤。
分析GC日誌
主要關注 MinorGC 和 FullGC 的回收效率(回收前大小和回收比較)、回收的時間。
1、-XX:+UseSerialGC
以參數-Xms5m -Xmx5m -XX:+PrintGCDetails -XX:+UseSerialGC 為例詳細說明。[DefNew: 1855K->1855K(1856K), 0.0000148 secs][Tenured: 2815K->4095K(4096K), 0.0134819 secs] 4671K。DefNew 指明瞭收集器類型,而且說明了收集發生在新生代。1855K->1855K(1856K)表示,回收前 新生代佔用 1855K,回收後佔用 1855K,新生代大小 1856K0.0000148 secs 表明新生代回收耗時。Tenured 表明收集發生在老年代。2815K->4095K(4096K), 0.0134819 secs:含義同新生代最後的 4671K 指明堆的大小。2、-XX:+UseParNewGC
收集器參數變為-XX:+UseParNewGC。日誌變為:[ParNew: 1856K->1856K(1856K), 0.0000107 secs][Tenured: 2890K->4095K(4096K), 0.0121148 secs]。收集器參數變為-XX:+ UseParallelGC 或 UseParallelOldGC。日誌變為:[PSYoungGen: 1024K->1022K(1536K)] [ParOldGen: 3783K->3782K(4096K)] 4807K->4804K(5632K)。3、-XX:+UseConcMarkSweepGC 和 -XX:+UseG1GC
使用這兩個收集器的日誌會和UseParNewGC一樣有明顯的相關字樣。
4. 項目啟動調優
開啟日誌分析-XX:+PrintGCDetails,啟動項目時,通過分析日誌,不斷地調整參數,減少GC次數。
例如:
1、碰到 Metadata空間 不足發生GC,那麼調整 Metadata空間 -XX:MetaspaceSize=64m 減少 FullGC 。2、碰到MinorGC,那麼調整堆空間 -Xms1000m 大小減少FullGC 。3、如果還是有MinorGC,那麼繼續增大堆空間大小,或者增大新生代比例 -Xmn900m GC,此時新生代空間為900m,老年代大小100m 。
5. 項目運行GC調優
使用 jmeter 工具 來進行壓測,然後分析原因,進行調優,當然 正式上線的項目請謹慎操作。
jmeter工具安裝使用
1、下載好對應版本的jmeter,注意jdk版本。
2、jmeter需要Java運行時環境,所以如果報錯請先檢查你的Java環境變數設定,解壓到你想要的路徑,例如我解壓在C:My Program Filesapache-jmeter-5.2.1,在bin目錄下有一個 jmeter.bat 檔案,雙擊啟動。
至於具體怎麼使用就百度吧,基本拿到軟體就知道使用了,畢竟這個說來就浪費篇幅了。
聚合報告參數
這裡放出我本地 jmeter 測試一個項目之後的 聚合報告參數解釋。
6. 推薦策略(僅作參考)
1、新生代大小選擇
儘可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇).在此種情況下,新生代收集發生的頻率也是最小的.同時,減少到達老年代的物件。避免設定過小,當新生代設定過小時會導致:MinorGC 次數更加頻繁、可能導致 MinorGC 物件直接進入老年代,如果此時老年代滿了,會觸發 FullGC。2、老年代大小選擇
一般吞吐量優先的應用都有一個很大的新生代和一個較小的老年代.原因是,這樣可以儘可能回收掉大部分短期物件,減少中期的物件,而老年代盡存放長期存活物件
七、逃逸分析
補充知識,並非所有的物件都會在堆上面分配,而沒有在堆上分配的物件是因為經過逃逸分析,分析之後發現該物件的大小可以在棧上分配,不會造成棧溢位,這時,物件就可以在棧上分配。
當然,如果經過逃逸分析,發現該物件在棧上分配會照成棧溢位,那麼該物件就會在堆空間分配。
參數jdk1.8預設開啟
-XX:+DoEscapeAnalysis 啟用逃逸分析(預設開啟)-XX:+EliminateAllocations 標量替換(預設開啟)-XX:+UseTLAB 本地執行緒分配緩衝(預設開啟)八、常用的效能評價/測試指標
一個 web 應用不是一個孤立的個體,它是一個系統的部分,系統中的每一部分都會影響整個系統的效能。
1、響應時間:提交請求和返回該請求的響應之間使用的時間,一般比較關注平均響應時間。2、併發數:同一時刻,對伺服器有實際互動的請求數,和網站線上使用者數的關聯:1000 個同時線上使用者數,可以估計併發數在 5%到 15%之間,也就是同時併發數在 50~150 之間。3、吞吐量:對單位時間內完成的工作量(請求)的量度,例如1秒處理5萬個請求。
需要Java學習資料的小夥伴,關注回覆000。
相關文章
一、記憶體溢位記憶體溢位的原因:程式在申請記憶體時,沒有足夠的空間。1. 棧溢位方法死迴圈遞迴呼叫(StackOverflowError)、不斷建立執行緒(OutOfMemoryError)。2. 堆溢位不斷創建
2021-06-15 14:46:33
Mac包管理工具Homebrew出現了一個大漏洞:在Homebrew/homebrew-cask倉庫中,通過混淆Homebrew項目中自動拉取請求審閱指令碼中使用的庫,可以合併惡意的拉取請求。如果被濫用,攻擊
2021-06-15 14:45:55
很多已經進入計算機專業的大學生會問,這麼多學科我應該學什麼?未來的發展方向是什麼?在校期間應該重點學習技術還是重點學習演算法? 基於上面這些問題苦於沒有人指導和答疑,導致
2021-06-15 14:45:41
果粉之家,專業蘋果手機技術研究十年!您身邊的蘋果專家~近日,蘋果的Apple Pay新增了對「大連明珠卡」的支援。大連明珠卡也是按照交通運輸部全國交通一卡通技術標準要求發行的全
2021-06-15 14:45:02
【TechWeb】6月15日訊息,資料連線平臺LiveRamp宣佈與家樂福擴大全球合作伙伴關係,通過 LiveRamp Safe Haven 賦能家樂福資料協作、分析和創新能力。使用 LiveRamp 的隱私保護
2021-06-15 14:44:56
生活在資訊爆炸的年代,我們身邊總是圍繞著一個電子零件——螢幕,我們常聽說,人們的日常已經離不開手機、電腦,事實上我們更離不開螢幕:從隨身手機到電腦,從路邊的廣告牌到家裡的電
2021-06-15 14:44:39