FIR*_*AST 12 java stack-overflow exception-handling exception
据我所知,这段代码应该抛出StackOverflowError,但事实并非如此.可能是什么原因?
public class SimpleFile {
public static void main(String[] args) {
System.out.println("main");
try{
SimpleFile.main(args);
}
catch(Exception e){
System.out.println("Catch");
}
finally{
SimpleFile.main(args);
}
}
}
Run Code Online (Sandbox Code Playgroud)
一个错误是不是Exception
.因此捕获任何异常都不会捕获StackOverflowError.
所以让我们从修复"明显的错误"开始 - (这个代码是不可取的,如本答案后面所述):
catch(Throwable e){
System.out.println("Catch");
}
Run Code Online (Sandbox Code Playgroud)
如果进行此更改,您将发现代码仍然无法打印.但它没有打印出一个非常不同的原因......
抓住任何ERROR
(包括a StackOverflowError
)是非常沮丧的.但是在这里你不仅要抓住一个,而是抓住一个,因为它发生在堆栈顶部.即使使用您的代码(没有上述更改),该finally
块也可以有效地捕获错误.
StackOverflowError
当堆栈已满并且您尝试向其添加更多内容时,会发生A.因此,当您捕获错误时,堆栈仍然已满.您无法调用任何方法(甚至打印到控制台),因为堆栈已满.所以在它成功打印之前会StackOverflowError
抛出一秒钟catch
.
结果是:
finally
,因为最后总是被调用.main
这里的关键是最终会开始印刷.但是调用print
使用大量的堆栈空间和代码将需要通过上述点重复和错误很长一段时间才能释放足够的堆栈空间进行打印.根据Holger的评论,使用Oracle的Java 8将main
堆栈帧所需的println
堆栈帧数接近50.
250 = 1,125,899,906,842,624
这就是为什么你不应该抓住错误.
只有少数借口可以让你违反这条规则,并且你已经发现了如果你打破它可以解决的问题.
Log*_*gos -1
静态方法是永久性的,它不存储在堆栈中,而是存储在堆中。您编写的代码只是从堆中一遍又一遍地调用相同的代码,因此它不会抛出StackOverFlowError
. 此外,内部的字符串System.out.println("main");
存储在永久的同一位置。即使您一遍又一遍地调用代码,使用相同的字符串对象,它不会填充堆栈。
我从以下链接得到了这个解释:
http://www.oracle.com/technetwork/java/javase/memleaks-137499.html#gbyuu
3.1.2 详细消息:PermGen space 详细消息PermGen space 表示永久代已满。永久代是堆中存储类和方法对象的区域。如果应用程序加载大量类,则可能需要使用 -XX:MaxPermSize 选项增加永久代的大小。
Interned java.lang.String 对象也存储在永久代中。java.lang.String 类维护一个字符串池。当调用 intern 方法时,该方法会检查池中是否已存在相等的字符串。如果有,则intern方法返回它;否则它会将字符串添加到池中。更准确地说,java.lang.String.intern方法用于获取字符串的规范表示;结果是对同一类实例的引用,如果该字符串作为文字出现,则将返回该实例。如果应用程序驻留大量字符串,则可能需要增加永久代的默认设置。
发生此类错误时,文本 String.intern 或 ClassLoader.defineClass 可能会出现在打印的堆栈跟踪顶部附近。
jmap -permgen 命令打印永久代中对象的统计信息,包括有关内部化 String 实例的信息。