【问题标题】:java memory managementjava内存管理
【发布时间】:2017-08-15 13:39:50
【问题描述】:

我是一名 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 个字段,大多数都是短字符串(

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

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

【问题讨论】:

    标签: java memory-leaks


    【解决方案1】:

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

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

    【讨论】:

    • GC 没有循环引用的问题,对象仍然会被删除。我不确定我是否理解您明确取消对对象的引用是什么意思。你还会如何删除它们?
    • 删除它们的另一种方法是让它们超出范围。将引用显式设置为 null 是为了结合庞大的对象和长循环,其中对象引用将在内存中保存很长时间。
    【解决方案2】:

    试试 SAP 内存分析器。

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

    这会读入转储文件并让您调查是什么占用了内存。

    【讨论】:

    • 你的链接没有把我带到它。请修复链接!
    【解决方案3】:

    嗯……

    在 Java 和数据库方面的长期经验(an example postpostgresSQL mysql oracle 差异>)告诉我,我们在执行这项工作时使用的 JDBC 驱动程序经常会出现问题。

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

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

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

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

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

    祝你好运。 RTIII

    【讨论】:

      【解决方案4】:

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

      你可以通过jvm的这个参数来增加这一代的空间:-XX:MaxPermSize=128m

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-04-28
            • 1970-01-01
            • 1970-01-01
            • 2017-04-28
            • 2018-08-17
            • 2012-08-08
            相关资源
            最近更新 更多