已經解決java.lang.OutOfMemoryError

cute發表於2003-07-11
有人說:
“不斷的將被選中的字串加到某一字串末尾,當長度超過一定量是就提示:
java.lang.StringIndexOutOfBoundsException: String index out of range: 10
”說明String有長度限制。
看一下Java API就會知道
java.lang.StringIndexOutOfBoundsException出現的情況是
Thrown by String methods to indicate that an index is either negative or greater than the size of the string. For some methods such as the charAt method。
上面的錯誤是因為
String.length()<10;
而你又要取index>=10的字元,自然就會丟擲上面的例外。
String其實是沒有限制的,而是當String太大了,超過JVM的自身的記憶體後會丟擲
java.lang.OutOfMemoryError錯誤

下面作個實驗:
public class testString{
public static void main(String args[])
{
String s="abbbbb";
System.out.println("JVM MAX MEMORY: "+Runtime.getRuntime().maxMemory()/1024/1024+"M");
System.out.println("JVM IS USING MEMORY:"+Runtime.getRuntime().totalMemory()/1024/1024+"M");
Runtime.getRuntime().traceMethodCalls(true);
while(true)
{
try{
s=s+s;

}catch(Exception e)
{
System.out.println(e);
}
catch(Error o)
{ String unit = null;
int sizeb = s.length();
int size = sizeb;
int time = 0;
while(size>1024)
{
size = size/1024;
time++;
}
switch(time)
{
case 0: unit = "byte";break;
case 1: unit = "k"; break;
case 2: unit = "M"; break;
default : unit = "byte";
}

System.out.println("String has used memory:"+size+unit);
System.out.println("JVM IS USING MEMORY:"+(float)Runtime.getRuntime().totalMemory()/1024/1024+"M");
System.out.println("MemoryError:"+o);
break;
}

}
}
}
然後我們用JVM的預設引數執行(我的機器記憶體是128M)
java testString
結果:
JVM MAX MEMORY: 128M
JVM IS USING MEMORY:1M
String has used memory:12M
JVM IS USING MEMORY:63.5625M
MemoryError:java.lang.OutOfMemoryError
開始JVM使用的記憶體是1M,當String為12M,JVM使用了63M多時
JVM溢位。

然後,我們用限制JVM記憶體大小的引數來執行,限制最大記憶體5M
java -mx5m testString
結果:
JVM MAX MEMORY: 70M
JVM IS USING MEMORY:1M
String has used memory:768.0k
JVM IS USING MEMORY:5.9375M
MemoryError:java.lang.OutOfMemoryError
開始JVM使用的記憶體是1M,當String為768k,JVM使用了5M多時
JVM溢位。

大家還可以改變 -mx引數,來進一步做實驗。
以上兩個實驗證明,String是沒有長度限制的,而是有JVM的記憶體限制了String的長度。同時說明,並不會丟擲任何Exception而只會丟擲Error.

OutMemoryError表明程式的設計很差,或者遇到了超出程式設計人員所預想的大批次的資料。不管哪種情況,都只有下面這幾種解決辦法。它們是:

設計人員重新設計程式,不致使程式一次載入所有的資料。

資料可以分割成更小的塊。

可以為程式分配更多的記憶體。

為Java虛擬機器提供更多的記憶體。

而上面的例子是為虛擬機器提供更多的記憶體

=======================================
其實應該少用String這東西,特別是 String的 +=操作
不僅原來的String物件不能繼續使用,主要是又要new出N多的新物件出來,再多的memory也要out~~
String用char array實現,就肯定由長度限制的,不能用memory來衡量

==================================
例如上面的程式改用StringBuffer實現,就可以得到極大的改善。
下面是我改用StringBuffer做的測試:
注意:程式迴圈了2097150次!
是使用String的程式的99864倍!

public class TestStringBuffer{
public static void main(String args[])
{
String s="abbbbb";
StringBuffer sb = new StringBuffer(s);
System.out.println("JVM IS USING MEMORY:"+
(Runtime.getRuntime().totalMemory()/1024/1024)+
"M");
Runtime.getRuntime().traceMethodCalls(true);

int count = 0;
while(true)
{
try{
sb.append(s);
count++;

}catch(Exception e)
{
System.out.println(e);
}
catch(Error o)
{
String unit = null;
int size = sb.length();
size *= 2;

int time = 0;
while(size>1024)
{
size = size/1024;
time++;
}
switch(time)
{
case 0: unit = "byte";break;
case 1: unit = "k"; break;
case 2: unit = "M"; break;
default : unit = "byte";
}

System.out.println("Loop times:"+count);
System.out.println("String has used memory:"+size+unit);
System.out.println("JVM IS USING MEMORY:"+
(float)Runtime.getRuntime().totalMemory()/1024/1024+
"M");
System.out.println("MemoryError:"+o);
break;
}

}
}
}

輸出結果:
JVM IS USING MEMORY:1M
Loop times:2097150
String has used memory:23M
JVM IS USING MEMORY:63.75M
MemoryError:java.lang.OutOfMemoryError



=====================
從另一方面說,如果你要處理的字串達到百兆甚至上GB,使用String物件,根本沒法工作,所以這個問題不需要太多討論。看一下jdk的原始檔,String的長度是String物件的一個成員count,型別是int,不是long,也不是char。知道這些,我認為夠了。

相關文章