【问题标题】:JVM Tunning of Java ClassJava类的JVM调优
【发布时间】:2014-04-25 14:48:28
【问题描述】:

我的 java 类读入一个 60MB 的文件,并产生一个 HashMap 和一个 HashMap 超过 3 亿条记录。

HashMap<Integer, HashMap<Integer, Double>> pairWise =
                             new HashMap<Integer, HashMap<Integer, Double>>();

我已经将 VM 参数调整为:

-Xms512M -Xmx2048M

但系统仍然适用:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.createEntry(HashMap.java:869)
    at java.util.HashMap.addEntry(HashMap.java:856)
    at java.util.HashMap.put(HashMap.java:484)
    at com.Kaggle.baseline.BaselineNew.createSimMap(BaselineNew.java:70)
    at com.Kaggle.baseline.BaselineNew.<init>(BaselineNew.java:25)
    at com.Kaggle.baseline.BaselineNew.main(BaselineNew.java:315)

在不因 OOME 失败的情况下运行需要多大的堆?

【问题讨论】:

  • 尝试构建 300mio x 300mio 相似度矩阵是一个非常糟糕的主意,即使您有 2gb 的堆。
  • 使用简化的测试,我测量了 Map.Entry&lt;Integer, Double&gt; 的 90 个字节,因此 3 亿个条目至少需要 25GB(可能比这还要多,因为您还会有一堆 Map.Entry&lt;Integer, HashMap&lt;...&gt;&gt; 和内存中的其他东西)

标签: java jvm heap-memory jvm-arguments


【解决方案1】:

您的数据集非常大,无法在内存中进行处理,这不是最终解决方案,只是一种优化。

您正在使用盒装原语,这是一件非常痛苦的事情。 根据this question,装箱整数可以比未装箱整数大 20 个字节。这不是我所说的内存效率

您可以使用专门的集合来优化这一点,这些集合不会将原始值装箱。提供这些的一个项目是Trove。您可以使用TIntDoubleMap 代替HashMap&lt;Integer, Double&gt;TIntObjectHashMap 代替HashMap&lt;Integer, …&gt;

因此你的类型应该是这样的:

TIntObjectHashMap<TIntDoubleHashMap> pairWise =
                         new TIntObjectHashMap<TIntDoubleHashMap>();

现在,算一算吧。

300.000.000 Doubles,每个 24 字节,使用 7.200.000.000 字节的内存,即 7.2 GB。
如果存储 300.000.000 doubles,每个占用 4 个字节,则只需要 1.200.000.000 个字节,即 1.2 GB。
恭喜,您节省了大约 83% 的之前用于存储号码的内存!

请注意,这个计算是粗略的,取决于平台和实现,并且不考虑用于 HashMap/T*Maps 的内存

【讨论】:

  • 谢谢,这似乎是一个很好的解决方案。我下载了jar。但它似乎无法作为其他 jar 库导入?你知道怎么做吗?
  • 我不确定我是否理解您的意图,我也不知道您在什么环境中开发。我假设是 Eclipse 或 IntelliJ,在这两种环境中它应该可以直接工作。如果没有更多信息,我无法为您提供帮助。
  • 很抱歉给您带来了困惑。我使用 Eclipse。 trove-3.0.3-src.jar 的 jar 文件像往常一样从构建路径导入到项目中。创建 TIntObjectHashMap 时,没有要导入的包...
  • 在那里下载this,解压,cd,调用ant,生成需要的文件。您现在可以继续打包或复制这些。
  • 哦,看来您也可以调用ant dist 并从/output/dist/…/trove-…-src.jar 复制生成的.jar
【解决方案2】:

您的数据集足够大,不可能一次将所有数据都保存在内存中。

考虑将数据存储在数据库中并加载部分数据集以执行操作。

编辑:我的假设是您将对数据进行不止一次传递。如果您所做的只是加载它并对每个项目执行一个操作,那么 Lex Webb 的建议(下面的评论)是比数据库更好的解决方案。如果您对每个项目执行多个操作,那么数据库似乎是一个更好的解决方案。数据库不需要是 SQL 数据库,如果您的数据是面向记录的,那么 NoSQL 数据库可能更合适。

【讨论】:

  • 或者只是不要一次读取整个文件,如果可能,逐位读取和处理信息。
【解决方案3】:

您对此卷的数据使用了错误的数据结构。 Java 为它创建的每个对象增加了显着的内存和时间开销——在 3 亿个对象级别上,您会看到大量开销。您应该考虑将这些数据留在文件中并使用随机访问技术来解决它 -- take a look at memory mapped files using nio

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-11
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    相关资源
    最近更新 更多