代码之家  ›  专栏  ›  技术社区  ›  Jeff Ferland

休眠:走数百万行,不要泄露内存

  •  6
  • Jeff Ferland  · 技术社区  · 14 年前

    下面的代码起作用,但Hibernate从不放弃对任何对象的控制。打电话 session.clear() 导致有关获取联接类和调用 session.evict(currentObject) 在检索下一个对象之前也无法释放内存。最终我耗尽了我的堆空间。

    检查我的堆转储时,StatefulPersistenceContext是指向我的对象的所有引用的垃圾收集器的根。

    public class CriteriaReportSource implements JRDataSource {
    
        private ScrollableResults sr;
        private Object currentObject;
        private Criteria c;
        private static final int scrollSize = 10;
        private int offset = 1;
    
        public CriteriaReportSource(Criteria c) {
            this.c = c;
            advanceScroll();
        }
    
        private void advanceScroll() {
    //        ((Session) Main.em.getDelegate()).clear();
            this.sr = c.setFirstResult(offset)
                       .setMaxResults(scrollSize)
                       .scroll(ScrollMode.FORWARD_ONLY);
            offset += scrollSize;
        }
    
        public boolean next() {
            if (sr.next()) {
                currentObject = sr.get(0);
                if (sr.isLast()) {
                    advanceScroll();
                }
                return true;
            }
    
            return false;
        }
    
        public Object getFieldValue(JRField jrf) throws JRException {
            Object retVal = null;
            if(currentObject == null) { return null; }
            try {
                retVal = PropertyUtils.getProperty(currentObject, jrf.getName());
            } catch (Exception ex) {
                Logger.getLogger(CriteriaReportSource.class.getName()).log(Level.SEVERE, null, ex);
            }
            return retVal;
        }
    }
    
    3 回复  |  直到 14 年前
        1
  •  4
  •   Community Jaime Torres    7 年前

    在这里使用有状态会话,它不是正确的工具 走上百万行 然后写一份报告。使用 The StatelessSession interface 相反。

    如果仅仅使用MySQL Connector/J还不够,那么还需要使用 this :

    Query query = session.createQuery(query);
    query.setReadOnly(true);
    // MIN_VALUE gives hint to JDBC driver to stream results
    query.setFetchSize(Integer.MIN_VALUE);
    ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
    // iterate over results
    while (results.next()) {
        Object row = results.get();
        // process row then release reference
        // you may need to evict() as well
    }
    results.close();
    
        2
  •  0
  •   Greg Case    14 年前

    试着打电话 setCacheMode(CacheMode.IGNORE) 在打开它之前。

    advanceScroll( )方法,添加 if (sr != null) sr.close(); 以便在重新分配给新的ScrollableResults之前关闭以前的ScrollableResults。

    一个问题:调用setMaxSize(),然后跟踪偏移量,然后重新打开可滚动结果的原因是什么,为什么不这样做呢?

    public CriteriaReportSource(Criteria c) {
        this.c = c;
        this.sr = c.setCacheMode(CacheMode.IGNORE)
                   .scroll(ScrollMode.FORWARD_ONLY);
    }
    
    
    public boolean next() {
        if (sr.next()) {
            currentObject = sr.get(0);
            return true;
        }
        return false;
    }
    
        3
  •  0
  •   Jeff Ferland    14 年前

    if (sr.isLast()) {
        advanceScroll();
        //...
    

    结合

    ((Session) Main.em.getDelegate()).clear();
    //Also, "Main.em.clear()" should do...
    

    导致数据库刷新过早。这就是有关托收的例外情况的原因。集合不能在无状态会话中处理,因此这是不可能的。我不知道为什么 session.evict(currentObject) 无法工作时 Session.clear() 确实管用,但这是我目前必须处理的方式。我会把答案分给任何能找出答案的人。

    所以,现在,我们有一个答案。需要手动滚动窗口,关闭ScrollableResults没有帮助,我需要正确运行Session.clear()。