栈溢出 StackOverflowError

2019-09-18  本文已影响0人  王某某的笔记

问题

生产环境栈溢出

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)

原因

递归调用次数太多导致
且启动脚本中设置了-Xss

-XX:PermSize=128m -Xss256k 

解决办法

由于涉及到的代码依赖较多,先不改动代码
加大线程栈大小到2m

-XX:PermSize=128m -Xss2m 


线程栈大小与递归次数测试

代码

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

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

执行结果

结果:966

此时的栈设置为: -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

不同栈大小下的递归次数

线程栈大小 递归次数约为
-Xss128k 966
-Xss256k 2467
-Xss512k 5447
-Xss1m 11404
-Xss2m 23323
-Xss3m 35247
-Xss4m 41306

方法中的局部变量会占用栈空间

当递归的方法中定义了一些局部变量时会占用线程栈的空间,从而影响到递归的次数
如将上面的方法稍微修改一下,增加3个局部变量:

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

在栈大小设置为: -Xss128k 时,结果是: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

您可能遇到线程的默认堆栈大小的问题。在Java SE 6中,Sparc的默认值是32位VM中的512k, 64位VM中的1024k。

在x86 Solaris/Linux上,32位虚拟机中是320k, 64位虚拟机中是1024k。

在Windows上,默认的线程堆栈大小是从二进制文件(java.exe)读取的。在Java SE 6中,这个值在32位VM中是320k,在64位VM中是1024k。

您可以通过使用-Xss选项运行来减小堆栈大小。例如:

java -server -Xss64k

注意,在某些版本的Windows上,操作系统可能使用非常粗的粒度来计算线程堆栈大小。如果请求的大小小于默认大小1K或更大,则将堆栈大小四舍五入到默认大小;否则,堆栈大小将四舍五入为1 MB的倍数。

64k是每个线程允许的最小堆栈空间量。

不显式设置-Xss或-XX:ThreadStackSize时,或者把-Xss或者-XX:ThreadStackSize设为0,就是使用“系统默认值”。

用命令查看

# 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)
上一篇 下一篇

猜你喜欢

热点阅读