1. 程式人生 > >java堆外記憶體洩漏

java堆外記憶體洩漏

問題描述

最近有個系統在做壓力測試,

環境配置:

4核CPU 8g記憶體 jdk1.6.0_25,jvm配置-server -Xms2048m -Xmx2048m 

出現問題如下

執行併發300人,壓測持續1個小時記憶體使用率從20%上升到100%,tps從1100多降低到600多。

排查過程

top命令檢視記憶體佔用如下


然後檢視java堆記憶體分佈情況

 

檢視堆記憶體佔用正常,jvm垃圾回收也沒有異常。

然後想到了是堆外記憶體洩漏,由於系統中用的jsf介面比較多,底層都是依賴的netty。首先考慮的是java中nio包下的DirectByteBuffer,可以直接分配堆外記憶體,不過該類分配的記憶體也有大小限制的,可以直接通過-XX:MaxDirectMemorySize=1g 進行指定,並且記憶體不夠用的時候程式碼中會顯式的呼叫System.gc()方法來觸發FullGC,如果記憶體還是不夠用就會丟擲記憶體溢位的異常。為了驗證這一想法,於是在啟動引數中通過-XX:MaxDirectMemorySize=1g指定了堆外記憶體大小為1g,然後再次進行壓測,發現記憶體還是在持續增長,然後超過了堆記憶體2g和堆外記憶體1g的總和,並且也沒有發現有記憶體溢位的異常,也沒有頻繁的進行FullGC。所以可能不是nio的DirectByteBuffer佔用的堆外記憶體。

為了分析堆外記憶體到底是誰佔用了,不得不安裝google-perftools工具進行分析,安裝步驟如下:

它的原理是在java應用程式執行時,當呼叫malloc時換用它的libtcmalloc.so,這樣就能做一些統計了
下載http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-beta.tar.gz,
./configure
make
sudo make install //需要root許可權
下載http://google-perftools.googlecode.com/files/google-perftools-1.8.1.tar.gz

,
./configure --prefix=/home/admin/tools/perftools --enable-frame-pointers
make
sudo make install //需要root許可權
修改
lc_config:
sudo vi /etc/ld.so.conf.d/usr-local_lib.conf,加入/usr/local/lib(libunwind的lib所在目錄)
執行
sudo /sbin/ldconfig,使libunwind生效
在應用程式啟動前加入:
export LD_PRELOAD=/home/admin/tools/perftools/lib/libtcmalloc.so
export HEAPPROFILE=/home/admin/heap/gzip
啟動應用程式,此時會在/home/admin/heap下看到諸如gzip_pid.xxxx.heap的heap檔案,
可使用/home/admin/tools/perftools/bin/pprof --text $JAVA_HOME/bin/java test_pid.xxxx.heap來檢視
/home/admin/tools/perftools/bin/pprof --text $JAVA_HOME/bin/java gzip_22366.0005.heap > gzip-0005.txt

然後檢視分析結果如下

Total: 4504.5 MB
4413.9 98.0% 98.0% 4413.9 98.0% zcalloc
60.0 1.3% 99.3% 60.0 1.3% os::malloc
16.4 0.4% 99.7% 16.4 0.4% ObjectSynchronizer::omAlloc
8.7 0.2% 99.9% 4422.7 98.2% Java_java_util_zip_Inflater_init
4.7 0.1% 100.0% 4.7 0.1% init
0.3 0.0% 100.0% 0.3 0.0% readCEN
0.2 0.0% 100.0% 0.2 0.0% instanceKlass::add_dependent_nmethod
0.1 0.0% 100.0% 0.1 0.0% _dl_allocate_tls
0.0 0.0% 100.0% 0.0 0.0% [email protected]_2.2.5
0.0 0.0% 100.0% 1.7 0.0% Thread::Thread
0.0 0.0% 100.0% 0.0 0.0% _dl_new_object
0.0 0.0% 100.0% 0.0 0.0% [email protected]_2.2.5
0.0 0.0% 100.0% 0.0 0.0% _dlerror_run
0.0 0.0% 100.0% 0.0 0.0% allocZip
0.0 0.0% 100.0% 0.0 0.0% __strdup
0.0 0.0% 100.0% 0.0 0.0% _nl_intern_locale_data
0.0 0.0% 100.0% 0.0 0.0% addMetaName

可以看到是Java_java_util_zip_Inflater_init這個函式一直在進行記憶體分配,檢視java原始碼原來是

