日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Java 分析并解決內存泄漏的實例

瀏覽:35日期:2022-08-26 08:01:09

這幾天,一直在為Java的“內存泄露”問題糾結。Java應用程序占用的內存在不斷的、有規律的上漲,最終超過了監控閾值。福爾摩 斯不得不出手了!

分析內存泄露的一般步驟

如果發現Java應用程序占用的內存出現了泄露的跡象,那么我們一般采用下面的步驟分析:

把Java應用程序使用的heap dump下來 使用Java heap分析工具,找出內存占用超出預期(一般是因為數量太多)的嫌疑對象 必要時,需要分析嫌疑對象和其他對象的引用關系。 查看程序的源代碼,找出嫌疑對象數量過多的原因。

dump heap

如果Java應用程序出現了內存泄露,千萬別著急著把應用殺掉,而是要保存現場。如果是互聯網應用,可以把流量切到其他服務器。保存現場的目的就是為了把 運行中JVM的heap dump下來。

JDK自帶的jmap工具,可以做這件事情。它的執行方法是:

jmap -dump:format=b,file=heap.bin <pid>

format=b的含義是,dump出來的文件時二進制格式。file-heap.bin的含義是,dump出來的文件名是heap.bin。<pid>就是JVM的進程號。(在linux下)先執行ps aux | grep java,找到JVM的pid;然后再執行jmap -dump:format=b,file=heap.bin <pid>,得到heap dump文件。

analyze heap

將二進制的heap dump文件解析成human-readable的信息,自然是需要專業工具的幫助,這里推薦Memory Analyzer 。

Memory Analyzer,簡稱MAT,是Eclipse基金會的開源項目,由SAP和IBM捐助。巨頭公司出品的軟件還是很中用的,MAT可以分析包含數億級對 象的heap、快速計算每個對象占用的內存大小、對象之間的引用關系、自動檢測內存泄露的嫌疑對象,功能強大,而且界面友好易用。

MAT的界面基于Eclipse開發,以兩種形式發布:Eclipse插件和Eclipe RCP。MAT的分析結果以圖片和報表的形式提供,一目了然。總之個人還是非常喜歡這個工具的。下面先貼兩張官方的screenshots:

Java 分析并解決內存泄漏的實例

Java 分析并解決內存泄漏的實例

言歸正傳,我用MAT打開了heap.bin,很容易看出,char[]的數量出其意料的多,占用90%以上的內存 。一般來說,char[]在JVM確實會占用很多內存,數量也非常多,因為String對象以char[]作為內部存儲。但是這次的char[]太貪婪 了,仔細一觀察,發現有數萬計的char[],每個都占用數百K的內存 。這個現象說明,Java程序保存了數以萬計的大String對象 。結合程序的邏輯,這個是不應該的,肯定在某個地方出了問題。

順藤摸瓜

在可疑的char[]中,任意挑了一個,使用Path To GC Root功能,找到該char[]的引用路徑,發現String對象是被一個HashMap中引用的 。這個也是意料中的事情,Java的內存泄露多半是因為對象被遺留在全局的HashMap中得不到釋放。不過,該HashMap被用作一個緩存,設置了緩 存條目的閾值,導達到閾值后會自動淘汰。從這個邏輯分析,應該不會出現內存泄露的。雖然緩存中的String對象已經達到數萬計,但仍然沒有達到預先設置 的閾值(閾值設置地比較大,因為當時預估String對象都比較小)。

但是,另一個問題引起了我的注意:為什么緩存的String對象如此巨大?內部char[]的長度達數百K。雖然緩存中的 String對象數量還沒有達到閾值,但是String對象大小遠遠超出了我們的預期,最終導致內存被大量消耗,形成內存泄露的跡象(準確說應該是內存消 耗過多) 。

