循环调用Autodesk API时出现Java堆内存溢出问题
问题复现场景
- 调用Autodesk平台接口
GET {urn}/metadata/{guid}/properties时,首次请求只会返回结构为{"result":"success"}的JSON响应,代表后台属性计算任务已启动 - 重复调用该接口,待后台任务处理完成后,接口会返回基于64MB大小对象树生成的全量扁平化对象属性列表
- 原有轮询校验逻辑实现代码如下:
boolean hasResult = false; boolean hasData = false; String result; do { result = 执行接口调用获取响应内容; JSONObject jo = new JSONObject(result); hasResult = jo.has("result"); hasData = jo.has("data"); if (hasData) { break; } } while (hasResult);
- 运行上述代码最终触发
java heap space堆内存溢出错误。
故障根因
- 轮询逻辑存在缺陷:代码没有设置轮询间隔、最大轮询次数/超时时间,会以最高频率向接口发请求。如果后台任务处理耗时较长,循环会持续运行,不断创建新的响应字符串、JSONObject对象,虽然YoungGC会回收部分无引用对象,但高频请求下内存分配速度远快于回收速度,最终耗尽堆空间。
- 内存加载方式不合理:64MB源对象树经过扁平化处理后,生成的JSON响应体积会膨胀到原大小的3~5倍,直接将完整响应读入
String再构造全量JSONObject的方式,会在堆中生成多份大体积对象副本,内存占用远超响应本身大小,很容易触发堆内存溢出。 - 缺少异常分支判断:代码没有处理接口返回错误、任务失败的场景,遇到异常响应时可能进入死循环,持续无效消耗内存。
修复方案
- 补全轮询控制逻辑:轮询请求添加2~5秒的间隔,同时设置最大轮询时长(比如最长等待5分钟),达到阈值后直接终止轮询抛出异常,避免无限循环。
- 优化大响应解析逻辑:不要将全量响应一次性读入内存,改用流式JSON解析工具逐段读取、处理属性数据,内存占用可以降低到原方案的1%以下。
- 启用传输压缩:请求时添加
Accept-Encoding: gzip头,接口返回的压缩后响应体积仅为原大小的10%~20%,能大幅降低传输过程和内存解析的开销。 - 按需调整JVM内存参数:如果业务场景确实需要加载全量属性数据,根据预估的数据量调大JVM堆内存配置,比如添加启动参数
-Xms2g -Xmx4g预留足够的内存空间。 - 增加响应状态校验:除了判断
result/data字段是否存在,还要校验接口返回的状态码、任务状态字段,遇到任务失败、权限不足等异常响应时直接终止循环,避免无效请求。
内容的提问来源于stack exchange,提问作者Pradeep Virtuele
相关产品推荐
相关产品推荐