public GZIPInputStream(InputStream in, int size) throws IOException {
super(in, new Inflater(true), size);
 usesDefaultInflater = true;
 readHeader(in);
}


原來java中gzip解壓縮類耗盡了系統記憶體,然後跟蹤原始碼到了系統裡邊使用的SerializationUtils類,jimdb客戶端使用該工具類對儲存在快取中的key和物件進行序列化和反序列化操作,並且在對Object進行序列化和反序列化的時候用到了java的GZIPInputStream和GZIPOutputStream解壓縮功能,當大併發進行壓測的時候,就會造成記憶體洩漏,出現記憶體持續增長的問題,當壓測停止後,記憶體也不會釋放。

暫時的解決方案

1、升級jdk版本為jdk7u71 ,壓測一段時間後,發現記憶體增長有所減慢,並且會穩定在一定的範圍內,不會把伺服器的所有記憶體耗盡。猜測可能是jdk1.6版本的bug

2、儘量不要GZIPInputStream和GZIPOutputStream解壓縮功能,因為如果物件本來就不大,壓縮不了多少空間。

如真的需要解壓縮功能,最好設定解壓縮閥值,當物件大小超過閥值之後在進行解壓縮處理,不要將所有物件都進行解壓縮處理。

相關推薦

java記憶體洩漏

問題描述 最近有個系統在做壓力測試, 環境配置: 4核CPU 8g記憶體 jdk1.6.0_25,jvm配置-server -Xms2048m -Xmx2048m  出現問題如下 執行併發300人,壓測持續1個小時記憶體使用率從20%上升到100%,tps從11

Java記憶體使用

JVM內部會把所有記憶體分成Java使用的堆記憶體和Native使用的記憶體,它們之間是不能共享的,就是說當你的Native記憶體用完了時,如果Java堆又有空閒記憶體,這時Native會重新向Jvm申請,而不是直接使用Java堆記憶體。 使用堆外記憶體,就是為了能直接分配和釋放記憶體,提高

Java——記憶體詳解

記憶體是好東西,我們常聽堆記憶體,很多人卻不知道還有一個堆外記憶體。 那這兩個都是個啥玩意呢?且讓本帥博主今天給你好好說道說道。 一、堆內記憶體 那什麼東西是堆記憶體呢?我們來看看官方的說法。 “Java 虛擬機器具有一個堆(Heap),堆是執行時資料區域,所

spring boot 引起的 “記憶體洩漏

spring boot 引起的 “堆外記憶體洩漏” 背景 組內一個專案最近一直報swap區域使用過高異常,筆者被叫去幫忙檢視原因。   發現配置的4G堆內記憶體,但是實際使用的實體記憶體高達7G,確實有點不正常,JVM引數配置是“-XX:MetaspaceSize=2

Java記憶體溢位問題排查,top命令下java服務res值上升

前幾天寫了一套java服務用於對接視訊單位的sdk介面,但是專案環境測試的時候出現了問題:          在linux環境下使用top命令檢視java命令的mem比值一直在緩慢的增加,第二天出現了服務宕機的情況,生成hs_err的log                 

JNI引起的記憶體洩漏問題分析

背景 客戶現場的監控系統中有一個網路聽診器功能,其每隔1分鐘會對全網裝置進行ping操作,以此來儘可能快的發現裝置及網路是否出現異常。暫且不說通過該功能來對裝置及網路作健康檢測是否靠譜。由於JAVA對於網路層以下的協議是無能為力的,而ping操作涉及ICM

Java記憶體排查小結

簡介JVM堆外記憶體難排查但經常會出現問題,這可能是目前最全的JVM堆外記憶體排查思路。通過本文,你應該瞭解:pmap 命令gdb 命令perf 命令記憶體 RSS、VSZ的區別java NMT起因這幾天遇到一個比較奇怪的問題,覺得有必要和大家分享一下。我們的一個服務,執行在

google-perftools 分析JAVA 記憶體

原文轉自:http://koven2049.iteye.com/blog/1142768,所有權利歸原作者所有 最近線上執行的hbase發現分配了16g記憶體,但是實際使用了22g,堆外記憶體達到6g。感覺非常詭異。堆外記憶體用一般的工具很難檢視,可以通過google-pe

Netty之Java記憶體掃盲貼

Java的堆外記憶體本來是高貴而神祕的東西,只在一些快取方案的收費企業版裡出現。但自從用了Netty,就變成了天天打交道的事情,畢竟堆外記憶體能減少IO時的記憶體複製,不需要堆記憶體Buffer拷貝一份到直接記憶體中,然後才寫入Socket中;而且也沒了煩人的GC。 好

