-
2006-08-05
使用Hibernate开发中,关于内存溢出的问题!
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
内存溢出的错误和连接资源超出的错误,我大致找到了原因:
http://cszyj780217.blogbus.com/logs/2985731.html
程序初始化的时候,要创建SessionFactory对象,大家都知道,这是个重量级对象,包含一些初始化信息和预先进行查询的数据。我写了个HibernateUtil类,实现了对Session及其他相关数据操纵的对象封装,也是参照Hibernate官方推荐的那个类改变而写。并写了个Servlet实现创建SessionFactory并存储在InitialContext对象,通过JNDI可以对该对象进行访问。HibernateUtil类中,有个静态块,进行Configuration对象和SessionFactory对象的初始化,每当我类改变时,Tomcat重新reload,这导致了重复执行此代码块,此时前一个
SessionFactory所包含的大量对象还未完全释放,又新创建了个此重量级的对象,从而导致了内存溢出或超出最大的数据连接限制的错误。只是Apache的DBCP和C3P0这两个连接池所反应的错误信息提示使不同,DBCP反映出内存溢出从错误,而C3P0反映出的错误使连接资源耗尽。觉得2个连接池反应的都有道理,从而看出这两个连接池的错误捕捉的出发点是不同。请大家注意了。我优化了下代码,现在不管怎么Tomcat的reload,都没有再出现上述的错误了。
原先的设计思路是把创建SF的工作放到一个Servlet中去,并存储到InitialContext中。当Tomcat启动时候,自动执行此Servlet。那么程序中就可以通过JNDI来得到此SF对象了。
通过对jvm里面Class类对象的跟踪,我发现在Tomcat服务器reload的时候,在ServletContext下产生的对象同处于一个线程内(Servlet本质是就是基于线程的),当reload时候,当前的线程却不能正常关闭!当然也就无法回收该线程内的class对象,这样的话,reload后,就会新产生一个线程,并在该线程内创建SF对象。也就是说,在JVM中,存在了2个SF对象,只是起hashcode不同。我们都知道创建SF这是个很浪费的操作。所以,reload几次后,我指的是间隔比较短,当JVM还没来得及回收无用对象时,又创建了新的SF对象。所以就会容易造成内存溢出或者是连接资源耗尽的错误了。
现在我变通的进行了一些优化,将原来进行SF创建工作的Servlet改变成一个监听器类,本质上Tomcat的reload就是一个ServletContext的开始和结束,那么在结束时,我手动的将SF对象关闭,我是这样实现的。当前运行正常。
监听器:
public class BuildSessionFactory implements ServletContextListener {
private static final long serialVersionUID = 2366489378019950504L;
Logger log = Logger.getLogger(this.getClass());
InitialContext context = null;
static Configuration configuration = null;
static SessionFactory sessionFactory = null;
/*
* (non-Javadoc)
*
* @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent arg0) {
// TODO Auto-generated method stub
log.info("初始化HibernateSessionFactory 开始...");
try {
context = new InitialContext();
} catch (NamingException e) {
log.error("初始化上下文对象失败. 详细原因:" + e.getMessage());
}
configuration = new Configuration();
log.info("构建SessionFactory对象.");
sessionFactory = configuration.configure().buildSessionFactory();
if (sessionFactory == null) {
try {
throw new Exception("SessionFactory 创建失败!");
} catch (Exception e) {
log.error("构造SessionFactory对象失败.原因:" + e.getMessage());
}
}
try {
if (context.lookup(Constants.SESSION_FACTORY_JNDI) != null) {
context.rebind(Constants.SESSION_FACTORY_JNDI, sessionFactory);
log.info("SessionFactory 重新绑定到环境中 JNDI 名称:hibernate.");
} else {
context.bind(Constants.SESSION_FACTORY_JNDI, sessionFactory);
log.info("SessionFactory 成功绑定到环境中 JNDI 名称:hibernate.");
}
} catch (NamingException e) {
log.error("进行 JNDI 名字绑定错误. 原因 1 :" + e.getMessage());
} catch (Exception e) {
log.error("进行 JNDI 名字绑定错误. 原因 2 :" + e.getMessage());
} finally {
try {
context.close();
context = null;
} catch (NamingException e) {
e.printStackTrace();
}
}
}
/*
* (non-Javadoc)
*
* @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
configuration = null;
if (!sessionFactory.isClosed()) {
sessionFactory.close();
}
sessionFactory = null;
}
}
我还不能确保这样就能解决问题。不过,在程序部署后,reload属性要设置成false的。那么也就不会存在这样的问题了。这样也只是方便调试方便而已。
考虑的不成熟,大家谁有更好的建议,thanks随机文章:
Hibernate + Proxool配置 2006-08-05扩展 Hibernate 对各类数据源支持 2006-01-05oracle中读写blob字段的问题 2005-12-28JVM的垃圾回收机制详解和调优 2005-09-22google使用指南 2005-08-18
收藏到:Del.icio.us









评论
cfg.configure();
但是我觉得是否有可以在SessionFactory本身,也就是Hibernate本身的解决方法呢?
今天我在用Hibernate + Struts做了个demo,发现即使只是使用
Configuration cfg = new Configuration();
cfg.configure();
Tomcat的内存都一直在增加,并且增加到一定程度,就显示内存溢出。
还在寻觅中。。。
希望前辈指点!