堆溢出和堆栈溢出示例
Heap Overflow And Stack Overflow examples
我在理解堆和堆栈溢出方面需要帮助,所以如果我有几个例子来很好地理解这个概念,那将会很有帮助
正如评论所指出的,互联网上已经有很多关于此的文章。尽管如此,我还是喜欢动手研究,所以这里有一些示例代码(如下)可以帮助您入门。我希望您发现以下发人深省...获得一个简单的答案是一回事,我认为您会发现通过数据点进行工作,以便您理解答案的含义更令人满意。
示例代码(见下文)在 main() 中硬连线以调用 stress_heap() 或 stress_stack()。
它在我的系统上几乎死机,以至于 catch 块对报告结果没有帮助,所以我添加了一些中间打印语句来显示正在发生的事情。
这里有一些图表(第一次发图片,希望有用)。
"cnt" 表示 stress_heap() 或 stress_stack() 被调用的次数。内存值 free 和 total 来自 Java Runtime class。 (total 是主机系统授予特定 Java 实例使用的数量,而 free 是总数中的多少内存可用于创建其他对象)。
您要了解的关键是可用内存(堆)与堆栈大小。
在 Java 中,所有对象实例都存在于堆中。
对象引用(和原语)直接在堆栈中作为参数传递,不使用堆。
需要考虑的一些问题:当您能够回答这些问题时,您将对堆与堆栈有更好的理解。
- 为什么 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.WhosebugError
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 值,看看 运行s 到 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 );
}
}
我在理解堆和堆栈溢出方面需要帮助,所以如果我有几个例子来很好地理解这个概念,那将会很有帮助
正如评论所指出的,互联网上已经有很多关于此的文章。尽管如此,我还是喜欢动手研究,所以这里有一些示例代码(如下)可以帮助您入门。我希望您发现以下发人深省...获得一个简单的答案是一回事,我认为您会发现通过数据点进行工作,以便您理解答案的含义更令人满意。
示例代码(见下文)在 main() 中硬连线以调用 stress_heap() 或 stress_stack()。 它在我的系统上几乎死机,以至于 catch 块对报告结果没有帮助,所以我添加了一些中间打印语句来显示正在发生的事情。
这里有一些图表(第一次发图片,希望有用)。 "cnt" 表示 stress_heap() 或 stress_stack() 被调用的次数。内存值 free 和 total 来自 Java Runtime class。 (total 是主机系统授予特定 Java 实例使用的数量,而 free 是总数中的多少内存可用于创建其他对象)。
您要了解的关键是可用内存(堆)与堆栈大小。 在 Java 中,所有对象实例都存在于堆中。 对象引用(和原语)直接在堆栈中作为参数传递,不使用堆。
需要考虑的一些问题:当您能够回答这些问题时,您将对堆与堆栈有更好的理解。
- 为什么 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.WhosebugError
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 值,看看 运行s 到 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 );
}
}