堆溢出和堆栈溢出示例

时间:2016-03-23 14:16:36

标签: java stack-overflow heap-memory

我需要帮助理解堆和堆栈溢出,所以如果我没有很好的例子来理解这个概念会很有用

1 个答案:

答案 0 :(得分:2)

正如评论所指出的那样,互联网上已有很多关于此的文章。不过,我很喜欢动手研究,所以这里有一些示例代码(如下)让您入门。我希望你能找到以下发人深省的想法......得到一个简单的答案是一回事,我认为你会找到通过数据点工作,这样你才能理解答案意味着什么更令人满意。

示例代码(见下文)在main()中硬连线,以调用stress_heap()或stress_stack()。 它几乎在我的系统上死得太厉害了,catch块对报告结果没有帮助,所以我添加了一些中间打印语句来显示正在发生的事情。

这是一些图表(我第一次发布图片,所以我希望它有效)。 “cnt”表示调用stress_heap()或stress_stack()的次数。内存值免费&amp;总数来自Java Runtime class。 (总计是主机系统授予特定Java实例使用的程度,而 free 是可用于创建其他对象的总内存量中的多少。)< / p>

plot of output when compiled for stress_heap() plot of output when compiled for stress_stack()

您需要了解的关键是可用内存(堆)与堆栈大小。 在Java中,所有对象实例都存在于堆中。 对象引用(和原语)作为params直接传递给堆栈,它不使用堆。

需要考虑的一些问题:当你能够回答这些问题时,你将更好地理解堆与堆栈。

  • 为什么stress_heap()的计数要大得多 stress_stack()?
  • 为什么stress_heap()的总内存增加但是 不是对于stress_stack()?
  • 为什么空闲内存会增加 对stress_heap()有所减少,但对stress_stack()没有减少吗?

以下是上述图表所基于的示例输出(省略冗余行):

输出,为stress_heap()

编译
$ java Stress
stress_heap() cnt=100000 free=264613280 total=381157376
stress_heap() cnt=200000 free=450105464 total=661127168
stress_heap() cnt=300000 free=343009208 total=661127168
stress_heap() cnt=400000 free=589030360 total=1009778688
stress_heap() cnt=500000 free=485900632 total=1009778688
...45 lines deleted...
stress_heap() cnt=5100000 free=327236008 total=5656018944
stress_heap() cnt=5200000 free=214346344 total=5656018944
stress_heap() cnt=5300000 free=115567888 total=5656018944
stress_heap() cnt=5400000 free=16789432 total=5656018944
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at Stress.stress_heap(Stress.java:14)
    at Stress.main(Stress.java:45)
$ 

输出,为stress_stack()

编译
$ java Stress
stress_stack_recurse() cnt=100 free=377151576 total=381157376
stress_stack_recurse() cnt=200 free=377151576 total=381157376
...114 lines deleted...
stress_stack_recurse() cnt=11700 free=377151576 total=381157376
stress_stack_recurse() cnt=11800 free=377151576 total=381157376
Exception in thread "main" java.lang.StackOverflowError
    at java.util.Arrays.copyOf(Arrays.java:2367)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:130)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:114)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:415)
    at java.lang.StringBuilder.append(StringBuilder.java:132)
    at Stress.stress_stack_recurse(Stress.java:34)
    at Stress.stress_stack_recurse(Stress.java:36)
...1,015 lines deleted...
    at Stress.stress_stack_recurse(Stress.java:36)
    at Stress.stress_stack_recurse(Stress.java:36)
$

Stress.java - 示例程序

根据您运行此系统的系统,您可能需要向上或向下调整输出间隔(计数阈值)。

我建议您修改stress_stack_recurse()以添加其他原始参数,例如:

stress_stack_recurse1( int a ) { ... stress_stack_recurse(a+1); }
stress_stack_recurse4( int a, int b, int c, int d )  { ... stress_stack_recurse4(a+1, b+1, c+1, d+1); }

我认为您需要修改参数,以便编译器不会优化它们,可能需要将它们添加到条件打印中以确保它们在方法体中使用。

这里的目标是根据参数的数量来看看cnt在失败之前有多高。

类似的研究项目是更改stress_heap()中的 size 值,并查看运行到1024 * 16(16kb)或1024 * 1024(1mb)大小块的时间长度。

import java.util.*;
public class Stress {

   static Runtime runtime = Runtime.getRuntime();
   static long cnt = 0;

   public static void stress_heap() {
      int size = 1024; // start with 1kb
      // research question: what happens to cnt as you modify size ?
      List<byte[]> a = new ArrayList<>(); // keep a list of byte arrays.
      System.out.println("stress_heap(): size="+size);
      try {
         while( true ) {
            byte[] b = new byte[size];
            a.add( b );
            ++cnt; // only increment if successful.
            if( 0 == (cnt % 100000) ) {
               // Question: does this output display at the same tempo?
               // Or does it get faster (or slower) as the program runs?
               System.out.println("stress_heap() cnt="+cnt+" free="+runtime.freeMemory()+" total="+runtime.totalMemory() );
            }
         }
      } catch( Exception e ) { 
         // Sometimes fails so hard it doesn't reach this catch block.
         System.out.println("stress_heap(): problem, cnt="+cnt+", exception="+e);
      }
   }

   public static void stress_stack_recurse( ) { 
      ++cnt;
      if( 0 == (cnt % 100) ) {
         // Question: why does stress_stack_recurse() fail at such a low value count compared to stress_heap() ?
         // Question: does this output display at the same tempo?
         System.out.println("stress_stack_recurse() cnt="+cnt+" free="+runtime.freeMemory()+" total="+runtime.totalMemory() );
      }
      stress_stack_recurse( );
   }

   public static void stress_stack( ) { 
      // want the 
      try {
         stress_stack_recurse( );
      } catch( Exception e ) { 
         // Sometimes fails so hard it doesn't reach this catch block.
         System.out.println("stress_stack(): problem, cnt="+cnt+", exception="+e);
      }
   }


   public static void main( String[] args ) {
      //stress_heap();
      stress_stack();
   }
}

对于奖励积分,您可以尝试传递在stress_stack()中分配的新字节数组 - 实际上是任何对象,但是字节数组似乎最容易推断使用内存。

这可能看起来像这样(这是未经测试的代码):

   public static void main( String[] args ) {
      //stress_heap();
      //stress_stack();
      byte[] b = new byte[1024];
      stress_both( b);
   }

   public static void stress_both( byte[] b ) {
      ++cnt;
      if( 0 == (cnt % 100) ) {
         System.out.println("stress_both() cnt="+cnt+" b.length="+b.length+" free="+runtime.freeMemory()+" total="+runtime.totalMemory() );
      }
      byte[] yetAnotherByteArray = new byte[1024];
      stress_stack_recurse( yetAnotherByteArray );

   }
}