[英]JPA EntityManager big memory problems
我在使用 Spring、Hibernate 和 JPA 的 web 应用程序时遇到了一些问题。问题是 memory 消耗非常高,随着时间的推移而增加,而且似乎永远不会减少。 它们很可能源于 EntityManager 的不正确使用。 我四处搜寻,但还没有找到确定的东西。
我们使用的 DAO 都扩展了以下 GenericDAO,其中注入了我们唯一的 EntityManager:
public abstract class GenericDAOImpl<E extends AbstractEntity<P>, P> implements
GenericDAO<E, P> {
@PersistenceContext
@Autowired
private EntityManager entityManager;
[...]
使用通用 DAO 是因为它具有通过 ID 等获取实体的方法,这在所有 ~40 个 DAO 中实现起来会很痛苦。
EntityManager 通过以下方式配置为 Spring bean:
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="entityManager" factory-bean="entityManagerFactory"
factory-method="createEntityManager" scope="singleton" />
我认为最大的问题是对所有东西都使用这个共享的 EntityManager。 在服务类中,我们对需要事务的方法使用@Transactional 注释。 这会根据我读取的内容自动刷新 EntityManager,但与清除不同,所以我猜这些对象仍在 memory 中。
我们注意到每天在数据库中每次自动导入数据后 memory 都会增加(约 7 个文件,每个文件 25k 行,其中创建了很多链接对象)。 而且在正常运行期间,当检索大量数据时(假设一次请求有 100-200 个对象)。
任何人都知道我可以如何改善当前情况(因为此时情况有点糟糕......)?
编辑:已在已部署的应用程序上运行探查器,这就是它发现的内容:
One instance of "org.hibernate.impl.SessionFactoryImpl" loaded by "org.apache.catalina.loader.WebappClassLoader @ 0xc3217298" occupies 15,256,880 (20.57%) bytes. The memory is accumulated in one instance of "org.hibernate.impl.SessionFactoryImpl" loaded by "org.apache.catalina.loader.WebappClassLoader @ 0xc3217298".
这大概是EntityManager没有清零吧?
我倾向于同意你的评估。 EntityManagers 并非真正设计为用作单例。 刷新 EntityManager 不会清除 memory 中的任何内容,它只会将实体与数据库同步。
可能发生的情况是 EntityManager 保持对持久性上下文中所有对象的引用,而您永远不会关闭上下文。 ( 这个人有一个类似的问题。)清除它确实会删除 EntityManager 对您的实体的所有引用,但是,如果您发现自己经常需要调用 clear(),您可能应该重新评估您通常如何使用 EntityManager。 如果您只是想避免 LazyInitializationExceptions,请考虑 Spring* 的 OpenSessionInViewFilter。 这允许您延迟加载实体,同时仍然让 Spring 管理 bean 的生命周期。 bean 的生命周期管理是 Spring 框架的一大优势,因此您需要确保覆盖该行为确实是您想要的。
确实在某些情况下您需要一个长期存在的 EntityManager,但这些情况相对较少并且需要大量的理解才能正确实施。
*注意:OpenSessionInView 需要非常小心以避免N+1 问题。 这是一个很大的问题, 有些人在 View an AntiPattern 中调用 Open Session 。 谨慎使用。
编辑
此外,您也不需要使用@Autowired
注释@PersistenceContext
元素。 @PersistenceContext
自己进行布线。
非 JEE 兼容的应用程序服务器,您不应该使用@Autowired/@PersistenceContext private EntityManager entityManager;
!
你应该做的是这样的:
class SomeClass {
@PersistenceUnit private EntityManagerFactory emf;
public void myMethod() {
EntityManager em = null;
try {
em = emf.createEntityManager();
// do work with em
}
} catch (SomeExceptions e) {
// do rollbacks, logs, whatever if needed
} finally {
if (em != null && em.isOpen()) {
// close this sucker
em.clear();
em.close();
}
}
}
一些注意事项:
您应该从private EntityManager entityManager;
上面删除@Autowired
注解; 并从上下文定义文件中删除entityManager
bean 定义。 此外,如果您不使用<context:annotation-config/>
和<context:component-scan/>
XML 标签,您必须在上下文中定义PersistenceAnnotationBeanPostProcessor
bean。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.