查询中的MyBatis内存泄漏,结果是平均列表.30k行

Bor*_*oro 5 java memory ibatis mybatis

我在NetBeans Profiler中观察到,在执行查询后,Surviving Generations继续增加:

@Select("SELECT * FROM ais_dynamic WHERE rep_time >= #{from} AND rep_time <= #{to} AND ais_system = #{sys}")    
@Options(useCache=false,fetchSize=8192)
List<AisDynamic> getRecords(
        @Param("from") Timestamp from,
        @Param("to") Timestamp to,
        @Param("sys") int sys);
Run Code Online (Sandbox Code Playgroud)

就好像列表中的对象永远不会被释放,尽管它​​们没有被其他地方使用,并且应该在后台线程运行查询并处理其结果时死亡.

以下是NetBeans Profiler返回的实时结果: NetBeans Profiler的实时结果

我的问题:

  1. 如何防止内存泄漏?
  2. 我怎样才能优化这个查询,因为我可以看到我开始玩了Options虽然这没有阻止内存泄漏?

如果需要什么,请告诉我们将提供什么.

更新:

经过更多的测试后,我更担心问题在于MyBatis持有对检索结果的引用,因此它们不会随着时间的推移而被垃圾收集.在完成20次查询调用之后,等待我即使在30分钟后也没有观察到垃圾回收.我所做的只是调用方法:List<AisDynamic> adList = mapper.getRecords(from, to, sys);

Bor*_*oro 3

我周末测试了它,看来我解决了这个问题。感谢@partlov 的建议,尽管这不是解决方案,但它让我再次测试问题,并且发现了真正的问题。

问题是我负责处理用户查询请求的客户端正在堆积线程(正在执行查询)。run()由于当我对客户端进行压力测试时,请求非常频繁地出现,因此当上一个查询未完成时,下一个查询就会开始,即使我通过在查询方法中设置和检查标志来取消它们。当查询会话仍在与数据库对话时,例如当选择有超过 30k 个结果时,就会出现这种情况。因此,尽管取消标志已提出,但尚未检查,因为查询正在从数据库检索结果。这对于下一个查询启动来说是足够的时间,因此如果它也有很多结果,客户端就会堆积线程,实际上会消耗越来越多的内存。

由于(据我所知)似乎无法取消与数据库对话的会话(例如选择查询)(在MyBatis),因此我必须自己实现一种保护机制。我在客户端中实现的机制可确保在前一个查询(为同一用户执行)完成之前不会启动下一个查询。因此,现在查询会在退出其run()方法时通知客户端,然后才可能启动同一用户的下一个查询。


更新我从经验中了解到,中止/取消长检索事务的唯一且有点肮脏的方法(根据我的口味)是通过调用事务使用的实例close()的方法。SqlSession这将导致异常(下面的示例),必须按预期捕获和处理该异常。

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.lang.NullPointerException
### The error may exist in YourMapper.java (best guess)
### The error may involve methodOfTheHandlerInvolved
### The error occurred while handling results
### SQL: sqlOfYourQuery
### Cause: java.lang.NullPointerException
... (and a trace follows) ...
Run Code Online (Sandbox Code Playgroud)