我需要帮助理解堆和堆栈溢出,所以如果我没有很好的例子来理解这个概念会很有用
答案 0 :(得分:2)
正如评论所指出的那样,互联网上已有很多关于此的文章。不过,我很喜欢动手研究,所以这里有一些示例代码(如下)让您入门。我希望你能找到以下发人深省的想法......得到一个简单的答案是一回事,我认为你会找到通过数据点工作,这样你才能理解答案意味着什么更令人满意。
示例代码(见下文)在main()中硬连线,以调用stress_heap()或stress_stack()。 它几乎在我的系统上死得太厉害了,catch块对报告结果没有帮助,所以我添加了一些中间打印语句来显示正在发生的事情。
这是一些图表(我第一次发布图片,所以我希望它有效)。 “cnt”表示调用stress_heap()或stress_stack()的次数。内存值免费&amp;总数来自Java Runtime class。 (总计是主机系统授予特定Java实例使用的程度,而 free 是可用于创建其他对象的总内存量中的多少。)< / p>
您需要了解的关键是可用内存(堆)与堆栈大小。 在Java中,所有对象实例都存在于堆中。 对象引用(和原语)作为params直接传递给堆栈,它不使用堆。
需要考虑的一些问题:当你能够回答这些问题时,你将更好地理解堆与堆栈。
以下是上述图表所基于的示例输出(省略冗余行):
$ 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)
$
$ 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_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 );
}
}