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 但它所做的只是实例化一个新的 org.apache.lucene.document.Document 并通过 add(new Field...) 等添加字段。总共大约有 50 个字段,大多数都很短长度为字符串(<32 个字符)。

在我的新手中,我正在做(或不做)一些完全愚蠢的事情会导致事情不被 GC 处理吗?

是否有关于 java 内存管理和 GC 的最佳实践?

4

6 回答 6

3

我没有看到任何明显不合适的地方。如果您正在使用一个非常大的数据库,您可以尝试通过使用-Xmx nJVM 调用中的选项来增加堆大小。这通常不是最好的解决方案——只有当你知道你的工作集大小实际上大于默认堆大小时才这样做。

您是否使用任何复杂的数据结构?如果对象之间存在循环引用,则可能会阻止垃圾收集器清理无法访问的对象。如果您有任何手写的数据结构,请确保您明确地清除对已删除对象的引用,而不是执行诸如递减大小变量之类的操作。

于 2008-10-20T01:21:11.857 回答
2

可能您的永久代空间不足。检查您的堆栈跟踪是否包含类似 java.lang.OutOfMemoryError: PermGen

您可以使用 jvm 的此参数增加这一代的空间:-XX:MaxPermSize=128m

垃圾回收期间不考虑永久代中的对象。查看sun 的此页面,了解有关垃圾收集和 JVM 中不同代对象的更多信息。

于 2008-10-22T09:41:59.067 回答
2

试试 SAP 内存分析器。

https://www.sdn.sap.com/irj/sdn/wiki?path=/display/Java/Java+Memory+Analysis

这会读入转储文件并让您调查占用内存的内容。

于 2008-10-20T01:28:17.723 回答
2

出色地...

长期使用 Java 和数据库( postgresSQL mysql oracle 差异示例>)的经验告诉我,我们在做这项工作时使用的 JDBC 驱动程序经常会出现问题。

我有一段代码需要 24/7 保持连接到数据库,并且由于驱动程序内存泄漏,JVM 总是会在某个时候阻塞。因此,我编写了代码来捕获抛出的特定异常,然后采取越来越激烈的措施,包括断开连接并重新连接,甚至在绝望中重新启动 JVM,没有任何工作可以解决问题。编写它是多么痛苦,但它一直有效,直到 DBMS 供应商推出了一个不会导致问题的新 JDBC 驱动程序......我实际上只是将代码留在原处,以防万一!

...所以,这可能不是你在做什么。

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

此外,可能不清楚,但 ResultSets 保持与数据库引擎本身的持续连接,在许多情况下(除非明确设置)是双向的,即使您只是在阅读。而且,一些 JDBC 驱动程序让您请求单向连接,但撒谎并返回双向连接!小心这个!

因此,最好将 ResultSet 对象卸载到其他对象中以保存值并尽快删除 ResultSet 对象本身。

祝你好运。RTIII

于 2008-10-20T01:41:24.733 回答
0

Java 维护着几个不同的内存池,用完其中任何一个都可能导致可怕的 OutOfMermoryException。操作系统分配内存的问题也可能表现为 OOM。

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

如果您使用一个不错的分析器 - 最近 Sun Java 6 JDK 附带的 JVisualVM 可能就足够了 - 您可以查看所有各种池并查看哪些池已用完。

于 2008-10-22T09:29:09.407 回答
0

您的结果集中有多少项?如果有足够的记录,那么您将用完所有内存,因为在这种情况下没有任何垃圾收集,因为您正在对编写器执行 addDocument,它将保存对您正在创建的所有文档的引用。

于 2008-10-20T01:38:30.213 回答