Java内存管理

5

我是一名C++程序员,在发现JPA对于我的几个当前应用程序来说是一个福音后,我开始玩Java。自从大学以来,我就没有接触过Java,现在我遇到了一个问题,即堆空间不足。我正在使用下面的代码作为jdbc / jpa / lucene的不太严肃的测试的主要部分,但我一直收到随机的OutOfMemory异常。

        EntityManager em = emf.createEntityManager();
        Query q = em.createQuery("select p from Product p" +
            " where p.productid = :productid");
        Connection con = DriverManager.getConnection("connection string");
        Statement st = con.createStatement();

        IndexWriter writer = new IndexWriter("c:\\temp\\lucene", new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);

        ResultSet rs = st.executeQuery("select productid from product order by productid");
        while (rs.next()) {
            int productid = rs.getInt("PRODUCTID");
            q.setParameter("productid", productid);
            Product p = (Product)q.getSingleResult();

            writer.addDocument(createDocument(p));
        }

        writer.commit();
        writer.optimize();
        writer.close();

        st.close();
        con.close();

我不会贴出createDocument的全部内容,但它只是通过add(new Field...)等方法实例化一个新的org.apache.lucene.document.Document并添加字段。总共大约有50个字段,大多数长度都很短(小于32个字符)。

在我的新手阶段,是否有完全愚蠢的事情(或者没有做的事情)会导致垃圾回收不起作用?

关于Java内存管理和触发GC,有最佳实践吗?

6个回答

3
我没有看到任何明显的问题。如果您正在使用非常大的数据库,可以尝试通过在JVM调用中使用-Xmx n选项来增加堆大小。通常情况下,这不是最好的解决方案 - 只有在您知道工作集大小实际上比默认堆大小更大时才使用此选项。
您是否使用了任何复杂的数据结构?如果对象之间存在循环引用,可能会阻止垃圾回收器清理无法访问的对象。如果您有任何手写的数据结构,请确保显式地将对已删除对象的引用置空,而不是执行类似于递减大小变量的操作。

1
GC对循环引用没有问题,对象仍将被删除。我不确定您所说的显式空出对象引用是什么意思。否则,您如何删除它们? - Robin
另一种删除它们的方法是让它们超出范围。明确地将引用设置为null适用于庞大对象和长循环的组合,其中对象引用将在内存中保留很长时间。 - gnud

2

你的链接没有带我去正确的页面,请修复该链接! - Richard T

2

好的...

通过使用Java和数据库(一个示例帖子postgresSQL mysql oracle differences>)的长期经验告诉我,我们在进行这项工作时使用的JDBC驱动程序经常出现问题。

我有一段代码需要保持与数据库的连接24/7,并且由于驱动程序内存泄漏,JVM总会在某个时候崩溃。因此,我编写了代码来捕获抛出的特定异常,然后采取越来越激烈的措施,包括断开连接并重新连接甚至重启JVM,在绝望的情况下清除问题。很痛苦,但是它有效,直到DBMS供应商推出了不会引起问题的新的JDBC驱动程序...实际上,我只是留下了代码,以防万一!

...所以,可能与您正在做的事情无关。

请注意,调用垃圾收集器是我使用的策略之一,但指标表明它很少有帮助。

此外,可能不清楚,但结果集在许多情况下(除非显式设置为其他方式),维护与数据库引擎本身的持续连接,即使您只是读取。一些JDBC驱动程序让您请求单向连接,但是会说谎并返回双向连接!小心!

因此,最好的做法是将ResultSet对象卸载到其他对象中以保存值,并尽快丢弃ResultSet对象本身。

祝你好运。 RTIII


2

可能您的永久代空间不足。

检查一下堆栈跟踪是否包含类似于java.lang.OutOfMemoryError:PermGen的内容。

您可以使用以下JVM参数增加此代的空间:-XX:MaxPermSize=128m。

垃圾回收期间不考虑永久代中的对象。

请参阅Sun网站上的此页面,了解有关垃圾回收和JVM中不同对象代的更多信息。


0
你的结果集中有多少项?如果记录足够多,那么你将耗尽所有内存,因为在这种情况下没有进行垃圾回收,你正在向写入器添加文档,它将持有对你创建的所有文档的引用。

0

Java维护了几个不同的内存池,其中任何一个内存池耗尽都会导致可怕的OutOfMemoryException。操作系统分配内存出现问题也可能表现为OOM。

您应该看到详细的堆栈跟踪 - 或者可能在应用程序目录中看到错误转储文件 - 这可能会提供更多有关问题的线索。

如果您使用一个体面的分析器 - 最近的Sun Java 6 JDK附带的JVisualVM可能已经足够了 - 您可以观察所有各种池,并查看哪些池正在运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接