【问题标题】:optimization on large volume of objects对大量对象进行优化
【发布时间】:2018-08-18 04:14:46
【问题描述】:

我有一个从 Oracle DB 获取 ResultSet 的进程,然后使用返回的数据创建对象,然后将对象添加到 Map 并最终将映射加载到内存数据网格中。我的最大结果集可能有 3000 万条记录。我注意到在处理了大约 1400 万条记录后,应用程序的速度显着降低。我有 -Xmx=12G 和 -Xms=512M。我的应用程序部署在 WebLogic 版本 12.2.0.1 和 Java 版本 8_66 上。我还注意到托管服务器产生的这条消息:

  1. JVM 暂停时间可能过长:

我试图弄清楚如何优化我的代码或 JVM 配置。这是代码

    MyObj myObj = null;

    while (rs.next()) {
        myObj = new Balance(rs.getString("Field1"), rs.getString("Field2"), rs.getString("Field3"),
                                   ....
        rs.getString("Field17"), rs.getString("Field18"), rs.getString("Field19"));

        Map<String, Account> myMap = new HashMap<>();
        myMap.put(rs.getString("FieldA"), new Account(rs.getString("FieldA"), rs.getDouble("FieldC"),
                rs.getString("FieldD"), Boolean.FALSE, Boolean.FALSE));
        myObj.setAccounts(myMap);

        myKey = myObj.getKey();

        existingObject = cacheMap.get(myKey);

        if (existingObject != null) {
            myObj = myObj.merge(existingObject);
        }

        cacheMap.put(myKey, myObj);
        recCount++;

        if (recCount % 250000 == 0) {
            logger.info("Processed " + recCount + " records.");
        }
    }

【问题讨论】:

  • 您是否有特定的原因需要内存中的如此大的地图?更好的策略是仅在需要时获取所需的东西。
  • 这真是一个设计问题。看起来 12G 对你的程序来说是不够的,所以当垃圾收集器试图释放未使用的对象时,它卡住了,因为大多数对象仍在使用中。无论如何,如果重构为时已晚 - 您可以重新组织循环。我认为你可以改变你的merge 策略。 IE。首先在缓存映射中检查该对象,然后,如果存在,则将其他实体添加到现有映射中,而不是构建新映射并将其与现有映射合并。
  • 我建议使用分析器来检查运行时发生的情况。只看代码,我猜你可能的痛点是:rs#nextmyMap#putmyObjsetAccountsmyObj#mergecacheMap#put

标签: java performance optimization


【解决方案1】:

您可以使用分页并以块的形式检索数据集,然后您的程序可以在检索下一个数据块的同时开始处理当前数据块等等。以这种方式检索结果集将花费更少的时间。这是一篇关于mySql中分页的好文章https://www.xarg.org/2011/10/optimized-pagination-using-mysql/

【讨论】:

  • 我已经在使用 Statement.setFetchSize 并找到了最佳位置。这一定是别的东西
【解决方案2】:

选项:1

如果您的某些字段值与String 相同,则在创建BalanceAccount 时,请执行String.intern()。如果存在冗余值,这将在内存占用方面带来巨大的好处。

在我们的一个数据为 2 lacs 的应用程序中,String.intern() 之后我们看到了 50% 的收益。

代码可能看起来有点难看。但是,通过一些实用方法,您可以减少丑陋。

代码在这里

String str(ResultSet rs, String fieldName)
{
    String s = rs.getString(fieldName);
    return s == null ? null : s.intern();
}

void somemethod()
{
...
...

    while (rs.next()) {
        myObj = new Balance(str(rs, "Field1"), str(rs, "Field2"), str(rs, "Field3"), str(rs, "Field17"), str(rs, "Field18"), str(rs, "Field19"));

        Map<String, Account> myMap = new HashMap<>();
        myMap.put(str(rs, "FieldA"), new Account(str(rs, "FieldA"), str(rs, "FieldC"), str(rs, "FieldD"), Boolean.FALSE, Boolean.FALSE));
        myObj.setAccounts(myMap);

...
...
    }
...
}

选项:2

这真的是一个节省内存的选项...!

如果您使用 Java 8 Update 20 或更高版本,则可以通过启用标志 XX:+UseStringDeduplication 为整个 JVM 启用此行为。如果您使用G1 GC,这将起作用

一些参考here。您可以搜索XX:+UseStringDeduplication 以获取更多参考资料

选项:3

你真的可以想想你在merge() 操作中做了什么,看看是否有任何功能变化来降低内存需求......

【讨论】:

猜你喜欢
  • 2019-08-23
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 2020-04-12
  • 1970-01-01
  • 2013-06-25
  • 1970-01-01
相关资源
最近更新 更多