Java中由substring方法引發(fā)的內(nèi)存泄漏詳解
內(nèi)存溢出(out of memory ) :通俗的說就是內(nèi)存不夠用了,比如在一個(gè)無限循環(huán)中不斷創(chuàng)建一個(gè)大的對(duì)象,很快就會(huì)引發(fā)內(nèi)存溢出。
內(nèi)存泄漏(leak of memory) :是指為一個(gè)對(duì)象分配內(nèi)存之后,在對(duì)象已經(jīng)不在使用時(shí)未及時(shí)的釋放,導(dǎo)致一直占據(jù)內(nèi)存單元,使實(shí)際可用內(nèi)存減少,就好像內(nèi)存泄漏了一樣。
由substring方法引發(fā)的內(nèi)存泄漏
substring(int beginIndex, int endndex )是String類的一個(gè)方法,但是這個(gè)方法在JDK6和JDK7中的實(shí)現(xiàn)是完全不同的(雖然它們都達(dá)到了同樣的效果)。了解它們實(shí)現(xiàn)細(xì)節(jié)上的差異,能夠更好的幫助你使用它們,因?yàn)樵贘DK1.6中不當(dāng)使用substring會(huì)導(dǎo)致嚴(yán)重的內(nèi)存泄漏問題。
1、substring的作用
substring(int beginIndex, int endIndex)方法返回一個(gè)子字符串,從父字符串的beginIndex開始,結(jié)束于endindex-1。父字符串的下標(biāo)從0開始,子字符串包含beginIndex而不包含endIndex
String x= "abcdef"; x= str.substring(1,3); System.out.println(x);
上述程序的輸出是“bc”
2、實(shí)現(xiàn)原理
String類是不可變變,當(dāng)上述第二句中x被重新賦值的時(shí)候,它會(huì)指向一個(gè)新的字符串對(duì)象。然而,沒有準(zhǔn)確說明的或者代表堆中發(fā)生的實(shí)際情況,當(dāng)substring被調(diào)用的時(shí)候真正發(fā)生的才是這兩者的差別。
JDK6中的substring實(shí)現(xiàn)
String對(duì)象被當(dāng)作一個(gè)char數(shù)組來存儲(chǔ),在String類中有3個(gè)域:char[] value、int offset、int count,分別用來存儲(chǔ)真實(shí)的字符數(shù)組,數(shù)組的起始位置,String的字符數(shù)。由這3個(gè)變量就可以決定一個(gè)字符串。當(dāng)substring方法被調(diào)用的時(shí)候,它會(huì)創(chuàng)建一個(gè)新的字符串,但是上述的char數(shù)組value仍然會(huì)使用原來父數(shù)組的那個(gè)value。父數(shù)組和子數(shù)組的唯一差別就是count和offset的值不一樣。
看一下JDK6中substring的實(shí)現(xiàn)源碼:
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); //使用的是和父字符串同一個(gè)char數(shù)組value }
String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
String str = "abcdefghijklmnopqrst"; String sub = str.substring(1, 3); str = null;
這段簡(jiǎn)單的程序有兩個(gè)字符串變量str、sub。sub字符串是由父字符串str截取得到的,假如上述這段程序在JDK1.6中運(yùn)行,我們知道數(shù)組的內(nèi)存空間分配是在堆上進(jìn)行的,那么sub和str的內(nèi)部char數(shù)組value是公用了同一個(gè),也就是上述有字符a~字符t組成的char數(shù)組,str和sub唯一的差別就是在數(shù)組中其實(shí)beginIndex和字符長(zhǎng)度count的不同。在第三句,我們使str引用為空,本意是釋放str占用的空間,但是這個(gè)時(shí)候,GC是無法回收這個(gè)大的char數(shù)組的,因?yàn)檫€在被sub字符串內(nèi)部引用著,雖然sub只截取這個(gè)大數(shù)組的一小部分。當(dāng)str是一個(gè)非常大字符串的時(shí)候,這種浪費(fèi)是非常明顯的,甚至?xí)硇阅軉栴},解決這個(gè)問題可以是通過以下的方法:
利用的就是字符串的拼接技術(shù),它會(huì)創(chuàng)建一個(gè)新的字符串,這個(gè)新的字符串會(huì)使用一個(gè)新的內(nèi)部char數(shù)組存儲(chǔ)自己實(shí)際需要的字符,這樣父數(shù)組的char數(shù)組就不會(huì)被其他引用,令str=null,在下一次GC回收的時(shí)候會(huì)回收整個(gè)str占用的空間。但是這樣書寫很明顯是不好看的,所以在JDK7中,substring 被重新實(shí)現(xiàn)了。
JDK7中的substring實(shí)現(xiàn)
在JDK7中改進(jìn)了substring的實(shí)現(xiàn),它實(shí)際是為截取的子字符串在堆中創(chuàng)建了一個(gè)新的char數(shù)組用于保存子字符串的字符。
查看JDK7中String類的substring方法的實(shí)現(xiàn)源碼:
public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); }
Arrays類的copyOfRange方法:
public static char[] copyOfRange(char[] original, int from, int to) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); char[] copy = new char[newLength]; //是創(chuàng)建了一個(gè)新的char數(shù)組 System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy; }
可以發(fā)現(xiàn)是去為子字符串創(chuàng)建了一個(gè)新的char數(shù)組去存儲(chǔ)子字符串中的字符。這樣子字符串和父字符串也就沒有什么必然的聯(lián)系了,當(dāng)父字符串的引用失效的時(shí)候,GC就會(huì)適時(shí)的回收父字符串占用的內(nèi)存空間。
總結(jié)
以上就是本文關(guān)于Java中由substring方法引發(fā)的內(nèi)存泄漏詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
- 排查Java應(yīng)用內(nèi)存泄漏問題的步驟
- Java DWR內(nèi)存泄漏問題解決方案
- Java內(nèi)存泄漏問題處理方法經(jīng)驗(yàn)總結(jié)
- java虛擬機(jī)內(nèi)存溢出及泄漏實(shí)例
- Android性能優(yōu)化之利用Rxlifecycle解決RxJava內(nèi)存泄漏詳解
- Java中關(guān)于內(nèi)存泄漏出現(xiàn)的原因匯總及如何避免內(nèi)存泄漏(超詳細(xì)版)
- 深入理解Java垃圾回收機(jī)制以及內(nèi)存泄漏
- 解析Java的JNI編程中的對(duì)象引用與內(nèi)存泄漏問題
- 淺析Java中的內(nèi)存泄漏
- Java中的內(nèi)存泄漏
相關(guān)文章
Spring框架web項(xiàng)目實(shí)戰(zhàn)全代碼分享
這篇文章主要介紹了Spring框架web項(xiàng)目實(shí)戰(zhàn)全代碼分享,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11SpringBoot接口正確接收時(shí)間參數(shù)的幾種方式
這篇文章主要給大家介紹了關(guān)于SpringBoot接口正確接收時(shí)間參數(shù)的相關(guān)資料,文中通過代碼示例介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用springboot具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09Spring?BeanDefinition父子關(guān)系示例解析
這篇文章主要為大家介紹了Spring?BeanDefinition父子關(guān)系示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Java File類的簡(jiǎn)單使用教程(創(chuàng)建、刪除、遍歷與判斷是否存在等)
這篇文章主要給大家介紹了關(guān)于Java File類的簡(jiǎn)單使用(創(chuàng)建、刪除、遍歷與判斷是否存在等)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12一個(gè)簡(jiǎn)易的Java多頁(yè)面隊(duì)列爬蟲程序
這篇文章主要為大家詳細(xì)介紹了一個(gè)多頁(yè)面的java爬蟲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08Java利用三目運(yùn)算符比較三個(gè)數(shù)字的大小
今天小編就為大家分享一篇關(guān)于Java利用三目運(yùn)算符比較三個(gè)數(shù)字的大小,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12Python實(shí)現(xiàn)filter函數(shù)實(shí)現(xiàn)字符串切分
這篇文章主要介紹了Python實(shí)現(xiàn)filter函數(shù)實(shí)現(xiàn)字符串切分,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03MyBatis綁定錯(cuò)誤提示BindingException:Invalid bound statement (not f
這篇文章主要介紹了MyBatis綁定錯(cuò)誤提示BindingException:Invalid bound statement (not found)的解決辦法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧2017-01-01