为什么我用hibernate创建的对象不被垃圾回收(即使会话过期也不消失)

悬赏:40 发布时间:2008-07-02 提问人:yourgame (初级程序员)

为什么Hibernate创建的对象不被销毁呢?
如图(1)先用hibernate查询,创建了36(TMain.java)个对象
然后再用jdbc查询如图(2)创建了37(TMain2.java)个对象
系统设置会话过期时间为2分钟
会话过期后如图(3)jdbc创建的对象消失了36个,而hibrenate创建的对象一个也没有消失,并且可以看到CGLIB代理对象也没有消失
这是为什么?小弟不才,没有仔细读过hibernate源代码,对其中原因不明。希望各位帮助解惑,版主请别删贴。

图(1)

图(2)

图(3)
WEB.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<servlet>
		<servlet-name>service</servlet-name>
		<servlet-class>org.test.Service</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>service</servlet-name>
		<url-pattern>/servlet/service</url-pattern>
	</servlet-mapping>
	<session-config>
		<!-- 这里为了测试,设置session国企时间为2分钟 -->
		<session-timeout>2</session-timeout>
	</session-config>
</web-app>


Hibernate.cfg.xml (hibernate 配置文件)
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="connection.username">sa</property>
		<property name="connection.url">jdbc:jtds:sqlserver://127.0.0.1:1433/fast</property>
		<property name="dialect">org.hibernate.dialect.SQLServerDialect</property>
		<property name="myeclipse.connection.profile">jtds</property>
		<property name="connection.password">yourgame</property>
		<property name="connection.driver_class">net.sourceforge.jtds.jdbc.Driver</property>
		<property name="show_sql">true</property>
		<mapping resource="org/test/TMain.hbm.xml" />
	</session-factory>
</hibernate-configuration>


Service.java
package org.test;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.Session;

@SuppressWarnings("serial")
public class Service extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String method = request.getParameter("method");
		List list = null;
		if (method.equals("hibernate")) { // 判断查询的方式
			list = this.hibernate(request, response);
		}
		if (method.equals("jdbc")) {
			list = this.jdbc(request, response);
		}
		request.setAttribute("list", list);	//将查询的结果集放入request中
		request.getRequestDispatcher("/index.jsp").forward(request, response);

	}

	/**
	 * 2008-7-1-上午11:05:41
	 * 
	 * 功能:通过Hibernate来查询对象返回集合
	 * 
	 */
	private List hibernate(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Session session = HibernateUtils.getSession();
		session.beginTransaction();
		List list = session.createQuery("from TMain").list();
		session.getTransaction().commit();
		HibernateUtils.closeSession(session);
		return list;
	}

	/**
	 * 2008-7-1-上午11:06:20
	 * 
	 * 功能:通过jdbc查询对象返回集合,Utils.list()方法将结果集封装成javabean
	 * 
	 */
	private List jdbc(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		return Utils.list(new TMain2(), "t_main", null, new Conn());
	}
}

TMain.java (这个javabean用户Hibernate映射)
package org.test;

public class TMain  implements java.io.Serializable {
     private Integer id;
     private String uuid;
其他字段和setter,getter() 略………

TMain2.java(和TMain.java 一样,这个javabean用于jdbc结果集封装)


问题补充:
package com.bjsxt.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

private static SessionFactory factory;

private HibernateUtils() {

}

static {
Configuration cfg = new Configuration().configure();
factory = cfg.buildSessionFactory();
}

public static SessionFactory getSessionFactory() {
return factory;
}

public static Session getSession() {
return factory.openSession();
}

public static void closeSession(Session session) {
if (session != null) {
if (session.isOpen()) {
session.close();
}
}
}
}

问题补充:
请问wangxin0072000

您指的是不是spring的OpenSessionInView ???
问题补充:
请问wangxin0072000:
OpenSessionInView 的作用就是把hiernate的Session保持到页面或者说保持到请求完成后再关闭吗?
问题补充:
请问请问wangxin0072000:
我按照您说的调用System.gc();

效果可以,但是还有一些对象没有消失,org.test.TMain$$BulkBeanByCGLIB$$6d008257
org.test.TMain$$FastClassByCGLIB$$2519251b
org.test.Service
org.test.TMain
以上类的实例都还存在一个实例不被回收,请问这是什么原因呢?
问题补充:
请问wangxin0072000
按照您说的调用System.gc()方法很凑效,对象大部分都消失了
但是还有四个实例不被回收
分别是
org.test.TMain  (1个)
org.test.Service (1个)
org.test.TMain$$FastClassByCGLIB$$2519251b (1个)
org.test.TMain$$BulkBeanByCGLIB$$6d008257   (1个)

