本节笔者分享一个在实际工作中遇到的栈内存溢出(StackOverflowError)问题,以及其解决方案。

问题介绍:笔者负责的一个Java Web项目在启动的时候,需要有一些初始化操作,而接下来的代码的执行必须要等到相关初始化操作完成。为了实现这个等待的功能,这个项目之前的负责人使用了一个递归方法进行判断,最终导致了应用每次在启动的时候,都会出现StackOverFlowException异常。

首先我们介绍,为什么递归操作会引起栈内存溢出?

线程在执行的时候,会直接调用一系列的方法,而被直接调用的方法可能又调用了其他的方法。为了保证直接或者间接被调用的方法可以按照方法声明的顺序那样进行执行,每个线程都在栈内存中维护了一个数据结构,就是一个栈。一个线程在执行的时候,每当遇到一个方法的开始,就将这个方法的相关信息(称之为栈帧)压入栈,而方法结束后再进行弹栈。通过这种方式保证了方法执行顺序的正确性。

但是因为栈内存的大小是有限制的,默认请下一般是1M。当我们再使用递归方法的时候,方法会存在不断的循环调用,因此会不断的往栈中压入数据,当数据量超过1M的时候,就会出现栈内存溢出。

为了说明上面这个问题,请看以下的代码演示:

  1. public class StackOverFlowDemo {
  2. int count=0;
  3. public void recursiveMethod(){
  4. if (count==1000000){//递归方法执行1000000次时,结束
  5. return;
  6. }
  7. count++;
  8. System.out.println("执行了:"+count+"次");
  9. recursiveMethod();//递归调用
  10. }
  11. public static void main(String[] args) {
  12. StackOverFlowDemo stackOverFlowDemo = new StackOverFlowDemo();
  13. stackOverFlowDemo.recursiveMethod();
  14. System.out.println("执行其他代码...");
  15. }
  16. }

在这段代码中,我们定义了一个递归方法recursiveMethod(),我们希望递归方法执行了1百万次之后结束,为了便于观察,每次递归都打印出当前是第几次迭代。但是真的可以递归1百万次吗?以下是其中一次运行结果:

执行了:3978次

执行了:3979次

执行了:3980次

执行了:3981次

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:579)

at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)

at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)

可以看到,事实上递归了3981次的后,就抛出了StackOverflowError异常。这提示我们,在Java开发过程中,要慎用递归,尤其是在不能预估递归方法大概需要执行多少次的时候,最好就不好使用。

为了解决上述问题,我们需要进行一些改造。

  1. public class SpinLockDemo {
  2. int count=0;
  3. public void incr(){
  4. count++;
  5. System.out.println("执行了:"+count+"次");
  6. }
  7. public static void main(String[] args) {
  8. SpinLockDemo spinLockDemo = new SpinLockDemo();
  9. while(spinLockDemo.count!=1000000){//这段代码其实就是一个自旋锁
  10. spinLockDemo.incr();
  11. }
  12. System.out.println("执行其他代码...");
  13. }
  14. }

因为我们只是希望count变量值达到1百万的时候,才继续执行剩余部分的代码。所以我们可以将判断条件放入一个while循环中,只要没到1000000次,就继续增加。到达之后,循环结束,执行剩余部分的代码。这里的while循环,其实就是所谓的自旋锁(Spin Lock)。需要注意的是:自旋锁不是真正的锁,其只是解决思路的一种方式,只要不能继续往下执行,就不断的循环。

原文地址:http://www.tianshouzhi.com/api/tutorials/mutithread/289

