問題
生產(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)