关于 Hibernate缓存的问题

05-12-13 willow990
我在开发一个J2EE的项目,我使用了Hibernate中间件,由于我的系统纪录数比较大,当系统初始启动时,需要Hibernate将纪录从数据库中读取到服务器端.这时Hibernate的缓存机制会将这些数据缓存.可是当我的纪录数达到一万多条时,系统会发生OutOfMemory异常.请高手指教,谢谢!!!

banq
2005-12-13 10:30
看看这个帖子,对这个问题讨论全面:

http://www.jdon.com/jive/thread.jsp?forum=62&thread=22251

willow990
2005-12-13 14:50
我看了你说的帖子,但是我觉得并不适用我的情况,由于我在服务器使用一个集合存放所有的Customer对象,有几万个对象,我直接使用Hibernate的O/RM映射机制,将整个集合进行映射,由于使用Lazy方式,当程序初始启动时,只会生成该集合的引用。这时不会OutOfMemory。但是如果当客户段的请求需要对该集合中的数据进行操作时,这时Hibernate会自动将数据库中所有的Customer对象恢复到集合中后,才可以对集合进行操作。问题在于当Hibernate将Customer从数据库中恢复时,Hibernate会将为这几万个对象在Session进行缓存,这时就会OutOfMemory

你所提到的帖子是对于多步操作可以进行Session的flush操作。可是我这里Hibernate的自动恢复集合对象中的元素,是不可以进行flush操作的。请问:有没有什么解决的方法,谢谢!!!

我的意思可以用代码表示如下:

List list = new ArrayList();

for(int i = 0; i < 50000; i++) {
  Customer c = new Customer();
  list.add(c);
}

// 将存取List存入数据库,这时没有异常
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction(); 
session.save(list)
tx.commit()
.....
//  将List读出,由于使用Lazy模式这时不会有异常.
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction(); 
List newList = session.createQuery("from List").get(0);
tx.commit()

// 这时读取List中元素。.Hibernate将所有Customer对象恢复,发生//OutofMemeory异常
Customer newCustomer = (Customer)newList.get(0);






<p>

banq
2005-12-13 15:22
个人认为:两个方案:

1.关闭Hibernate缓存,如何关闭,在我上面链接中的文章有介绍。

2.直接使用JDBC,借助其他专门批量数据优化的框架提升性能,如Jdon框架等。

willow990
2005-12-14 09:39
由于我设计时是期望对于List中Customer 对象的任何更新后由Hibernate自动更新,我期望的事务处理的粒度是在List级而不是在Customer级.所有我倾向于不清空Session.我的解决方案是增加JVM的Heap的尺寸。由于所有Hibernate的Session管理的对象是存放JVM的Heap中,它Session可是使用的大小是由JVM的Heap大小决定的。而JVM的默认Heap大小是1M。所以会遇到前面说到的OutOfMemory问题。我通过改变JVM的Heap大小解决了上面的问题(设为32M―128M)。同时通过增加Heap大小,测试通过了10万条数据.不知你觉得这种方案是否可行.

关于改变JVM的Heap尺存,见

Setting Heap Sizes

yuxie
2005-12-14 11:16
为什么要把所有的数据都取出来呢??很少需要这种情况吧

yellowicq
2005-12-14 12:07
应该使用分页查询

chenruixiang
2009-03-29 17:23
你这里不应该用list,应该用iterator()方法进行查询。

用list()查询时,一次执行整个查询,并将查询的结果放在二级缓存中,你查询的对象又几万条,当然会造成内存溢出。

而用iterator则不同,iterator()方法是用到时才执行查询操作

也就是说当你真正要对某个对象进行操作时才会执行查询操作。

chenruixiang
2009-03-29 17:26
list()方法会一次获得所有的结果集对象,而且它会依据查询的结果初始化所有的结果集对象。这在结果集非常大的时候必然会占据非常多的内存,甚至会造成内存溢出情况的发生。

  iterator()方法在执行时不会一次初始化所有的对象,而是根据对结果集的访问情况来初始化对象。因此在访问中可以控制缓存中对象的数量,以避免占用过多缓存,导致内存溢出情况的发生。使用iterator()方法的另外一个好处是,如果只需要结果集中的部分记录,那么没有被用到的结果对象根本不会被初始化。所以,对结果集的访问情况也是调用iterator()方法时执行数据库SQL语句多少的一个因素。

usejava
2009-04-20 13:56
是不是用iterator()每次访问都会有一次数据库操作?

这样效率也太低了。

yellowcat
2009-06-06 12:14
createQuery().setReadOnly(true).list()

或者

createQuery().scroll()

[该贴被yellowcat于2009-06-06 12:15修改过]

猜你喜欢