【问题标题】:Hibernate out of memory exception while processing large collection of elements在处理大量元素时休眠内存不足异常
【发布时间】:2014-01-01 14:13:24
【问题描述】:

我正在尝试处理重量级元素(图像)的集合。集合的大小在 8000 - 50000 个条目之间变化。但是由于某种原因,在处理了 1800-1900 个条目后,我的程序出现了 java.lang.OutOfMemoryError: Java heap space。

据我了解,每次调用 session.getTransaction().commit() 程序时都应该释放堆内存,但看起来它永远不会发生。 我做错了什么?代码如下:

private static void loadImages( LoadStrategy loadStrategy ) throws IOException {
    log.info( "Loading images for: " + loadStrategy.getPageType() );

    Session session = sessionFactory.openSession();
    session.setFlushMode( FlushMode.COMMIT );
    Query query = session.createQuery( "from PageRaw where pageType = :pageType and pageStatus = :pageStatus and sessionId = 1" );
    query.setString( "pageStatus", PageStatus.SUCCESS.name() );
    query.setString( "pageType", loadStrategy.getPageType().name() );
    query.setMaxResults( 50 );

    List<PageRaw> pages;
    int resultNum = 0;

    do {

        session.getTransaction().begin();

        log.info( "Get pages statring form " + resultNum + " position" );
        query.setFirstResult( resultNum );
        resultNum += 50;
        pages = query.list();
        log.info( "Found " + pages.size() + " pages" );


        for (PageRaw pr : pages ) {
            Set<String> imageUrls = new HashSet<>();
            for ( UrlLocator imageUrlLocator : loadStrategy.getImageUrlLocators() ) {
                imageUrls.addAll(
                        imageUrlLocator.locateUrls( StringConvector.toString( pr.getSourceHtml() ) )
                );
            }

            removeDeletedImageRaws( pr.getImages(), imageUrls );
            loadNewImageRaws( pr.getImages(), imageUrls );
        }

        session.getTransaction().commit();

    } while ( pages.size() > 0 );

    session.close();
}

【问题讨论】:

    标签: java performance hibernate out-of-memory data-processing


    【解决方案1】:

    您将 flushing 与 clearing 混淆了:

    • 刷新会话对数据库执行所有未决语句(它将内存状态与数据库状态同步);

    • 清除会话会清除会话(一级)缓存,从而释放内存。

    所以你需要同时刷新和清除会话以恢复占用的内存。

    除此之外,您必须禁用二级缓存。否则,即使在清除会话后,所有(或大部分)对象仍可访问。

    【讨论】:

    • 如何禁用二级缓存? session.evict() 有帮助吗?
    【解决方案2】:

    我不知道您为什么认为提交事务会释放堆内存。运行垃圾回收就是这样做的。

    如果您的 perm gen 用尽,可能会发生 OOM 错误。

    简单的答案是在启动 JVM 时更改最小和最大堆大小以及 perm gen 大小,看看它是否会消失。

    我建议使用 VisualVM 之类的分析器,并查看运行时消耗内存的内容。应该很容易修复。

    我猜你试图一次提交太大的块。把它分解成更小的部分,看看是否有帮助。

    【讨论】:

    • 我怀疑会话是问题所在。所以我添加了这一行 session.setFlushMode(FlushMode.COMMIT)。但看起来我错了。我以前从未使用过分析器,我想是时候开始了)))
    • 为了记录,有效地释放内存的不是垃圾回收,而是对象变得无法访问。这是 OP 假设在事务提交时会发生的情况(隐式刷新)。
    【解决方案3】:

    尝试使用 session.clear() “完全清除会话。驱逐所有加载的实例并取消所有挂起的保存、更新和删除。不要关闭打开的迭代器或 ScrollableResults 的实例”

    【讨论】:

      【解决方案4】:

      这篇文章解决了我的问题

          Session session = sessionFactory.getCurrentSession();
            ScrollableResults scrollableResults = session.createQuery("from DemoEntity").scroll(ScrollMode.FORWARD_ONLY);
            int count = 0;
            while (scrollableResults.next()) {
             if (++count > 0 && count % 100 == 0) {
              System.out.println("Fetched " + count + " entities");
             }
             DemoEntity demoEntity = (DemoEntity) scrollableResults.get()[0];
             //Process and write result
             session.evict(demoEntity);//important to add this
            }
           }
      

      bulk fetching hibernate

      1. 使用休眠 ScrollableResult
      2. 使用驱逐

      顺便说一句我尝试了无状态解决方案,它给了我这个我没有解决的异常(也许你可以改进这个答案)Exception details is here

      org.hibernate.SessionException: collections cannot be fetched by a stateless session
      

      所以我将睡眠(延迟)作为其后台进程进行了调整,并且很长时间服务器上的资源不足,我必须冷却 CPU;半夜工作(非高峰时间)。

      【讨论】:

        猜你喜欢
        • 2011-09-02
        • 1970-01-01
        • 2016-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多