无论我调用多少次System.gc() 这些对象也不会被回收
请您帮助

采纳的答案

2008-07-02 wangxin0072000 (高级程序员)

那么有可能是垃圾回收器一直没有回收。你试试手动回收一下。
见:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {   
        String method = request.getParameter("method");   
        List list = null;   
        if (method.equals("hibernate")) { // 判断查询的方式   
            list = this.hibernate(request, response);   
        }   
        if (method.equals("jdbc")) {   
            list = this.jdbc(request, response);   
        }   
        if (method.equals("gc")) {   
            System.gc( );
            list=new ArrayList();  
        } 
        request.setAttribute("list", list); //将查询的结果集放入request中   
        request.getRequestDispatcher("/index.jsp").forward(request, response);   
  
    }  


之后等用hibernate测完之后,用method=gc为参数再来一次。之后再看看。

提问者对于答案的评价:
谢谢你的帮助,等我把我的问题项目的例子发给你啊。到时候记得帮我检查一下,谢谢你!

其他回答

倒是想看看 HibernateUtils里的代码。
llade (资深程序员) 2008-07-02
1 会话销毁,是指在servlet容器中存放的session信息销毁。与hibernate无关。
2 而hibernate创建的对象与hibernate的session相关。如果你关闭了session后,这些创建的对象就会成为游离状态,当内存不足的时候,JAVA的垃圾回收机制就会回收游离状态的对象。
3 仔细看看你的代码,你没有关闭session所以自然不会回收,这也就是hibernate造成的内存泄漏问题,这也就是为什么spring中会用一个过滤器关闭与这条线程相关的hibernate.session了。
wangxin0072000 (高级程序员) 2008-07-02
楼主的代码有一些问题:

1、hibernate的session建立了一级缓存,对于session的获取以基于线程局部变量(ThreadLocal)方式存取比较合理。
2、对于关闭后,如2楼楼主所言,查出来的对象变成游离状态,但是否被垃圾回收,而与HttpServlet的会话时间长短无关。
3、对于查询,不需要事务,不需要获得表的独占或共享级别锁。
4、在Servlet中直接进行数据库操作的方式比较地不好。
jujucom (初级程序员) 2008-07-02
OpenSessionInView是保证每个访问servlet用户的线程都能从spring的配置文件中得到hibernate的session。我说的那个过滤器具体是什么我也忘了,我明天去看看,好想与你写的HibernateUtils中的closeSession类似。我始终认为是垃圾回收器一直没有回收。看hibernate的源码:
public Connection close() throws HibernateException {
		log.trace( "closing session" );
		if ( isClosed() ) {
			throw new SessionException( "Session was already closed" );
		}
		

		if ( factory.getStatistics().isStatisticsEnabled() ) {
			factory.getStatisticsImplementor().closeSession();
		}

		try {
			try {
				if ( childSessionsByEntityMode != null ) {
					Iterator childSessions = childSessionsByEntityMode.values().iterator();
					while ( childSessions.hasNext() ) {
						final SessionImpl child = ( SessionImpl ) childSessions.next();
						child.close();
					}
				}
			}
			catch( Throwable t ) {
				// just ignore
			}

			if ( rootSession == null ) {
				return jdbcContext.getConnectionManager().close();
			}
			else {
				return null;
			}
		}
		finally {
			setClosed();
			cleanup();
		}
	}

最后有个cleanup();
再看这个方法的注释:
/**
	 * clear all the internal collections, just 
	 * to help the garbage collector, does not
	 * clear anything that is needed during the
	 * afterTransactionCompletion() phase
	 */
	private void cleanup() {
		persistenceContext.clear();
	}

可见在close的同时已经清除了所有持久化对象与Session的关联了。所以这些对象肯定是游离状态。
wangxin0072000 (高级程序员) 2008-07-02
org.test.TMain$$FastClassByCGLIB$$2519251b (1个) 不应该被回收,这个是是org.test.TMain的代理类。Hibernate就是使用cglib来增强普通java对象的。对于代理类保留一个Class对象,那下次你load TMain就不会再创建了。

org.test.TMain$$BulkBeanByCGLIB$$6d008257 (1个) 具体是什么作用则需要研究cglib的类库了。估计是对对象的setter和getter进行快速操作的类。
org.test.TMain (1个) 估计是被org.test.TMain$$BulkBeanByCGLIB$$6d008257引用的一个副本。

org.test.Service (1个) 这个就没有看到代码,不好猜测。
llade (资深程序员) 2008-07-03