在 201X 年 X 月 X 日早上 10 点多时,某保险公司核心系统多个
Weblogic 的应用服务器都极度缓慢,正常的业务都无法开展。
检查 Weblogic 的相关日志文件,发现有内存溢出的情况发生,如下:
java.lang.OutOfMemoryError: Java heap space
并产生了相关的内存溢出 HeapDump 文件:java_pid25528.hprof 随后对该文件进行下一步分析,可知:
其中占据内存较多的两组对象都是产生了 Stuck 的 Weblogic 线程,分别占用的内存是 84.85%和 10.40%,这两部分加起来是 95.25%,
说明已经占用了整个 Weblogic Server 的绝大部分内存。
进一步分析,发现大量的对象都被缓存在
(org.hibernate.engine.StatefulPersistenceContext)中.
在这个缓存中的 对象是我们的业务对象
业务对象如下:
com.**.scms.inf.model.ScmsCcommission
com.**.scms.inf.model.ScmsCcomissionId
由于这些缓存的数据无法释放,系统在业务量较大时,内存迅速 在 2 分钟内从 1G 增长到 4G,并且不断地进行 Full GC,导致系统极其缓慢。
Hibernate的一级缓存就是指Session缓存。通过查看Session接口的实现类——SessionImpl.java的源码可发现有如下两个类:
161 private transient ActionQueue actionQueue;
162 private transient StatefulPersistenceContext persistenceContext;
actionQueue它是一个行动队列,它主要记录crud操作的相关信息。
persistenceContext它是持久化上下文,它其实才是真正的缓存。
当执行完以下这句代码:
Customer customer = session.get(Customer.class, 1);
就会向一级缓存中存储数据,一级缓存其底层使用了一个Map集合来存储,Map的key存储的是一级缓存对象,而value存储的是快照。通过在这句代码上打个断点,然后以debug的方式运行,Watch一下session会看得更加清楚,如下:
从如下的代码中,也可以看出,session在执行相关清理工作时,也会执行persistenceContext.clear();
public void clear() {
281 errorIfClosed();
282 checkTransactionSynchStatus();
283 persistenceContext.clear();
284 actionQueue.clear();
285 }
466 /**
467 * clear all the internal collections, just
468 * to help the garbage collector, does not
469 * clear anything that is needed during the
470 * afterTransactionCompletion() phase
471 */
472 private void cleanup() {
473 persistenceContext.clear();
474 }
[StatefulPersistenceContext源码](http://www.docjar.com/html/api/org/hibernate/engine/StatefulPersistenceContext.java.html "StatefulPersistenceContext源码)
org.hibernate.engine.StatefulPersistenceContext
结合以上代码和该类的实现类可以确定是一个缓存上下文引用,而且从session.cleanup()方法session.cleanup()实际调用的就是persistenceContext.clear(),注意这句话**clear all the internal collections, just to help the garbage collector;
**
/**
58 import org.hibernate.util.IdentityMap;
59 import org.hibernate.util.MarkerObject;
62 * A PersistenceContext represents the state of persistent "stuff" which
63 * Hibernate is tracking. This includes persistent entities, collections,
64 * as well as proxies generated.
65 *
66 * There is meant to be a one-to-one correspondence between a SessionImpl and
67 * a PersistentContext. The SessionImpl uses the PersistentContext to track
68 * the current state of its context. Event-listeners then use the
69 * PersistentContext to drive their processing.
70 *
71 * @author Steve Ebersole
72 */
由于 Hibernate 的一级缓存是其内部使用的,无法关闭或停用(随着Session 销毁)。从
Hibernate 的手册或文档中可知,Hibernate 的一级缓存的清除可通过以下方式:
1)对于单个对象的清除:
Session session=sessionFactory.getCurrentSession(); session.evict(entity);
2)对于实体集合的清除:
Session session=sessionFactory.getCurrentSession(); session.clear();
建议在程序中加入对 Hibernate 一级缓存的清除工作,以便可以其
内存数据可以及时释放。
关于Hibernate缓存问题可参考:
Hibernate缓存策略
如果应用会定时启动几个quartz任务来处理复杂且影响页面、响应时间的业务,这部分业务的业务数据是从数据库查的,只有业务数据全都被处理完后这个quartz才会结束。
当这几个 quartz任务的业务数据较多的时候,就会有很多对象被填入一级缓存这样一来持久化上下文中保存的对象越来越多。最终导致OOM.
http://www.docjar.com/html/api/org/hibernate/engine/StatefulPersistenceContext.java.html
http://www.docjar.com/html/api/org/hibernate/impl/SessionImpl.java.html
https://blog.csdn.net/yerenyuan_pku/article/details/70148567
https://www.cnblogs.com/hyl8218/p/5076338.html
Hibernate缓存策略
Hibernate缓存策略
米宝窝 https://rocklei123.github.io/