REST API获取JSON转Java对象时多次运行触发StackOverflowError求助
问题描述
我正在尝试将通过REST API接收的JSON文件转换为Java对象列表。初始运行时一切正常,但多次运行后会触发StackOverflowError。若不调用该转换方法,程序运行完全正常。目前我无法定位问题原因,恳请提供解决方案。报错信息如下:
Exception in thread "main" java.lang.StackOverflowError at org.json.JSONTokener.nextClean(JSONTokener.java:292) at org.json.JSONTokener.nextValue(JSONTokener.java:422) at org.json.JSONObject.
(JSON...
可能的原因与解决方案
结合你遇到的「初始正常、多次调用后栈溢出」的特殊情况,我整理了几个最可能的排查方向和解决办法:
检查Java对象的循环引用
如果你是自己写解析逻辑或者用了简单的映射工具,先确认你的实体类之间有没有循环引用(比如User类里有Order对象,Order类又反过来持有User引用)。org.json在处理这种循环映射时,多次调用会让递归深度不断累积,最终触发栈溢出。
解决办法:给循环引用的字段添加忽略注解(比如用Jackson的话是@JsonIgnore,自定义解析就手动跳过该字段),或者调整对象结构打破循环依赖。不要复用JSON解析实例
很多开发者会图方便复用JSONTokener、JSONObject这类实例,但org.json的这些类内部维护了解析状态,多次复用会导致内部指针混乱,反复解析时递归调用的栈深度越来越深。
解决办法:每次处理REST返回的JSON字符串时,都重新创建解析实例,比如:// 正确示例:每次转换都新建解析实例 public List<MyEntity> parseJsonToList(String jsonResponse) { // 每次都新建JSONTokener,不要复用 JSONTokener tokener = new JSONTokener(jsonResponse); JSONArray jsonArray = new JSONArray(tokener); List<MyEntity> resultList = new ArrayList<>(); for (int i = 0; i < jsonArray.length(); i++) { JSONObject obj = jsonArray.getJSONObject(i); // 映射到实体类的逻辑... resultList.add(mapToEntity(obj)); } return resultList; }检查REST返回的JSON是否有异常结构
初始运行正常但后续报错,有可能是某次请求返回的JSON出现了极深的嵌套结构(比如十几层甚至几十层的嵌套对象/数组)。org.json解析这种结构时,递归调用的栈会超过JVM的栈容量限制。
解决办法:打印每次请求的JSON内容,排查是否存在异常的深嵌套。如果是,把递归解析改成迭代式解析,或者在JVM启动参数里适当调大栈容量(比如-Xss2m,但这只是临时方案,最好还是优化解析逻辑)。排查内存泄漏问题
多次调用转换方法后,如果有大量解析后的对象被静态集合或者全局变量持有不释放,会导致内存占用过高,间接压缩栈空间引发栈溢出。
解决办法:用VisualVM或者JProfiler这类工具分析内存使用情况,检查转换方法是否有内存泄漏,比如静态List一直添加对象却从不清理。考虑更换更成熟的JSON库
org.json库比较基础,在高频调用或者复杂场景下的稳定性不如Jackson、Gson这些主流库。它们对循环引用、深嵌套的处理更智能,内存管理也更优。
比如用Jackson实现转换的示例:// 初始化ObjectMapper(可以全局单例) private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public List<MyEntity> convertWithJackson(String jsonResponse) throws IOException { return OBJECT_MAPPER.readValue(jsonResponse, new TypeReference<List<MyEntity>>() {}); }
内容的提问来源于stack exchange,提问作者Marius Pop