自旋锁解决StackOverflowError案例的更多相关文章

  1. 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02

    百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...

  2. JUC 并发编程--05, Volatile关键字特性: 可见性, 不保证原子性,禁止指令重排, 代码证明过程. CAS了解么 , ABA怎么解决, 手写自旋锁和死锁

    问: 了解volatile关键字么? 答: 他是java 的关键字, 保证可见性, 不保证原子性, 禁止指令重排 问: 你说的这三个特性, 能写代码证明么? 答: .... 问: 听说过 CAS么 他 ...

  3. 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...

  4. Java锁之自旋锁详解

    锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类 ...

  5. 可重入锁 & 自旋锁 & Java里的AtomicReference和CAS操作 & Linux mutex不可重入

    之前还是写过蛮多的关于锁的文章的: http://www.cnblogs.com/charlesblc/p/5994162.html <[转载]Java中的锁机制 synchronized &a ...

  6. SpinLock 自旋锁, CAS操作(Compare & Set) ABA Problem

    SpinLock 自旋锁 spinlock 用于CPU同步, 它的实现是基于CPU锁定数据总线的指令. 当某个CPU锁住数据总线后, 它读一个内存单元(spinlock_t)来判断这个spinlock ...

  7. 自旋锁spin_lock和raw_spin_lock

    自旋锁spin_lock和raw_spin_lock Linux内核spin_lock.spin_lock_irq 和 spin_lock_irqsave 分析 http://blog.csdn.ne ...

  8. linux内核--自旋锁的理解

    http://blog.chinaunix.net/uid-20543672-id-3252604.html 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对 ...

  9. Nginx学习之四-Nginx进程同步方式-自旋锁(spinlock)

    自旋锁简介 Nginx框架使用了三种消息传递方式:共享内存.套接字.信号. Nginx主要使用了三种同步方式:原子操作.信号量.文件锁. 基于原子操作,nginx实现了一个自旋锁.自旋锁是一种非睡眠锁 ...

随机推荐

  1. WPF MVVM示例自定义模板数据绑定

    在触摸屏设备上.由于列表是的信息展示不是非常直观和便捷操作. 所以也就出现了很多用面板控件:类似win10的Metro风格, 所以抽空做了一个WPF面板控件. 话不多上 , 先上一个示例图. 为了便于 ...

  2. [Node.js] Serve Static Files with Express

    In this lesson we will find out how to serve static assets (images, css, stylesheets, etc.) with Exp ...

  3. cv2.putText 文字换行('\n')无法解析换行

    OpenCV putText() new line character cv2.putText 在向图像中添加文本信息时,如果在待添加的文本中含有换行转义符,一般它是无法正确处理的: cv2.putT ...

  4. HTML基础第四讲---图像

    转自:https://blog.csdn.net/likaier/article/details/326735 图像,也就是images,在html语法中用img来表示,其基本的语法是:   < ...

  5. SSH进阶(2)——用Struts拦截器实现登陆限制

    拦截器从字面意思来看就是限制.限制用户訪问某些网页.在Action提出请求之前用拦截器来做权限设置,让符合的用户跳入对应的界面中.近期做的一个商城项目中就用到了自己定义的拦截器,实现了一个简单的ses ...

  6. python3 随机生成10以内的加法算术题

    今晚晚饭过后,看到小孩在做加法题,全是10以内的,因为她现在只会10以内的加法题.而这些题是老婆手动出的题目. 看到这个情景,突然想到,可以用python来实现随机出题,而且可以指定出多少题,出多少以 ...

  7. 7 Java Performance Metrics to Watch After a Major Release--转

    原文地址:https://dzone.com/articles/7-java-performance-metrics-to-watch-after-a-major-1 The Java perform ...

  8. Android(Lollipop/5.0) Material Design(四) 创建列表和卡片

    Material Design系列 Android(Lollipop/5.0)Material Design(一) 简单介绍 Android(Lollipop/5.0)Material Design( ...

  9. nodejs+express4.0+mongodb安装方法 for Linux, Mac

    废话不多说 1:下载nodejs包 下载地址例如以下:http://www.nodejs.org/download/ 下载source code版本号须要解压后到其文件夹运行./configure,然 ...

  10. 微信端 h5 视频 video 自动播放

    document.addEventListener("WeixinJSBridgeReady",function(){ document.getElementById(" ...