MyBatis查询接口返回大量数据导致OOM


异常描述

  • 接口调试过程中发现程序运行中断,DEBUG跟踪到Mapper中的查询接口报错,没有接收到返回值且抛出 out of memory 异常

异常定位

  • 逐步DEBUG,对比传入底层的参数及最终的执行的SQL,发现WHERE条件均没有传参,即此时是全表查询,且数据量很大,导致了OOM异常的发生

异常反思

  • 接口复用性:Mapper.java 是 DAO 层中接口,无论是 Service 层或 Business 层都会间接或直接的调用 Mapper.java 中的接口
  • 接口命名没有见名知义
  • 除了使用MyBatis工具自动生产的接口外,其余接口均是为了开发某个功能时添加的,此时为了排序、为了某个特定值的传参或者有时候自己开发者觉得某个值肯定不会空,而没有使用 test 进行参数判空,其他开发人员在方法调用时直接根据接口名称和返回值就断定方法适合自己使用,而没有查看底层的Mapper.xml中的SQL实现,导致问题出现
  • 特定功能开发的接口要通过名称注明,避免其他人误调用
  • 共用的接口功能,通过 Map 或 PO 类作为参数的接口,每个属性都要加 test 判断,且需注意 order limit 与 属性条件使用顺序,避免报语法错误
  • 查询接口一定要使用分页,调用接口前对入参进入处理,如果是集合,则拆分为500或1000一组,分批查询
  • 即使是返回一条数据,也加上 limit 1

OOM常见异常类型

  • 堆异常
  • 内存泄露
  • 对象引用一直未释放
  • 开发过程中使用到的对象、集合,使用完毕后直接赋值为null,等待GC
  • 避免对象不使用依然向下一个方法中传递
  • 方法内的形参不使用时及时进行方法重构
  • 内存溢出
  • 生成对象过大
  • 合理设计表结构,避免对象中内容过多
  • 对象生命周期过长
  • 开辟无用空间
  • 在循环中创建新的对象
  • 可以改为在循环外声明对象,在循环内实例化对象,始终是同一个对象
  • 可以改为克隆
  • 堆空间大小设置不合理,手动调整
  • 栈异常
  • 用于存放各个方法的内部变量表
  • 当大量的方法被调用,而又未及时结束,或者单个方法内部定义了大量局部变量
  • 方法区
  • 用于存放类的定义Class文件和常量池等
  • 项目中类过多

解决方案

  • 因为jdbc默认会把查询结果集全部返回到客户端导致oom错误
  • 返回的数据量的大小大于了JVM分区中内存空间的大小