上传图片重新调整大小导致内存溢出oom
目录
- 压测上传人员图片接口
- 一、了解案发现场情况:
- 二、分析原因
- 三、导出oom分析
- 1. System 时间大于 User 时间
- old区回收情况
- 四、解决方案
- 五、后续
最近准备优化之前老代码的 openapi 对外接口性能,进行了一轮摸底测试,发现了一系列问题
压测上传人员图片接口
一、了解案发现场情况:
- 并发1 ,压测10分钟,正常
- 并发10,压测10分钟,正常
- 并发20 ,压测10分钟,内存占用增大,qps下降,但是无oom,无失败, 接口平均响应时长增大
- 并发30 ,测试10分钟,服务开始变慢,压测失败数量增高,线程不断oom报错,最后导致服务崩溃
二、分析原因
分析请求链路
- 压测工具-> openapI ms -> middle-base ms -> file server ,很明显base微服务出问题
查看base服务日志,发现大量的上传图片请求处理,后期每一个请求报一个oom,最后服务崩溃
- 怀疑内存溢出导致,
复现压测场景
使用arthas 工具, dashboard ,重新压力30测试 , 发现处理大约30个左右线程,大部分处于wait状态,运行时间2min 十几个,本来一个上传图片的请求 不该执行这么长时间
使用 thread -3 ,查看最繁忙的线程做什么,发现大多数阻塞在一个resize方法,改方法会重新调整图片分辨率大小,压测使用1M 和4M图片 ,但是没有规定分辨率,业务后台会自动把图片调整到指定的分表率,大概率这个方法出问题了
接下来分为2步,
第一步,导出oom日志,使用mat工具分析,发现奇奇整整30个线程,查看占用内存最大,发现每个线程自身存在一个大的字段存储图片4M ,使用G1 ,一共1g内存,, 会直接分配到大对象中,
本地复现,把这段上传图片代码复制到maim方法重新写一遍,读取本地文件,sleep线程,启动后使用jvisivmg 图形化工具查看内存占用,发现执行2次resize 后, 内存增加60M ,同时30个并发,内存飙升600M,不断触发minjor 和mixed gc
public static byte[] fullHandler(File file) throws IOException {ImageInfo image = ImageUtil.resize(file, 480, 0);//第一次调用方法List<AnalyzeResp> analyzeRespList = FoliageUtil.analyzeMulti(image.getImage());if(CollectionUtils.isEmpty(analyzeRespList)){log.info("no face found");return null;}AnalyzeResp analyzeResp = analyzeRespList.get(0);int x1 = analyzeResp.getFace_info().getRect().getLeft();// int y1 = image.getHeight() - analyzeResp.getFace_info().getRect().getTop();int y1 = analyzeResp.getFace_info().getRect().getTop();int x0 = (int) (x1 * ((double) image.getOrgWidth() / (double) image.getWidth()));int y0 = (int) (y1 * ((double) image.getOrgHeight() / image.getHeight()));int faceHeight =analyzeResp.getFace_info().getRect().getBottom() - analyzeResp.getFace_info().getRect().getTop();int faceWidth =analyzeResp.getFace_info().getRect().getRight() - analyzeResp.getFace_info().getRect().getLeft();int faceTrueHeight = (int) (faceHeight * ((double) image.getOrgHeight() / image.getHeight()));int faceTrueWidth = (int) (faceWidth * ((double) image.getOrgWidth() / image.getWidth()));//boolean flag = true;int x00 = (int) (x0 - (faceTrueWidth / 2d));if (x00 < 0) {flag = false;}int faceTrueWidthExtend = faceTrueWidth * 2;if (flag && (x00 + faceTrueWidthExtend > image.getOrgWidth())) {flag = false;}int y00 = (int) (y0 - (faceTrueHeight * 0.7));if (flag && y00 < 0) {flag = false;}int faceTrueHeightExtend = faceTrueHeight * 2;if (flag && y00 + faceTrueHeightExtend > image.getOrgHeight()) {flag = false;}byte[] ret = null;if (flag) {ret = ImageUtil.getBytesByCmpImageSub(file, x00, y00, faceTrueWidthExtend, faceTrueHeightExtend);}if (ret == null) {ret = image.getImage();}ImageInfo result = ImageUtil.resize(ret, 800, 200);// 第二次if (result == null) {return ret;}return result.getImage(); }
public static ImageInfo resize(byte[] image, int objectLength, int minlength) throws IOException {ImageInfo imageInfo = new ImageInfo();InputStream byteArrayInputStream = new ByteArrayInputStream(image);BufferedImage sourceImg = ImageIO.read(byteArrayInputStream);try {int minLength = sourceImg.getHeight() > sourceImg.getWidth() ? sourceImg.getWidth() : sourceImg.getHeight();if (minLength < minlength) {return null;}imageInfo.setOrgHeight(sourceImg.getHeight());imageInfo.setOrgWidth(sourceImg.getWidth());if (minLength < objectLength) {imageInfo.setImage(image);imageInfo.setHeight(sourceImg.getHeight());imageInfo.setWidth(sourceImg.getWidth());return imageInfo;}// 高度不符合标准的进行等比压缩try {double height, width;ByteArrayOutputStream bos = new ByteArrayOutputStream();if (sourceImg.getHeight() > sourceImg.getWidth()) {width = objectLength;height = (double) sourceImg.getHeight() * ((double) objectLength / (double) sourceImg.getWidth());} else {width = (double) sourceImg.getWidth() * ((double) objectLength / (double) sourceImg.getHeight());height = objectLength;}Thumbnails.of(new ByteArrayInputStream(image)).size((int) width, (int) height).toOutputStream(bos);imageInfo.setImage(bos.toByteArray());imageInfo.setHeight((int) height);imageInfo.setWidth((int) width);return imageInfo;} catch (Throwable e) {e.printStackTrace();return null;}} catch (Exception e) {e.printStackTrace();return imageInfo;} }
总结:目前看来,压测会不断并发30个,应该就是改方法导致,重新resize4m 图片,查询google,会把图片摊开,再押错,摊开后,内在占用会增大,
三、导出oom分析
图片、堆栈日志、分析报告 :链接: https://pan.baidu.com/s/1NVrPtcYwuTPZEq3l2_vg-w 提取码: mbil
https://gceasy.ycrash.cn/my-gc-report.jsp?p=c2hhcmVkLzIwMjIvMDEvMTcvLS1nYy5sb2ctLTgtNS00NA==&channel=WEB
使用垃圾回收器 G1 ,内存参数设置
CMD java -Xms1G -Xmx1G -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30 -XX:+DisableExplicitGC -XX:TargetSurvivorRatio=90 -XX:G1NewSizePercent=25 -XX:G1MaxNewSizePercent=40 -XX:G1MixedGCLiveThresholdPercent=35 -XX:+AlwaysPreTouch -XX:+ParallelRefProcEnabled -jar micro-server-base-1.0.6.2-SNAPSHOT-exec.jar
1. System 时间大于 User 时间
刚开始压测,正常运行,随着请求处理增加,每个请求需要60m空间,一共1G ,其中图片4M, 会直接分配到大对象, 随着内存增加,先进行mijor gc ,然后mixed gc(full gc) ,刚开始可以正常回收,但是后来system占用时间越来愈多,
- 刚gc完毕,大量线程过来,导致系统不断gc,中间偶尔用户线程执行完,一个线程过来申请内存,直接移除,接下来一个接一个,
- 内在老年代里因为新时代存在引用不能及时回收,新生代不断有对象进入,又gc死亡,导致mixgc回收不过来,最终导致内存不断溢出,直到崩溃
old区回收情况
- 开始时,还可以注册回收
- 最后,回收不掉
这样下去,肯定会溢出 ,但是为什么回收不掉呢? 是内存泄漏了吗,报告没有,具体待分析
初步定位30个线程,每个持有resize后大对象,根据线程执行进度不同,有道resize()变大60m,有的还没有走到resize(),总共占用950M,通过oom堆快照可以看出每个线程内存不同,进度不同, 而且oom日志,可以看到线程栈出错位置也不一样
四、解决方案
- 最快的方案 ,对openapi 进行限流处理 ,测试最新并发量 ,进行限流 ,如果项目现场需要速度,可以临时增大内存
- 后续,可以考虑优化resize()方法,寻找占用内存小的 调整图片分别率的方法,或者找c++团队重新写一个,我们调用
- 限制上传分辨率和大小,其实是 just so so
- 这里没有考虑异步,因为业务场景必须保证图片上传完毕立即响应,否则可以,先提交mq,慢慢限流消费处理,在返回用户,其实效果一样,无非限流位置不同
五、后续
后来使用一个50m ,分辨率超高的地图图片,调用上传文件接口,一个请求直接打穿了服务,工具分享发现,内存中该图片resize()超过了1G,
上传图片重新调整大小导致内存溢出oom相关推荐
- Java常见内存溢出(OOM)解决方案
Java 常见内存溢出(OOM)解决方案 一,jvm内存区域 1, 程序计数器 一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器. 2, java栈 与 ...
- Spark 中 JVM 内存使用及配置详情、spark报错与调优、Spark内存溢出OOM异常
一般在我们开发spark程序的时候,从代码开发到上线以及后期的维护中,在整个过程中都需要涉及到调优的问题,即一开始需要考虑如何把代码写的更简洁高效调优(即代码优化),待开发测试完成后,提交任务时综合考 ...
- JVM--调优--04--案例02--大SQL导致内存溢出
JVM–调优–04–案例02–大SQL导致内存溢出 1.现象 项目启动,且没有人使用,一段时间后就报redis连接异常. 1.1.日志信息 org.redisson.client.RedisTimeo ...
- 内存溢出(oom)和内存泄漏(leak)
一.概念介绍: 1.内存溢出 out of memory: 是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory:比如申请了一个integer,但给它存了long才能存下的 ...
- 添加IFrame导致内存溢出的解决过程(IE浏览器,目前发现了原因,还未解决)...
1. 现象 每次动态添加iframe时,iexplore.exe进程占据的内存都会增加(大概10M左右),不会自动释放,最终导致内存溢出 2. 解决过程 经过网络的一番搜索,基本上给出的解决方案是 ...
- 本地项目antd 修改.less文件导致内存溢出
项目场景: antd 项目,修改less文件会导致内存溢出 问题描述 本地环境antd 项目,修改less文件会导致内存溢出:如下 FATAL ERROR: Reached heap limit Al ...
- php查询mysql返回大量数据结果集导致内存溢出的解决方法
web开发中如果遇到php查询mysql返回大量数据导致内存溢出.或者内存不够用的情况那就需要看下MySQL C API的关联,那么究竟是什么导致php查询mysql返回大量数据时内存不够用情况? 答 ...
- android 内存溢出检查,Android Handler使用导致内存溢出附带LeakCanary排查
最近项目马上结束了在对项目进行检查有没有内存泄漏的地方,这里使用的是leakcanary. 使用步骤很简单: 添加依赖: dependencies { debugCompile 'com.square ...
- 图片--Android加载图片导致内存溢出(Out of Memory异常)
Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证) ...
最新文章
- Swift中的问号?和感叹号!
- 两院院士评选2020年中国、世界十大科技进展揭晓,「机器学习模拟上亿原子」等入选 | AI日报...
- ECshop 数据库表结构
- 安装oracle 12c 还用装11g_oracle12c-RAC安装部署之GRID安装
- Debug javascript inside jsp page 调试jsp嵌入的js
- 使用 IntraWeb (13) - 基本控件之 TIWLabel、TIWLink、TIWURL、TIWURLWindow
- Android的activity的生命周期
- where is field waerk in pricing structure filled
- 【精华】详解Qt中的内存管理机制
- android通知栏内添加快捷键_Android实现向Launcher添加快捷方式的方法
- 互联网秒杀业务架构设计
- java aws批量_通过 S3 Batch Operations (批量操作)功能轻松处理海量对象
- python3.7帮助文档,文档说明 · Python3.7.3官方文档 简体中文 · 看云
- linux笔记本风扇调节,Linux下笔记本的风扇控制问题
- HTML网页设计制作大作业(div+css) ~ 中华美德6页面带音乐 ~ 学生网页设计作业源码(中华文化)
- ZYNQ7000 学习(二十五)纯PL 实现独立显存VGA驱动并显示画面
- MATLAB 中的 mod() 函数
- 从零开始搭建免费小程序商城
- UI设计中按钮如何设计,常见的按钮设计类型
- android connection refused 模拟器,Docker:Docker Appium 镜像连接夜安卓神模拟器,并处理 connection refused 异常...
热门文章
- 直播大战终极猜想:会上演滴滴快的式结局吗?
- 钉钉自定义Outgoing机器人开发
- Android:Ethernet:实现RJ45有线网和USB host无线网卡的静态IP(StaticIpConfiguration)设置上网(附源码)
- 【细节很多的dp】Educational Codeforces Round 133 (Rated for Div. 2) C. Robot in a Hallway
- 光流定点若干问题分析
- 一文了解异步编程基础
- Python ElementTree 解析 xml文件
- mt管理器修改迷你世界服务器,用支付宝支付的游戏,怎么改代码,游戏迷你世界...
- ubuntu安装react-native 必须安装watchman
- 递归树: 如何借助树来求解递归算法的时间复杂度