背景
- 平台上传图片列表压缩文件,大小87M,耗时44s,然后报了500异常。
- 随后查看后端日志,发现报OutOfMemoryError: Java heap space,
但是数据却成功完全添加到mysql中了
,特别奇怪。
分析原因
- 流程:前端解析压缩文件转化为image-base64的json数据 =》传给后端 =》后端解析入mysql。
- 通过分析OOM日志,发现是AOP切面类往
mysql插入操作日志时
产生的OOM异常。 - 但是内存只多了大约100M数据(
图片文件转base64后会变大
),难道就真的差这100M数据就达到极限吗?查看JVM进程信息,堆内存给的1G
,确实有点小,但是这个服务本身就属于配置类的服务,也应该够用了。 - 后面发现同时还存在一个定时任务,每隔10s就要从mysql中取一次图片base64数据,原因应该就是image-base64数据刚插入mysql,随之定时任务就执行了,将刚插入的数据又拿到内存中,此时瞬间占用
将近200M内存
。 - 具体报错位置是JSON.toJSONString()方法,操作日志会保存body体参数,当序列化body体参数时,系统直接崩溃了。
解决
1、堆内存增加到2G。
2、限制body体大小。
- 正常情况改SpringBoot的一个配置(
server.tomcat.max-http-post-size: 100MB
)就行了,但是在SpringBoot 2.4.0
版本之后,该配置就被移除了。 - 只留下
server.tomcat.max-http-form-post-size=2MB
(这个专门限制application/x-www-form-urlencoded表单提交的形式)、server.tomcat.max-swallow-size=2MB
(这个表示当超出限制便不再读取了,避免内存溢出)、spring.servlet.multipart.max-request-size=2MB
(这个专门限制multipart/form-data文件上传),但是此时请求是application/json
类型,所以这几个都不能用。 - 最后只能写一个filter来限制了。
3、json序列化时忽略不必要的参数。
- 像image-base64这类数据根本没必要存,所以直接忽略。
@JSONField(serialize = false, deserialize = false)
,仅作用于FastJSON,并且不影响ORM
框架。- private
transient
String imageBese64,作用于FastJSON/Gson/Jackson/输入输出流等,不影响ORM
框架。