就這個問題進一步順藤摸瓜,看看String大對象是如何被放到HashMap中的。通過查看程序的源代碼,我發現,確實有String大對象,不 過并沒有把String大對象放到HashMap中,而是把String大對象進行split(調用String.split方法),然后將split出 來的String小對象放到HashMap中 了。

這就奇怪了,放到HashMap中明明是split之后的String小對象,怎么會占用那么大空間呢?難道是String類的split方法有問題?

查看代碼

帶著上述疑問,我查閱了Sun JDK6中String類的代碼,主要是是split方法的實現:

public String[] split(String regex, int limit) { return Pattern.compile(regex).split(this, limit); }

可以看出,Stirng.split方法調用了Pattern.split方法。繼續看Pattern.split方法的代碼:

public String[] split(CharSequence input, int limit) { int index = 0; boolean matchLimited = limit > 0; ArrayList<String> matchList = new ArrayList<String>(); Matcher m = matcher(input); // Add segments before each match found while(m.find()) { if (!matchLimited || matchList.size() < limit - 1) { String match = input.subSequence(index, m.start()).toString(); matchList.add(match); index = m.end(); } else if (matchList.size() == limit - 1) { // last one String match = input.subSequence(index, input.length()).toString(); matchList.add(match); index = m.end(); } } // If no match was found, return this if (index == 0) return new String[] {input.toString()}; // Add remaining segment if (!matchLimited || matchList.size() < limit) matchList.add(input.subSequence(index, input.length()).toString()); // Construct result int resultSize = matchList.size(); if (limit == 0) while (resultSize > 0 && matchList.get(resultSize-1).equals('')) resultSize--; String[] result = new String[resultSize]; return matchList.subList(0, resultSize).toArray(result); } 注意看第9行:Stirng match = input.subSequence(intdex, m.start()).toString();

這里的match就是split出來的String小對象,它其實是String大對象subSequence的結果。繼續看 String.subSequence的代碼:

public CharSequence subSequence(int beginIndex, int endIndex) { return this.substring(beginIndex, endIndex); } String.subSequence有調用了String.subString,繼續看:

public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > count) { throw new StringIndexOutOfBoundsException(endIndex); } if (beginIndex > endIndex) { throw new StringIndexOutOfBoundsException(endIndex - beginIndex); } return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); }

看第11、12行,我們終于看出眉目,如果subString的內容就是完整的原字符串,那么返回原String對象;否則,就會創建一個新的 String對象,但是這個String對象貌似使用了原String對象的char[]。我們通過String的構造函數確認這一點:

// Package private constructor which shares value array for speed. String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }

為了避免內存拷貝、加快速度,Sun JDK直接復用了原String對象的char[],偏移量和長度來標識不同的字符串內容。也就是說,subString出的來String小對象 仍然會指向原String大對象的char[],split也是同樣的情況 。這就解釋了,為什么HashMap中String對象的char[]都那么大。

原因解釋

其實上一節已經分析出了原因,這一節再整理一下:

程序從每個請求中得到一個String大對象,該對象內部char[]的長度達數百K。程序對String大對象做split,將split得到的String小對象放到HashMap中,用作緩存。Sun JDK6對String.split方法做了優化,split出來的Stirng對象直接使用原String對象的char[]HashMap中的每個String對象其實都指向了一個巨大的char[]HashMap的上限是萬級的,因此被緩存的Sting對象的總大小=萬*百K=G級。G級的內存被緩存占用了,大量的內存被浪費,造成內存泄露的跡象。

解決方案

原因找到了,解決方案也就有了。split是要用的,但是我們不要把split出來的String對象直接放到HashMap中,而是調用一下 String的拷貝構造函數String(String original),這個構造函數是安全的,具體可以看代碼:

