Spring Boot 超大请求-响应 JSON(100M)如何避免OOM?

12333社保查询网www.sz12333.net.cn 2026-02-12来源:人力资源和社会保障局

  环境:Spring Boot 3.5.0

  1. 简介

  在金融行业批量交易记录导入、医疗系统批量患者检查报告上传以及物联网数据上报等场景中,应用常需处理用户上传的批量数据、设备传感器日志等超大JSON请求(如单次100MB)。传统 @RequestBody 反序列化方式会一次性加载完整JSON到内存,导致堆内存溢出(OOM),尤其在并发请求时易引发服务崩溃,严重影响系统可用性。

  解决方案并非升级更大的实例,而是转变思路:不再将整个数据加载到内存,转而采用流式处理,逐段解析和处理数据,从而有效降低内存占用。

  2.实战案例

  2.1 危险处理方式

  大多数JSON库都倾向于先完整读取所有内容。它们会解析全部文本、构建对象。对于小型请求,这种做法基本上没有任何问题;但面对数百MB的数据时,它会悄然耗尽内存。如下示例:

  private final ObjectMapper objectMapper ;public LargeController(ObjectMapper objectMapper) { this.objectMapper = objectMapper;}@PostMappingpublic ResponseEntity parse(HttpServletRequest request) throws Exception { var body = new String(request.getInputStream().readAllBytes()); var students = this.objectMapper.readValue(body, new TypeReference>() {}); this.process(students) ; return ResponseEntity.ok("success") ;}public void process(List students) { students.forEach(s -> { // TODO }) ;}public static record Student(Long id, String name,Integer age,String gender, String className, String email, String phone,String address, String city,String province,String country) {}

  这种方式处理大 JSON 数据存在严重性能问题:

  • 内存溢出风险:readAllBytes() 将整个 100MB 请求体一次性加载到内存,readValue() 反序列化为 List 会生成大量对象,极易引发 OOM
  • 高内存占用:JSON 文本和反序列化后的对象同时存在于堆中,内存峰值可达数倍于请求大小。
  • 低效解析:ObjectMapper 默认构建完整对象树,无法流式处理,延迟高。
  • 阻塞处理:同步读取和处理,占用线程时间长,影响吞吐量。

      性能测试

      2.2 使用流式处理

      该流式处理利用 Jackson 的 JsonParser 逐个解析 JSON 数组中的对象,避免将整个请求体加载到内存。每解析一个 Student 就立即处理,内存仅保留单个对象,极大降低堆占用,防止 OOM,提升处理大 JSON 文件的性能与稳定性,适用于超大请求的高效解析场景。如下示例:

      private final ObjectMapper objectMapper ;@PostMapping("/2")public ResponseEntity parse2(HttpServletRequest request) throws Exception { JsonFactory factory = this.objectMapper.getFactory() ; try (JsonParser jp = factory.createParser(request.getInputStream())) { jp.nextToken(); while (jp.nextToken() != JsonToken.END_ARRAY) { Student student = this.objectMapper.readValue(jp, Student.class); this.processNode(student); } } return ResponseEntity.ok("success") ;}private void processNode(Student node) { // TODO}

      性能测试

      结果不是很明显,由于我这里使用的请求body大小为1000条数据。

      2.3 流式响应输出

      如果响应内容也是非常大的JSON内容,那么也很可能导致OOM错误。所以针对此种情况我们也可以进行流式的输出,如下示例:

      private final StudentRepository studentRepository ;@GetMapping("/4")public void streamResponse(HttpServletResponse response) throws Throwable { response.setContentType("application/json;charset=UTF-8"); response.setHeader("Transfer-Encoding", "chunked") ; try (var gen = this.objectMapper.getFactory().createGenerator(response.getOutputStream())) { gen.writeStartArray(); this.studentRepository.queryStudents().forEach(student -> { try { gen.writeObject(student) ; gen.flush() ; TimeUnit.SECONDS.sleep(1) ; } catch (Exception e) { e.printStackTrace(); } }) ; gen.writeEndArray(); }}public interface StudentRepository extends JpaRepository { Stream queryStudents();}

      这里查询的时候我们采用流式查询。

      运行结果

    本文标题:Spring Boot 超大请求-响应 JSON(100M)如何避免OOM?本文网址:https://www.sz12333.net.cn/zhzx/kexue/58075.html 编辑:12333社保查询网
  • 本站是社保查询公益性网站链接,数据来自各地人力资源和社会保障局,具体内容以官网为准。
    定期更新查询链接数据 苏ICP备17010502号-11