Spring Boot引起的“記憶體洩漏”排查及經驗總結

背景 為了更好地實現對專案的管理,我們將組內一個專案遷移到MDP框架(基於Spring Boot),隨後我們就發現系統會頻繁報出Swap區域使用量過高的異常。筆者被叫去幫忙檢視原因,發現配置了4G堆內記憶體,但是實際使用的實體記憶體竟然高達7G,確實不正常。JVM引數配置是“-XX:MetaspaceSiz

Netty之Java記憶體掃盲

Java的堆外記憶體本來是高貴而神祕的東西,只在一些快取方案的收費企業版裡出現。但自從用了Netty,就變成了天天打交道的事情,畢竟堆外記憶體能減少IO時的記憶體複製,不需要堆記憶體Buffer拷貝一份到直接記憶體中,然後才寫入Socket中;而且也沒了煩人的GC。 好

深入理解Java記憶體

    關於堆記憶體(Heap),相信作為 Java 開發者的你已經早有耳聞,但是,你瞭解堆外記憶體(Off Heap)嗎?  堆記憶體完全由 JVM 負責分配和釋放,如果程式存在缺陷,有可能導致記憶體洩漏而溢位,丟擲 OOM 異常: java.lang.OutOfMemor

java記憶體

簡介 誰在使用堆外記憶體:執行緒棧,應用程式程式碼,NIO快取 什麼東西需要池化,昂貴的物件:執行緒,資料庫連線池, socket。 好處: 理論上能減少GC時間, 提高效率 JDK5.0之後,程式碼中能直接操作本地記憶體的方式有2種:使用未公開的Uns

超乾貨!Cassandra Java記憶體排查經歷全記錄

背景 最近準備上線cassandra這個產品,同事在做一些小規格ECS(8G)的壓測。壓測時候比較容易觸發OOM Killer

Netty記憶體洩漏排查,這一篇全講清楚了

上篇文章介紹了Netty記憶體模型原理,由於Netty在使用不當會導致堆外記憶體洩漏,網上關於這方面的資料比較少,所以寫下這篇文章,專門介紹排查Netty堆外記憶體相關的知識點,診斷工具,以及排查思路提供參考 現象 堆外記憶體洩漏的現象主要是,程序佔用的記憶體較高(Linux下可以用top命令檢視),但J

一次完整的JVM記憶體洩漏故障排查記錄

## 前言 記錄一次線上JVM堆外記憶體洩漏問題的排查過程與思路,其中夾帶一些**JVM記憶體分配機制**以及**常用的JVM問題排查指令和工具分享**,希望對大家有所幫助。 在整個排查過程中,我也走了不少彎路,但是在文章中我仍然會把完整的思路和想法寫出來,當做一次經驗教訓,給後人參考,文章最後也總結了下

後端---java記憶體詳解

堆外記憶體和堆內記憶體    堆外記憶體又稱為直接記憶體(Direct Memory)並不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域.一直以來是Javaer們難以關注的一片領域,今天我們就一起探索一下這片區域究竟隱藏著什麼東東???? &

JAVA記憶體記憶體

定義堆記憶體完全由JVM負責分配和釋放,如果程式碼有程式缺陷,可能是觸發OOM堆外記憶體為了能直接分配和釋放記憶體,提高效率。使用方式:使用未公開的Unsafe和NIO下的ByteBuffer堆外記憶體的回收機制Direct Memory是受GC控制的,例如ByteBuffe

java中使用記憶體,關於記憶體回收需要注意的事和沒有解決的遺留問題(等大神解答)

JVM可以使用的記憶體分外2種:堆記憶體和堆外記憶體,堆記憶體完全由JVM負責分配和釋放,如果程式沒有缺陷程式碼導致記憶體洩露,那麼就不會遇到java.lang.OutOfMemoryError這個錯誤。使用堆外記憶體,就是為了能直接分配和釋放記憶體,提高效率。JDK5.

Java開發雜談(四)記憶體

 JVM可以使用的記憶體分外兩種:堆記憶體和堆外記憶體。堆記憶體完全由JVM負責分配和釋放,堆外記憶體的存在是為了能直接分配和釋放記憶體,提高效率。 堆外記憶體優點: 對外記憶體由於避免了資料從使用者態向核心態的拷貝,提升IO效率,另外還可以節約大量的堆內記憶體,減少GC次數。