# 内存溢出问题排查与解决指南
当遇到内存溢出(OutOfMemoryError)或内存压力过大问题,请根据您看到的具体问题,直接查阅下方对应的章节,按照步骤操作即可。
# 问题一:应用运行一段时间后内存飙升,最终溢出
现象:
- AFCenter或其他EOS应用在运行一段时间后,CPU或内存使用率异常飙升,页面卡顿或无法访问,最终可能因内存溢出而宕机。
- 错误日志中包含
java.lang.OutOfMemoryError: Java heap space。
根本原因:
- 查询未带分页条件
- 大数据量Excle导出
- JVM或线程池配置不合理,无法处理高并发请求。
排查及解决方案:
1、 增加日志
为方便定位内存溢出、响应慢等性能问题,请在logback-spring.xml增加如下配置,便于在eos-sys.log打印出查询记录数超过2000和执行时间超过10s的sql。
<logger name="log.sys.sql" additivity="false" level="INFO"> <appender-ref ref="EOS_SYS_LOG_APPENDER_FILE"/> </logger>![系统日志配置]()
2、 增强分页查询规范:
所有列表查询接口必须使用分页,建议每次查询不超过10条。
若使用低开视图列表,注意在视图设置-表格设置-分页,设置分页条件。
![视图分页配置]()
禁止无限制的queryAll类方法调用。
3、 导出数据量限制:
- 不要全量导出,建议分批导出,单次导出数据量不超过1W条。
4、 优化JVM及线程池配置:
# 问题二:导出视图数据时内存溢出(Java heap space / NegativeArraySizeException)
现象:
- 在低开视图中,点击“导出”按钮(尤其是导出“查询的全部数据”时)失败。
- 导出的Excel文件为0KB。
- 后台日志报错:
Message: Error exporting'导出视图失败!Cause:Java heap space'。 - 在达梦(DM)数据库环境下,可能报错:
java.lang.NegativeArraySizeException。
根本原因: 视图导出的数据量过大(如数万条记录、数十个字段),后端一次性将所有数据加载到内存中生成Excel,导致JVM堆内存溢出。
NegativeArraySizeException通常也是内存分配失败的另一种表现。解决方案与操作步骤:
方案A:优化导出策略(推荐优先尝试) 1、 培训用户: 建议业务用户在导出大量数据时,优先使用“导出当前页”功能,而不是“导出查询的全部数据”。 2、 添加筛选条件: 指导用户在导出前,通过视图上方的筛选条件,尽可能缩小数据范围,分批导出。
方案B:调整JVM内存大小 1、 定位应用: 找到提供该视图导出功能的后端应用(通常是AFCenter或具体的业务应用)。 2、 修改内存: 按照问题一方案的步骤,修改对应应用的JVM内存参数(
-Xms,-Xmx),然后重启应用。方案C:自定义导出实现(最终解决方案) 1、 分析需求: 如果业务上必须支持导出全量大数据,且调整内存后仍无法解决,需要考虑自定义开发。 2、 开发异步导出功能: * 思路: 创建一个异步导出任务。前端点击导出后,后端立即返回“任务已提交”,避免前端页面超时。 * 步骤: 1. 后端创建一个定时任务或线程池来处理导出请求。 2. 处理时,使用分页查询的方式,分批从数据库读取数据,并分批写入Excel文件(可使用EasyExcel等支持分批写入的库)。 3. 导出完成后,将生成的Excel文件保存到服务器或文件服务器,并通过站内信、邮件等方式通知用户下载。 3、 评估工作量: 此方案需要项目组投入开发资源进行实现。
方案D:调整Nginx超时时间 1、 分析问题: 如果导出时后台没有明显的内存报错,但前端返回504 Gateway Time-out,这通常是Nginx超时。 2、 修改Nginx配置: 按照主文档问题二方案B的步骤,增加Nginx的
proxy_read_timeout配置,为大数据导出提供更长的处理时间。
# 问题三:导出基线包时内存溢出(Java heap space)
现象:
- 在开发中心导出基线包时,操作失败。
- 后台日志报错:
Caused by: java.lang.OutOfMemoryError: Java heap space。 - 前端长时间无响应,最终提示失败。
根本原因: 基线包中包含大量构件包资源,导出时JVM需要将相关数据加载到内存中进行打包。如果分配的堆内存不足,或者基线包内容本身过大(如超过数据库
max_allowed_packet限制),就会导致内存溢出。解决方案与操作步骤:
方案A:调整JVM内存大小(最常用)
1、 定位应用: 找到正在执行导出操作的AFCenter应用(因为导出功能由AFCenter后端提供)。
2、 找到启动脚本: 进入AFCenter应用的安装目录,找到启动脚本
startup.sh(Linux) 或startup.bat(Windows)。3、 修改内存参数: 用文本编辑器打开脚本,找到设置JVM内存的参数行(通常包含
-Xms和-Xmx)。将最大堆内存调大,例如从-Xmx2048m修改为-Xmx4096m或更高(取决于服务器可用内存)。bash # 修改前 JAVA_OPTS="-Xms1024m -Xmx2048m ..." # 修改后 JAVA_OPTS="-Xms2048m -Xmx4096m ..."4、 重启应用: 保存脚本,重启AFCenter服务使配置生效。
5、 重新导出: 再次尝试创建并导出基线包。
方案B:调整数据库max_allowed_packet限制(针对MySQL)
1、 检查错误日志: 确认在内存溢出之前,是否还有类似
Packet for query is too large或max_allowed_packet相关的报错。2、 临时修改数据库配置: 登录MySQL数据库,执行以下SQL命令,临时增大允许的数据包大小:
sql -- 查看当前值 SHOW VARIABLES LIKE 'max_allowed_packet'; -- 临时修改为256M (重启MySQL后失效) SET GLOBAL max_allowed_packet = 256 * 1024 * 1024;3、 永久修改配置: 若要永久生效,需要修改MySQL配置文件(
my.cnf或my.ini)。在[mysqld]部分添加或修改:ini [mysqld] max_allowed_packet = 256M保存后重启MySQL服务。4、 重试导出: 再次尝试导出基线。
# 问题四:附件上传导致内存溢出
现象:
- 上传大附件(例如300M文件)时失败,报错或内存溢出。
- 服务器日志显示内存溢出,可能与附件上传的日志记录有关。
根本原因:
- 附件大小超过了Tomcat、Spring Boot或Nginx的默认上传限制。
- 附件上传接口的请求/响应日志过大,占用了大量内存。
解决方案与操作步骤:
方案A:修改上传大小限制
1、 后端配置 (AFCenter/业务应用): 在应用的
application.properties文件中,添加或修改以下配置:properties spring.servlet.multipart.max-file-size=500MB spring.servlet.multipart.max-request-size=500MB2、 数据库配置 (MySQL): 如果使用MySQL数据库,也需要调整其
max_allowed_packet参数。3、 Nginx配置: 在Nginx配置文件的
location块中,增加client_max_body_size配置:nginx client_max_body_size 500m;4、 重启服务: 依次重启Nginx、后端应用。
方案B:排除附件上传接口的日志记录
1、 定位配置文件: 找到出现内存溢出问题的应用的
application-afc.properties或application.properties文件。2、 添加排除配置: 在配置文件中添加以下配置,排除附件上传相关方法的日志记录。这可以防止大文件的日志信息消耗过多内存。
properties eos.log-control.method.excludes=com.yourcompany.YourUploadService.uploadMethod,com.primeton.gocom.afcenter.common.controller.AttachmentController.uploadAttachments注意: 需要将com.yourcompany.YourUploadService.uploadMethod替换为您实际的上传服务类和方法名。示例中已包含了产品自带的附件上传Controller。如果配置原本有值,用逗号将新值拼接上去。
# dump日志获取及分析工具
如遇到未覆盖的问题,请获取并分析dump,若问题紧急,请获取dump后第一时间联系普元售后。
dump日志获取
1、自动获取(推荐生产环境)
EOS应用启动脚本中已经配置了 set JAVA_OPTS=%JAVA_OPTS% -XX:+HeapDumpOnOutOfMemoryError,所以当发生OOM时,会自动在JVM进程的当前目录生成堆转储文件。
例如:server\afcenter\bin\java_pid12345.hprof(PID为12345的进程)
2、手动获取(问题复现时使用)
方法1:jmap命令(推荐)
使用JDK自带的 jmap 命令手动生成堆转储文件,这是最常用、最稳定的方式。
命令格式:
# 1. 找到Java进程PID
jps -l | grep -E 'AFCENTER|BPS|应用名'
# 2. 生成heap dump(不暂停应用)
jmap -dump:live,format=b,file=/tmp/heap_$(date +%Y%m%d_%H%M%S).hprof <PID>
参数说明:
| 参数 | 说明 | 示例 |
|---|---|---|
-dump | 生成堆转储 | 必选 |
live | 只dump存活对象(推荐),会先触发Full GC | -dump:live |
format=b | 二进制格式,MAT等工具只能识别此格式 | format=b |
file | 输出文件路径,支持绝对路径和相对路径 | file=/tmp/heap.hprof |
<PID> | Java进程ID | 12345 |
dump分析工具
| 工具名称 | 下载地址 | 核心功能 |
|---|---|---|
| Eclipse MAT | https://eclipse.dev/mat/ | 堆内存分析,内存泄漏检测 |
| GCeasy | https://gceasy.io/ | GC日志在线分析,报表生成 |
# 应急操作命令速查表
| 场景 | 命令 | 预期结果 |
|---|---|---|
| 查看进程PID | jps -l \| grep -E 'AFCENTER\|BPS\|应用名' | 输出Java进程ID |
| 快速内存状态 | jstat -gcutil <PID> 2000 5 | 显示GC和内存使用百分比 |
| 生成堆dump | jmap -dump:live,format=b,file=/tmp/heap.hprof <PID> | 生成dump文件 |
| 生成线程dump | jstack <PID> > /tmp/thread.log | 生成线程堆栈 |
| 查看GC历史 | grep "Full GC" /path/to/gc.log \| tail -20 | 显示最近20次Full GC |
| 重启服务 | cd /app/bin && ./stop.sh && ./startup.sh && tail -f ../logs/app.log | 服务重启并查看日志 |

