手把手教你定位"/>
OOM别慌,手把手教你定位
本渣渣今早正在写BUG呢,TL丢过来一个问题,说是平台有异常让我康康。一顿操作找到错误日志后,傻眼了OutOfMemoryError,这玩意我也就会写写,也没定位过啊。
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.OutOfMemoryError: Java heap spaceat org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:446)at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:378)at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:475)
Caused by: java.lang.OutOfMemoryError: Java heap space
于是习惯性的打开了百度。再看了若干经验之谈后,准备下手了。步骤如下:
- 先找到服务器下oom错误快照
- 使用工具分析错误原因
- 结合代码定位到异常发生的地方
查找错误快照
不明白heap dump的看这个:
我的是在这里
当然可以通过find命令来查找,一般都在tomcat的目录下
以hprof结尾的就是目标文件了
使用工具分析
MemoryAnalyzer.exe
下载链接:.php(建议科学上网,70M左右)
打开之后,把上一步找到的快照文件导进去。注意可能有坑(一般线上环境的快照文件动辄上G,所以建议把工具的最大内存调大,如下)
MemoryAnalyzer.ini文件,我的文件有4.1G所以调的比较大。
-startup
plugins/org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.700.v20180518-1200
-vmargs
-Xmx5096m
成功导入之后会到欢迎界面,忽略就行。点击分析产生的Leak Suspects,如图:
其中Histogram比较常用:
占用内存排行,对于定位也很有帮助。饼图下方有存在的错误报错,自动生成的,主要是把占用内存高的展示出来了,意思就是你的问题在这里。
先看堆栈信息,找到报错的地方,这里会有具体的报错方法的信息(我没截全):
再看是什么对象导致的,点details
可以看到一共有接近1千万的对象,后面的内存占用也有,那么到这一步,可以去代码里面找了。
查找代码
@Overridepublic List<ResourceResp> queryResources(ResourcesVerifyReq resourcesVerifyReq) {try {ResponseData responseData;long total = 1001;HashMap<String, Object> queryParam = new HashMap<>(8);int pageNo = resourcesVerifyReq.getPageNo();queryParam.put("pageNo", pageNo);int pageSize = resourcesVerifyReq.getPageSize();queryParam.put("pageSize", pageSize);if (pageSize > 0) {responseData = RpcExchange.call(rpcParamDto, queryParam, Constants.HTTP_WEB_PORT);} else {pageSize = 1000;queryParam.put("pageSize", pageSize);while ((long) pageNo * (long) pageSize < total) {responseData = RpcExchange.call(rpcParamDto, queryParam, Constants.HTTP_WEB_PORT);List<ResourceResp> list = data.getList();total = data.getTotal();returnData.addAll(list);}}return returnData;} catch (Exception e) {logger.error("资源查询出错",e);}}
// 相信你们看到这个while循环,心里也咯噔了一下,原来当查询后total大于1000时,这里就进入死循环了,原因竟然是,分页去查的时候,pageNo没进行递增的操作。
修改如下:
while ((long) pageNo * (long) pageSize < total) {responseData = RpcExchange.call(rpcParamDto, queryParam, Constants.HTTP_WEB_PORT);List<ResourceResp> list = data.getList();total = data.getTotal();returnData.addAll(list);// 大于1000时,需要递增queryParam.put("pageNo", ++pageNo);}
总结如下:
// 1,写循环的时候,尽量考虑考虑次数是否明确,终止条件是否确定,小心死循环,小心死循环,小心死循环
// 2,定位OOM时,不用慌,工具其实替我们做了大量的工作,但你要会使用,我只是刚用了下皮毛
// 3,review的时候,多注意可能导致死循环的地方
// 4,OOM的定位,还有别的很强大的工具,需要去多接触
更多推荐
OOM别慌,手把手教你定位
发布评论