棧溢出 StackOverflowError

問題

生產(chǎn)環(huán)境棧溢出

3282788         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:410)
3282789         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:410)
3282790 Exception in thread "pool-250-thread-43" java.lang.StackOverflowError
3282791         at java.util.AbstractCollection.toArray(AbstractCollection.java:183)
3282792         at java.lang.String.split(String.java:2378)
3282793         at com.chedaia.task.address.util.ParkingPointUtil.isGps(ParkingPointUtil.java:32)
3282794         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:406)
3282795         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:410)
3282796         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:410)
3282797         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:410)
3282798         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:410)
3282799         at com.chedaia.task.address.util.ParkingPointUtil.getPreviewGpsPoint(ParkingPointUtil.java:410)

原因

遞歸調(diào)用次數(shù)太多導(dǎo)致
且啟動(dòng)腳本中設(shè)置了-Xss

-XX:PermSize=128m -Xss256k 

解決辦法

由于涉及到的代碼依賴較多,先不改動(dòng)代碼
加大線程棧大小到2m

-XX:PermSize=128m -Xss2m 


線程棧大小與遞歸次數(shù)測(cè)試

代碼

    public static void main(String[] args) {
        recursion(0);
    }

    public static void recursion(int i) {
        System.out.println(i);
        i++;
        recursion(i);
    }

執(zhí)行結(jié)果

結(jié)果:966

此時(shí)的棧設(shè)置為: -Xss128k

963
964
965
966
Exception in thread "main" java.lang.StackOverflowError
    at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
    at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)
    at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)
    at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
    at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java

不同棧大小下的遞歸次數(shù)

線程棧大小 遞歸次數(shù)約為
-Xss128k 966
-Xss256k 2467
-Xss512k 5447
-Xss1m 11404
-Xss2m 23323
-Xss3m 35247
-Xss4m 41306

方法中的局部變量會(huì)占用棧空間

當(dāng)遞歸的方法中定義了一些局部變量時(shí)會(huì)占用線程棧的空間,從而影響到遞歸的次數(shù)
如將上面的方法稍微修改一下,增加3個(gè)局部變量:

    public static void recursion(int i) {
        System.out.println(i);
        long a = 1l;
        long b = 2l;
        long c = a + b;
        i++;
        recursion(i);
    }

在棧大小設(shè)置為: -Xss128k 時(shí),結(jié)果是:623

620
621
622
623
java.lang.StackOverflowError
    at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
    at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)
    at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)
    at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)

官方說明

https://www.oracle.com/technetwork/java/hotspotfaq-138619.html#threads_oom

您可能遇到線程的默認(rèn)堆棧大小的問題。在Java SE 6中,Sparc的默認(rèn)值是32位VM中的512k, 64位VM中的1024k。

在x86 Solaris/Linux上,32位虛擬機(jī)中是320k, 64位虛擬機(jī)中是1024k。

在Windows上,默認(rèn)的線程堆棧大小是從二進(jìn)制文件(java.exe)讀取的。在Java SE 6中,這個(gè)值在32位VM中是320k,在64位VM中是1024k。

您可以通過使用-Xss選項(xiàng)運(yùn)行來減小堆棧大小。例如:

java -server -Xss64k

注意,在某些版本的Windows上,操作系統(tǒng)可能使用非常粗的粒度來計(jì)算線程堆棧大小。如果請(qǐng)求的大小小于默認(rèn)大小1K或更大,則將堆棧大小四舍五入到默認(rèn)大小;否則,堆棧大小將四舍五入為1 MB的倍數(shù)。

64k是每個(gè)線程允許的最小堆??臻g量。

不顯式設(shè)置-Xss或-XX:ThreadStackSize時(shí),或者把-Xss或者-XX:ThreadStackSize設(shè)為0,就是使用“系統(tǒng)默認(rèn)值”。

用命令查看

# java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
     intx CompilerThreadStackSize                   = 0                                   {pd product}
     intx ThreadStackSize                           = 1024                                {pd product}
     intx VMThreadStackSize                         = 1024                                {pd product}
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • StackOverflowError原因 ?Java 里的 StackOverflowError。拋出這個(gè)錯(cuò)誤表明...
    SunnyMore閱讀 13,184評(píng)論 0 4
  • 前言 筆者使用http上傳大文件,采用分片上傳的方式,下把大文件分割成等分的小文件上傳到服務(wù)器目錄再合并到寫入大文...
    南風(fēng)nanfeng閱讀 1,353評(píng)論 0 1
  • 從內(nèi)存模型開始說起:作為一般人需要了解到的,JVM的內(nèi)存區(qū)域可以被分為:線程棧,堆,靜態(tài)方法區(qū)(實(shí)際上還有更多功能...
    ice_aisi閱讀 362評(píng)論 0 1
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方,同時(shí)不同JDK版本的...
    高廣超閱讀 16,042評(píng)論 3 83
  • 趙吳迪雖然殺了機(jī)器天藍(lán)和機(jī)器焰火,但是還不解恨,因?yàn)闄C(jī)器天藍(lán)殺了李奇緣,就又想把機(jī)器李奇緣也給殺了。 ...
    夕陽下的夢(mèng)比優(yōu)斯閱讀 107評(píng)論 0 0

友情鏈接更多精彩內(nèi)容