/** * Initializes a newly created {@code String} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@code original} is needed, use of this constructor is * unnecessary since Strings are immutable. * * @param original * A {@code String} */ public String(String original) { int size = original.count; char[] originalValue = original.value; char[] v; if (originalValue.length > size) { // The array representing the String is bigger than the new // String itself. Perhaps this constructor is being called // in order to trim the baggage, so make a copy of the array. int off = original.offset; v = Arrays.copyOfRange(originalValue, off, off+size); } else { // The array representing the String is the same // size as the String, so no point in making a copy. v = originalValue; } this.offset = 0; this.count = size; this.value = v; }

只是,new String(string)的代碼很怪異,?濉;蛐恚?ubString和split應該提供一個選項,讓程序員控制是否復用String對象的 char[]。

是否Bug

雖然,subString和split的實現造成了現在的問題,但是這能否算String類的bug呢?個人覺得不好說。因為這樣的優化是比較合理 的,subString和spit的結果肯定是原字符串的連續子序列。只能說,String不僅僅是一個核心類,它對于JVM來說是與原始類型同等重要的 類型。

JDK實現對String做各種可能的優化都是可以理解的。但是優化帶來了憂患,我們程序員足夠了解他們,才能用好他們。

一些補充

有個地方我沒有說清楚。

我的程序是一個Web程序,每次接受請求,就會創建一個大的String對象,然后對該String對象進行split,最后split之后的String對象放到全局緩存中。如果接收了5W個請求,那么就會有5W個大String對象。這5W個大String對象都被存儲在全局緩存中,因此會造成內存泄漏。我原以為緩存的是5W個小String,結果都是大String。

有同學后續建議用'java.io.StreamTokenizer'來解決本文的問題。確實是終極解決方案,比我上面提到的“new String()”,要好很多很多。

以上就是Java 分析并解決內存泄漏的實例的詳細內容,更多關于Java 內存泄漏的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
欧美日韩在线网站| 欧美a级一区二区| 日韩理论片av| 日韩大片在线| 亚洲国产专区校园欧美| 偷拍欧美精品| 亚洲欧美视频一区二区三区| 伊人久久大香伊蕉在人线观看热v| 综合激情网...| 日韩av中文字幕一区二区| 亚州国产精品| 国产亚洲欧美日韩精品一区二区三区 | 亚洲精品成人图区| av中文字幕在线观看第一页| 婷婷激情一区| 亚洲综合日韩| 亚洲精品人人| 日本免费新一区视频| 欧美日韩1区| 国产成人77亚洲精品www| 五月激情久久| 午夜久久福利| 日韩在线黄色| 成人午夜网址| 99国产精品99久久久久久粉嫩| 亚洲精品大全| 精品一区二区三区的国产在线观看 | 亚洲一区网站| 亚洲毛片视频| 国产精品尤物| 青青久久av| 亚洲开心激情| 国产在视频一区二区三区吞精| 九九色在线视频| 丝瓜av网站精品一区二区| 欧美日韩一区二区三区四区在线观看| 国产一区二区色噜噜| 国产一区日韩一区| 青草久久视频| 人人香蕉久久| 日韩亚洲精品在线观看| 国产精品成人a在线观看| 亚洲精品123区| 国产精品中文| 精品一区在线| 国产精品成人自拍| 好看的亚洲午夜视频在线| 青青国产精品| 欧美日韩在线二区| 国产图片一区| 欧美.日韩.国产.一区.二区 | 日韩和欧美一区二区三区| 久久a爱视频| 在线亚洲成人| 精品一区二区三区在线观看视频| 婷婷综合激情| 国产精品免费不| 亚洲精品1区| 成人在线视频区| 日韩在线成人| 国产综合婷婷| 精品视频一区二区三区在线观看| 亚洲主播在线| 中文字幕在线视频网站| 日韩一区二区三免费高清在线观看 | 日韩精品一级二级| 精品视频久久| 日本欧美在线看| 日韩久久精品网| 欧美片第1页综合| 欧美日韩国产传媒| 成人一区而且| 日韩av电影一区| 亚洲欧洲一区| 天堂√中文最新版在线| 日本成人精品| 亚洲尤物在线| 色88888久久久久久影院| 91欧美极品| 亚洲欧美成人综合| 国产亚洲一区二区手机在线观看| 国产日韩精品视频一区二区三区| 香蕉精品999视频一区二区| 亚洲精品国产嫩草在线观看 | 国产精品视区| 日韩一区三区| 久久99久久久精品欧美| 亚洲精品伦理| 亚洲第一精品影视| 日本在线啊啊| 国产一区二区三区不卡视频网站 | 一区二区三区国产在线| 91精品一区二区三区综合在线爱| 蜜桃精品视频| 国产日韩欧美一区二区三区| 亚洲免费福利一区| 日韩影院免费视频| 在线成人直播| 久久精品国产大片免费观看| 欧美韩日一区| 精品国产乱码久久久| 国产精品啊v在线| 国产亚洲精品精品国产亚洲综合 | 色婷婷亚洲mv天堂mv在影片| 国产精品亚洲片在线播放| 亚洲精品少妇| 亚洲免费福利一区| 亚洲我射av| 蜜臀精品一区二区三区在线观看 | 天堂资源在线亚洲| 日韩欧美精品一区| 国产精品麻豆久久| 国产成人精品一区二区三区在线| 欧美极品中文字幕| 麻豆一区二区99久久久久| 国产乱人伦丫前精品视频| 日本亚洲视频在线| 亚洲精选久久| 亚洲精品影视| 一区二区三区四区在线观看国产日韩| 亚洲免费高清| 日本欧美在线看| 亚洲精选久久| 91麻豆精品激情在线观看最新| 日本精品另类| 国产精品毛片久久久| 美女视频网站久久| 日韩av在线播放网址| 日韩伦理一区| 国产高清一区| 日韩在线观看一区二区| 亚洲精品少妇| 久久精品99久久久| 久久99国产精品视频| 欧美激情aⅴ一区二区三区| 久久伊人亚洲| 日韩视频网站在线观看| 免费精品国产的网站免费观看| 在线成人直播| 少妇精品久久久一区二区| 欧美日韩亚洲一区| 国产一区二区三区网| 日韩精品影视| 爽好多水快深点欧美视频| 日韩精品亚洲专区在线观看| 国产精品极品国产中出| 成年男女免费视频网站不卡| 亚洲二区精品| 亚洲精品成人一区| 国产欧美在线观看免费| 国产一区一一区高清不卡| 欧洲亚洲一区二区三区| 99热精品在线观看| 日韩不卡一二三区| 成午夜精品一区二区三区软件| 激情丁香综合| 日韩黄色在线观看| 国产一区二区三区天码| 欧美+亚洲+精品+三区| 综合欧美亚洲| 精品精品久久| 国产毛片一区| 国产精品探花在线观看| 韩国精品主播一区二区在线观看| 欧美亚洲国产精品久久| 中文字幕av亚洲精品一部二部| 美腿丝袜亚洲三区| 欧美精品黄色| 国产福利一区二区精品秒拍 | 免费观看亚洲| 中文字幕av一区二区三区四区| 精品视频自拍| 免费日韩视频| 九九九精品视频| 日韩亚洲国产欧美| 国产精品久久久久久久久免费高清 | 亚洲一级高清| 日韩欧美2区| 日韩另类视频| 日韩av中文字幕一区二区| 在线中文字幕播放| 亚洲精品中文字幕99999| 精品国产不卡一区二区| 香蕉久久久久久久av网站| 精品黄色一级片| 亚洲一区二区日韩| 综合日韩av| 日韩av中文字幕一区二区三区| 成人在线超碰| 中文字幕av一区二区三区人| 国产资源在线观看入口av| 亚洲精品一级二级三级| 久久久亚洲一区| 国产乱子精品一区二区在线观看 | 视频一区二区三区入口| 激情不卡一区二区三区视频在线| 亚洲一区二区三区四区五区午夜| 精品三级国产| 涩涩涩久久久成人精品| 国产在线不卡|