2011-09-06 21 views
2

的我故意创建了以下类导致内存不足的错误逼出来的java内存..不一致的java行为

public class Test1 
{ 
    public static void main(String[] args) 
    { 
     StringBuffer sb = new StringBuffer(); 
     while(true) 
     { 
      Test1 a = new Test1(); 
      sb.append(a.toString()); 
     } 
    } 
} 

的如我所料这上面的类失败,我想要的东西......

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
     at java.util.Arrays.copyOf(Unknown Source) 
     at java.lang.AbstractStringBuilder.expandCapacity(Unknown Source) 
     at java.lang.AbstractStringBuilder.append(Unknown Source) 
     at java.lang.StringBuffer.append(Unknown Source) 
     at Test1.main(Test1.java:10) 

但这:

public class Test1 
{ 
    public static void main(String[] args) 
    { 
     StringBuffer sb = new StringBuffer(); 
     while(true) 
     { 
      Test1 a = new Test1(); 
      System.out.println(sb.toString()); 
      sb.append(a.toString()); 
     } 
    } 
} 

不会崩溃。运行良好,通过在控制台上反复打印对象地址。

我的问题是:

简单的SOP有什么不同?

+3

SOP只是推迟了崩溃。 – Ishtar

+0

@Ishtar实际上没有。那么在某些时候它可能会,但我监控了它很长的一段时间,并没有崩溃。 – Ayusman

+0

@Ayusman:你的JVM有多少内存?你是否也观察过内存消耗,例如'jconsole'? –

回答

9

你的假设没有OutOfMemoryError可能是不正确的。这只是大规模推迟。打印出来的字符串越来越大,需要花费很多时间,因此您的循环可能需要一个小时才能耗尽内存。

您可以通过每10,100,1000次打印来仔细检查一次。您会看到错误会在您生成的IO越少之前发生。也许你会看到这样的曲线jconsole

enter image description here

正如你可以看到,堆是缓慢而稳步上升。即使我试图强制垃圾回收(15:02和15:07),我也无法释放所有内存。但由于我仍然只占我堆的5%,因此我现在不再运行你的代码:-)

+0

和Ishtar建议的那样非常多的答案。正如Lukas指出的那样,我的内存使用量正在增加,看起来在我的代码崩溃之前需要很长时间。但它最终会。多谢你们。卢卡斯建议使用jconsole确实有帮助。有时候,我倾向于忘记我可以使用的漂亮工具。再次感谢。 – Ayusman

+0

@Ayusman,不客气;-) –

4

刚刚测试了一下,实际的瓶颈是sb.toString()。这当然需要与字符串(缓冲区)的长度成比例的时间,所以每一个下一个循环都需要稍长的时间来执行。在内存耗尽之前,在几千个循环之后,一个循环将需要几秒钟才能创建字符串。

替换sb.toString()long计数器,使它“即刻”崩溃。删除System.out.println()对速度影响不大。

在我的电脑java -Xmx2m Test1 > /dev/null需要约8分钟崩溃。如果堆大小正常,则可能需要几天时间。 (随意试试吧。)

+1

+1重定向到'/ dev/null'。尝试运行此代码时,我崩溃了Eclipse :-) –

+0

+1将SOP的参数更改为计数器。 –