![](/img/trans.png)
[英]Code gives java.lang.StackOverflowError for 123456789 but not for 9999999999999999999
[英]Java gives seemingly random StackOverflowError
這是我的一個項目中的Factorial函數:
public class Fac {
public static void main(String[] args) {
System.out.println(fac(9000));
}
public static long fac(long x) {
if (x <= 1) {
return 1;
}
else {
return x * fac(x-1);
}
}
}
當使用大參數(例如fac(9000)
運行此函數時,它有時(但不總是)會導致StackOverflowError。 我注意到,當我從我的IDE(IntelliJ Community Edition 2018.2.1)中啟動調用fac(9000)
的程序時,錯誤發生在3次中的2次,但是當我從shell啟動時它可能是調用超過300次或更多,同時只崩潰一次或兩次(我使用一個簡短的bash腳本來測試這個)。 我在Ubuntu 18.04上使用openjdk-11。
我已經嘗試使用-Xss1024k
設置顯式堆棧大小(也使用了其他一些大小),但問題仍然存在。
我知道存儲在long中的結果將不再正確(當它沒有崩潰時結果為0
),但我不知道是什么原因導致看似隨機的崩潰以及為什么它從內部運行時更頻繁地發生IDE。
這幾乎肯定是一個JIT問題。
值得注意的是,由於java中的“堆棧”不是非常傳統,因此StackOverflowError
以非常奇怪的方式觸發。 我不會深入細節,但它似乎基本上是堆中的鏈表。
鑒於人們在評論中報告的一些症狀,我認為它與JIT密切相關。 引用西蒙的話
當我在一個簡單的for(int i = 0; i <100000; ++ i){fib(9000); 出現兩種情況之一:程序在第一次調用fib(9000)時崩潰,或者在100000次調用中都沒有崩潰。
通常,JIT應該在一致點激活,特別是如果運行中沒有變化。 如果您決定啟用編譯打印,您會注意到正在編譯的許多類似乎根本與您的程序無關。 這可能是有問題的,因為代碼可能需要一段時間才能編譯(JIT編譯作為隊列處理)。 另一個問題是JIT是非阻塞的。 這意味着JIT將被觸發,然后它將在另一個線程中編譯該方法,然后它將方法指針注入到程序中。 因為它是非阻塞的,所以它可能在函數觸發StackOverflowError
后完成。
有很多方法可以提高成功幾率。
Go to details
來完成(我只能在Win10中確認如何執行此操作)。 這將帶您進入“詳細信息”頁面並選擇該流程。 您現在可以右鍵單擊流程詳細信息並選擇“ Set affinity
,取消選擇除一個之外的所有核心。 這應該強制程序在單個核心上下文中運行,這將導致所有線程都被阻塞,包括JIT。 這確實有一個值得注意的警告,GC現在將阻止。 Set priority
,然后選擇“ High
(我發現選擇“ Realtime
會導致系統停滯)。 這將把進程線程放在列表的頂部以便運行其他東西,這有望讓你的JIT在核心上獲得更多的連續時間來進行編譯。 對於#2和#3,您可能必須在程序中使用某些內容來停止執行,直到您設置優先級或關聯。 您可以選擇使用Process Hacker之類的程序來保存優先級或親和力,並在每次啟動時立即設置。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.