JVM StackOverFlowError
文章目录
- 问题信息
- 出现问题的环境
- 问题描述
- 错误信息
- 排查过程
- 初次排查
- 再起波澜
- 问题原因
- 解决方案
- 修改JVM大小
- SQL优化
问题信息
出现问题的环境
Linux + Tomcat + jdk1.8
Mybatis + 阿里的druid数据源
问题描述
通过手机号去数据库里面查询数据,手机号太多就会出现栈溢出异常。
linux 300+作业出现溢出
错误代码信息
错误信息
Caused by: java.lang.StackOverflowError
at com.alibaba.druid.sql.visitor.SQLASTOutputVisitor.print(SQLASTOutputVisitor.java:182) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.sql.dialect.oracle.visitor.OracleParameterizedOutputVisitor.visit(OracleParameterizedOutputVisitor.java:79) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.sql.ast.expr.SQLCharExpr.accept0(SQLCharExpr.java:44) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.sql.ast.SQLObjectImpl.accept(SQLObjectImpl.java:43) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.sql.visitor.SQLASTOutputVisitor.printAndAccept(SQLASTOutputVisitor.java:226) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.sql.visitor.SQLASTOutputVisitor.visit(SQLASTOutputVisitor.java:499) ~[druid-0.2.22.jar:0.2.22]
…
at com.alibaba.druid.filter.stat.StatFilter.mergeSql(StatFilter.java:145) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.filter.stat.StatFilter.createSqlStat(StatFilter.java:629) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.filter.stat.StatFilter.internalBeforeStatementExecute(StatFilter.java:397) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.filter.stat.StatFilter.statementExecuteBefore(StatFilter.java:345) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:438) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:2927) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:118) ~[druid-0.2.22.jar:0.2.22]
at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:493) ~[druid-0.2.22.jar:0.2.22]
at sun.reflect.GeneratedMethodAccessor76.invoke(Unknown Source) ~[?:?]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_73]
at java.lang.reflect.Method.invoke(Method.java:497) ~[?:1.8.0_73]
at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:55) ~[mybatis-3.2.1.jar:3.2.1]
at com.sun.proxy. P r o x y 282. e x e c u t e ( U n k n o w n S o u r c e ) [ ? : ? ] a t o r g . a p a c h e . i b a t i s . e x e c u t o r . s t a t e m e n t . P r e p a r e d S t a t e m e n t H a n d l e r . q u e r y ( P r e p a r e d S t a t e m e n t H a n d l e r . j a v a : 56 ) [ m y b a t i s − 3.2.1. j a r : 3.2.1 ] a t o r g . a p a c h e . i b a t i s . e x e c u t o r . s t a t e m e n t . R o u t i n g S t a t e m e n t H a n d l e r . q u e r y ( R o u t i n g S t a t e m e n t H a n d l e r . j a v a : 70 ) [ m y b a t i s − 3.2.1. j a r : 3.2.1 ] a t s u n . r e f l e c t . G e n e r a t e d M e t h o d A c c e s s o r 78. i n v o k e ( U n k n o w n S o u r c e ) [ ? : ? ] a t s u n . r e f l e c t . D e l e g a t i n g M e t h o d A c c e s s o r I m p l . i n v o k e ( D e l e g a t i n g M e t h o d A c c e s s o r I m p l . j a v a : 43 ) [ ? : 1.8. 0 7 3 ] a t j a v a . l a n g . r e f l e c t . M e t h o d . i n v o k e ( M e t h o d . j a v a : 497 ) [ ? : 1.8. 0 7 3 ] a t o r g . a p a c h e . i b a t i s . p l u g i n . P l u g i n . i n v o k e ( P l u g i n . j a v a : 59 ) [ m y b a t i s − 3.2.1. j a r : 3.2.1 ] a t c o m . s u n . p r o x y . Proxy282.execute(Unknown Source) ~[?:?] at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:56) ~[mybatis-3.2.1.jar:3.2.1] at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:70) ~[mybatis-3.2.1.jar:3.2.1] at sun.reflect.GeneratedMethodAccessor78.invoke(Unknown Source) ~[?:?] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_73] at java.lang.reflect.Method.invoke(Method.java:497) ~[?:1.8.0_73] at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:59) ~[mybatis-3.2.1.jar:3.2.1] at com.sun.proxy. Proxy282.execute(UnknownSource) [?:?]atorg.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:56) [mybatis−3.2.1.jar:3.2.1]atorg.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:70) [mybatis−3.2.1.jar:3.2.1]atsun.reflect.GeneratedMethodAccessor78.invoke(UnknownSource) [?:?]atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [?:1.8.073]atjava.lang.reflect.Method.invoke(Method.java:497) [?:1.8.073]atorg.apache.ibatis.plugin.Plugin.invoke(Plugin.java:59) [mybatis−3.2.1.jar:3.2.1]atcom.sun.proxy.Proxy280.query(Unknown Source) ~[?:?]
at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:57) ~[mybatis-3.2.1.jar:3.2.1]
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:259) ~[mybatis-3.2.1.jar:3.2.1]
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:132) ~[mybatis-3.2.1.jar:3.2.1]
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:115) ~[mybatis-3.2.1.jar:3.2.1]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104) ~[mybatis-3.2.1.jar:3.2.1]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98) ~[mybatis-3.2.1.jar:3.2.1]
at sun.reflect.GeneratedMethodAccessor90.invoke(Unknown Source) ~[?:?]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_73]
at java.lang.reflect.Method.invoke(Method.java:497) ~[?:1.8.0_73]
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:354) ~[mybatis-spring-1.2.0.jar:1.2.0]
排查过程
初次排查
由于是stackOverFlowError, 错误日志里面,有很长的日志都是druid在设置参数,怀疑是递归层级太深,超过了栈允许的最大深度。
想在自己的开发环境重现, Windows + Junit + jdk1.7,使用相同数据库,一次测试300+手机号,正常。 直接改成上限1000 (oracle in 传参上限),运行多次正常。
此时初步判断不是调用层级太深, 环境问题?
此时查看Linux上的JVM启动参数配置,-Xss256k (Thread Stack Size)
本地开发环境再验证时,增加-Xss256k设置,手机号一次验证数量到660左右时,出现相同的异常。 将Xss修改为512k,问题解决,Linux上问题也解决。
问题圆满解决?
补充点:
JVM栈出现StackOverFlowError的场景:栈调用层级太深,或栈申请不到内存
再起波澜
反馈给运维的同学, Xss需要设置为512k。 运维的同学不同意!!! 原该参数为128k,后来不满足需求调整为256k, 不同意再调大了。建议我们优化代码 ( 内存大小固定的情况下,单个线程占用的内存越大,总线程数量就会越少 )
what can I do ?
- 提示日志级别, 出现问题时是debug,改成error ? 因为debug输出的日志多,会多定义一些局部变量 。 No,不行
- 使用到Long类型的修改为Integer. 栈内存中,Long需要占用2个slot, Integer只需要占用一个。 No,不行
…
没有想到好的头绪,先自己手工调整了服务器上的JVM参数. Xss修改为512k - 拖了几天,有次骑车突然闪现了一个很笨的办法 (当时自得意为灵光一闪)。 既然300+无法查询,内部实现的时候,每100个号查询一次,然后拼装结果?
当时的想法是,300个号码能查询, 每次100个肯定可以查询,然后外层方法调用里面的具体实现,里层方法退出时栈 (栈帧)占用的空间释放掉,则应该不会超过256k的限制
回来后进行验证, 不行, 为什么, - 后来查看druid连接池的具体实现,网上搜索了一些资料。需要先做SQL解析,生成语法树,然后根据目标数据库类型生成对应的SQL。 怀疑由于SQL太复杂导致解析复杂,方法调用太多,大量局部变量 ?
对SQL进行修改,还原成最初的SQL (原来是由于有性能问题,每次in遍历的时候,所有手机号均需做一次加密操作), 该问题不再出现。
后来SQL改写了下,避免了SQL union的问题,但有个新的性能问题:对每个手机号需要做一次解密操作。 新问题尚未想到解决方案(文章后面有后来想到的其他思路)
问题原因
Thread Stack Size大小,问题环境设置为256k,内存申请不到,所以出现栈溢出的异常
阿里的druid框架,做SQL解析(visitor模式), 由于SQL很复杂, 很多SQL做union拼接, SQL解析时构造了很复杂的语法树
解决方案
修改JVM大小
-Xss参数由256k改到512k后,一次查询1000个手机号,不会出现栈溢出异常
##方法优化,降低占用的栈内存
没找到好的方案
SQL优化
同样带来了一个新问题, 每个手机号都需要做一次解密操作。
后来换了个角度想了下,把解密这个,可以先在程序里面做掉。或者反过来对外层数据加密也可以。
JVM StackOverFlowError相关推荐
- 写java线程导致电脑内存不足_如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码...
程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...
- 【JVM】StackOverflowError与OutOfMemoryError
· StackOverflowError与OutOfMemoryError是JVM中常见的有关内存的异常,需结合JVM来: · 在理解.区分这两个异常前,需要知道JVM中运行时数据区的结构:[见图1的 ...
- JVM如何处理StackOverflowError
如何模拟StackOverflowError,可以参考: http://blog.csdn.net/u011983531/article/details/63250882 我们知道,当虚拟机栈容量太小 ...
- JVM——13.定位 StackOverflowError
文章目录 1. Java虚拟机栈和方法调用 2. 什么情况会发生 StackOverflow 3. 模拟 StackOverflowError 4. StackOverflowError 的定位及解决 ...
- JVM中OutOFMemory和StackOverflowError异常代码
1.Out of Memory 异常 右键Run As --->Run Configuration 设置JVM参数 -Xms20m -Xmx20m 上代码: 1 /** 2 * VM Args: ...
- JVM 常见异常及内存诊断
栈内存溢出 栈内存大小设置:-Xss size 默认除了window以外的所有操作系统默认情况大小为 1MB,window 的默认大小依赖于虚拟机内存. 栈帧过多导致栈内存溢出 下述示例代码,由于递归 ...
- 面试高频!JVM必备教程~
请你谈谈你对JVM的理解?Java8虚拟机和之间的变化更新? JVM类加载器是怎么样的?有几种? 什么是OOM,什么是StackOverFlowError? 怎么分析? JVM常用调优参数有哪写? G ...
- Tomcat 调优及 JVM 参数优化
Tomcat 本身与 JVM 优化 Tomcat:调整Server.xml JVM:bat启动服务方式的话修改catalina.bat 服务式启动的话参考:http://www.cnblogs.com ...
- JVM学习--(一)基本原理
前言 JVM一直是java知识里面进阶阶段的重要部分,如果希望在java领域研究的更深入,则JVM则是如论如何也避开不了的话题,本系列试图通过简洁易读的方式,讲解JVM必要的知识点. 运行流程 我们都 ...
最新文章
- android 分辨率合集,Android编程之分辨率处理相关代码段合集
- c语言的语言扩展的数据类型,C语言之数据类型
- (转)Git详解之一:Git起步
- 【IBatis.Net】 各种配置说明
- Azure手把手系列 4:深入了解Azure 一块钱当三块用
- java什么叫元素_什么是Java做什么 - 每个元素使它们成为对象类型?
- datagrid sortname如何定义多列_如何实现一个小说分页的功能
- ubuntu16.04安装opencv3.3
- 街机模拟器 WinKawaks 及街机 ROM 下载
- html表格宽度设置没效果,html表格宽度设置失效
- 普通台式机上Tesla M40显卡paddleGPU深度学习柯南的变身器上机体验
- 服务器域名 一级域名_HTTP简介:域名系统服务器
- C语言约分、求最大公约数?三角形用分数格式输出其较小锐角的正弦值
- 使用Java编写自己的区块链
- 空间相关性----地理探测器--学习记录
- 2022探索电商新模式 乐创播客共启未来
- 记录常用的代码工具(二)--Poi工具类
- 查看计算机网络ip,详细教你电脑ip地址怎么看
- mysql如何打码隐私数据列_MSSQL - 最佳实践 - 如何打码隐私数据列
- 关于完全卸载2345王牌输入法-win10
热门文章
- WPF x:Key标签
- VLookup函数怎么用?详细解析
- 2.2 单片机最小系统
- 免费分享:5本安卓开发经典书籍,Android 7编程入门经典(第4版),Android底层驱动分析和移植,底层驱动分析和移植
- Autofill Framework(自动填写)用法详解
- PID控制算法实践应用(一):PID算法的离散化
- 金立m5android经常自启,金立M5(全网通)一键救砖教程,轻松刷回官方系统
- linux主机路由命令,linux下路由设置详解
- 【2022】超详细的JAVA JDK配置和IDEA安装教程(Windows 版)
- concurrent.futures调研