一起SQL引发OOM的解决思路与过程(转载)
在TOMCAT WEB程序的运行过程中,突然触发了内存溢出错误,检查Tomcat的localhost日志,找到如下信息:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:242)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
at java.util.ArrayList.add(ArrayList.java:440)
at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3442)
……
at com.yiifaa.indicator.impl.HoleIndicatorServiceImpl.expireHoles(HoleIndicatorServiceImpl.java:191)
at com.yiifaa.indicator.controller.HoleIndicatorController.expireHoles(HoleIndicatorController.java:69)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
按照提示,找到代码进行检阅:
Map<String, Object> params = Maps.newHashMap();
String sql = sqlCache.get("expireHoles");
// 这里就是191行
List<Map<String, Object>> results = this.jdbcTemplate.queryForList(sql, params);
- 1
- 2
- 3
- 4
看了好几遍,没有发现明显的异常,既没有全局变量,也没有属性变量,没理由出现内存溢出,更重要的是,按照以往的经验,SQL应该没有这么大的爆炸力,而且难以确定的是,到底是该方法引发了OOM异常,还是碰巧就遇到了内存的瓶颈,从而导致内存异常,所以需要进一步判断。
刷新页面,调用应用程序接口,同时使用jstat监测JVM内存使用,如下:
# 首先获取程序ID
jps -l
# 然后根据程序ID获取其JVM内存使用信息
# 10代表时间间隔,毫秒
jstat -gcutil 47279 10
- 1
- 2
- 3
- 4
- 5
执行jstat命令后,发现如表1的现象:
| S0 | S1 | E(年轻代占比) | O(老年代占比) | P(永久代占比) |
|---|---|---|---|---|
| 不变 | 持续增长 | 持续增长 | 持续增长 | 不变 |
增长的顺序依次是E(年轻代占比)增长到100%,然后S1增长到100%,然后O(老年代占比)增长到100%,最后触发full gc(全局垃圾回收),并且在内存变化的这段时间,并没有进行任何操作,连接数量也在比较小的范围。
# 统计HTTP连接的活动线程
jstack 47279|grep http-bio-8080-exec|grep runnable|wc -l
# 统计HTTP连接数量
netstat -nat|grep -i '8080'|wc -l
- 1
- 2
- 3
- 4
上述两个命令相互论证,证明HTTP连接是正常的,继续用jstack进行诊断,发现很多HTTP线程处于“WAITED”状态,这说明都是处于连接池中,等待被激活,依旧正常。
继续推测,内存的增长必然伴随对象数量的增加,于是采用jmap进行诊断,如下:
jmap -histo 47279|less
- 1
持续多次运行此命令,发现有个对象数量以及增长速度都不正常,如下:
1716115 41187603 com.mysql.jdbc.ByteArrayRow
- 1
难道真是数据库连接的问题?突然想到,消耗内存较多的代码一定是运行中的线程,于是用jstack获取到的信息依次诊断线程,发现真有个数据库线程不正常,如下:
http-bio-8080-exec-89" daemon prio=10 tid=0x00007f039c09f800 nid=0xbcd1 runnable [0x00007f05350cc000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)
at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)
at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
……
at com.yiifaa.sec.indicator.impl.HoleIndicatorServiceImpl.expireHoles(HoleIndicatorServiceImpl.java:191)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
发现程序依旧卡在HoleIndicatorServiceImpl类的191行,最后诊断SQL语句,发现少了一个括号,导致or语句连接数量增长极快,从而是数据量达到了10的22次方,最后引发了如此大的执行灾难。
继续细化分析下去,因为返回结果是个List容器,所以随着游标的滑动,导致数据集不断增长,从而使得“com.mysql.jdbc.ByteArrayRow”对象数量暴增,最后导致内存溢出,结合内存的增长趋势,可以明显看出,当方法内部的对象增大到一定程度时,将会直接进入幸存代(S1),甚至是老年代。
总结
从内存的增加现象看:
1. 只要对象足够大,完全有可能直接进入老年代,即使实在局部方法内;
2. 局部方法也可能会导致内存溢出;
从诊断过程看,相关工具的作用如下:
1. 用jps获取jvm进程ID;
2. 用jstat确认GC内存消耗速度;
3. 用jmap确认对象的增长速度与耗费空间;
4. 用jstack确认错误代码的位置。
从SQL的教训看:
1. 在多表连接时,请仔细检查连接语句;
2. 万无一失的做法,请限定抓取数据的数量,例如100条,如“limit 0, 100”。
原文地址i:https://blog.csdn.net/yiifaa/article/details/78648358
一起SQL引发OOM的解决思路与过程(转载)的更多相关文章
- 【OOM】解决思路
一.什么是OOM? OOM就是outOfMemory,内存溢出!可能是每一个java人员都能遇到的问题!原因是堆中有太多的存活对象(GC-ROOT可达),占满了堆空间. 二.怎么解决? 1.拿到内存溢 ...
- MySQL在并发场景下的问题及解决思路
目录 1.背景 2.表锁导致的慢查询的问题 3.线上修改表结构有哪些风险? 4.一个死锁问题的分析 5.锁等待问题的分析 6.小结 1.背景 对于数据库系统来说在多用户并发条件下提高并发性的同时又要保 ...
- 大数据小视角5:探究SSD写放大的成因与解决思路
笔者目前开发运维的存储系统的服务器都跑在SSD之上,目前单机服务器最大的SSD容量有4T之多.(公司好有钱,以前在实验室都只有机械硬盘用的~~)但SSD本身的特性与机械硬盘差距较大,虽然说在性能上有诸 ...
- java spring 等启动项目时的异常 或 程序异常的解决思路
今天搭建ssm项目的时候,因为pagehelper的一个jar包没有导入idea的web项目下的lib目录中,异常报错找不到pagehelper,这个问题在出异常的时候疯狂crash,让人心情十分不舒 ...
- DG日志不应用,GAP,主备切换解决思路与办法
环境ORACLE 10G OS WINDOWS 对于DG故障解决思路,DG日志切换不进行应用,DG出现GAP解决方法,DG主备库切换, 当DG出现故障时,第一时间检测alert日志,服务器OS日志,网 ...
- ADO.NET 使用DELETE语句批量删除操作,提示超时,删除失败,几种优化解决思路
起因是如此简单的一句sql 提示:Timeout 时间已到.在操作完成之前超时时间已过或服务器未响应. 提供几种解决思路: 1.检查WHERE条件中字段是否已建索引 2.检查是否被其他表引用,引用表外 ...
- C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路
C#不用union,而是有更好的方式实现 用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...
- java高并发解决思路
一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构.性能的要求都很简单,随着互联网业务的不断丰富,网站 ...
- ArcMap基于Oracle出现sde.instances_util.check_instance_table_conflicts:: ORA-00942:表或视图不存在/table or view doesnot exist解决思路
SDE环境:Oracle12C+ArcMap10.7+WinServer2012 出现问题情况: 1.SDE可以连接正常打开,但就是无法新建要素.导入要素等: 1)在根目录新建或导入要素,弹出提示: ...
随机推荐
- PHPexcel的用法
由于经常要统计学生的考试成绩,就研究了下PHPexcel这个插件 顺便说一下,读取方法只针对xls文件. 如果报错,可以先生存一个xls文件,把需要读取的xls内容复制进去. <?php //读 ...
- #7 //[CQOI2014]和谐矩阵
题解: bitset优化高斯消元 无关变量为1 #include <bits/stdc++.h> using namespace std; #define eps 1e-9 #define ...
- Codeforces 269C Flawed Flow (看题解)
我好菜啊啊啊.. 循环以下操作 1.从队列中取出一个顶点, 把哪些没有用过的边全部用当前方向. 2.看有没有点的入度和 == 出度和, 如果有将当前的点加入队列. 现在有一个问题就是, 有没有可能队列 ...
- 统计难题 HDU1251
简单方法: #include<bits/stdc++.h> using namespace std; int main() { ]; map<string,int>ma; ) ...
- 日常推荐大神操作,如何再oracle中delete数据后恢复
http://blog.csdn.net/wangdinghai365/article/details/8593869
- 2019最新最全HUSTOJ本地及云端服务器搭建(基于腾讯云服务器)
在刚接触ACM的时候,对于那些在线测评的网站很感兴趣,就在网上搜索了一下,在Github上发现了一个有趣的项目,然后在 Github 上获取 了HUST OJ 的开源项目代码,根据网上的教程踩了无数的 ...
- Object类浅析
Object类的方法有: hashCode(): 返回该对象的哈希码值 hashCode 的常规协定是: 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回 ...
- 数据恢复工具PhotoRec
数据恢复工具PhotoRec PhotoRec是一款文件恢复工具.它可以从硬盘.光驱.记忆卡中恢复视频.文档.压缩包等文件.该工具绕开文件系统,采用文件特征码机制,直接进行底层数据扫描,尝试恢复文件. ...
- BZOJ.3620.似乎在梦中见过的样子(KMP)
题目链接 /* 896kb 6816ms A+B+A是KMP的形式,于是固定左端点,对于每个位置i,若fail[i]所能到的点k中(k=fail[fail[fail[...]]]),有满足len(l~ ...
- 潭州课堂25班:Ph201805201 第二课:数据类型和序列类型 (课堂笔记)
workon py3env pip install ipython 安装虚拟环境, 安装完成之后,键入:ipython 进入环境, 数字类型:(整型) int --------> ...