2

今天我在尝试编年史地图。这是一个代码示例:

package experimental;

import net.openhft.chronicle.core.values.IntValue;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.values.Values;

public class Tmp {

    public static void main(String[] args) {

        try (ChronicleMap<IntValue, User> users = ChronicleMap
                .of(IntValue.class, User.class)
                .name("users")
                .entries(100_000_000)
                .create();) {

            User user = Values.newHeapInstance(User.class);
            IntValue id = Values.newHeapInstance(IntValue.class);

            for (int i = 1; i < 100_000_000; i++) {

                user.setId(i);
                user.setBalance(Math.random() * 1_000_000);

                id.setValue(i);
                users.put(id, user);

                if (i % 100 == 0) {
                    System.out.println(i + ". " +
                            users.values()
                                    .stream()
                                    .max(User::compareTo)
                                    .map(User::getBalance)
                                    .get());
                }
            }
        }
    }

    public interface User extends Comparable<User> {

        int getId();
        void setId(int id);
        double getBalance();
        void setBalance(double balance);

        @Override
        default int compareTo(User other) {
            return Double.compare(getBalance(), other.getBalance());
        }
    }
}

正如您在上面的代码中看到的,我只是创建用户对象并将其放入 Chronicle Map,并且在每 100 条记录之后,我只是打印具有最大余额的用户。但不幸的是,它正在产生一些垃圾。当我用 VisualVM 监控它时,我得到了以下信息:

VisualVM 截图

似乎在 Chronicle Map 中使用流无论如何都会产生垃圾。

所以我的问题是:
* 这是否意味着我不应该将 Streams API 与 Chronicle Map 一起使用。
*还有其他解决方案/方法吗?
*如何以适当的方式过滤/搜索编年史地图,因为我有一些用例,而不仅仅是在其中放置/获取数据。

4

1 回答 1

3

ChronicleMapentrySet().iterator()(以及和上的迭代器keySet()values()被实现,以便在迭代它们之前将编年史地图段中的所有对象转储到内存中。

您可以通过调用来检查您有多少段map.segments()。您也可以在 ChronicleMap 构建阶段对其进行配置,请查看ChronicleMapBuilder javadoc

因此,在迭代期间,您应该期望定期、大约一次numEntries / numSegments将条目转储到内存,其中 numEntries 是 Chronicle Map 的大小。

您可以通过 Segment Context API 重用对象,在 Chronicle Map 上实现流式处理,避免产生大量垃圾:

    User[] maxUser = new User[1];
    for (int i = 0; i < users.segments(); i++) {
        try (MapSegmentContext<IntValue, User, ?> c = map.segmentContext(i)) {
            c.forEachSegmentEntry((MapEntry<IntValue, User> e) -> {
              User user = e.value().get();
              if (maxUser[0] == null || user.compareTo(maxUser[0]) > 0) {
                // Note that you cannot just assign `maxUser[0] = user`:
                // this object will be reused by the SegmentContext later
                // in the iteration, and it's contents will be rewritten.
                // Check out the doc for Data.get().
                if (maxUser[0] == null) {
                  maxUser[0] = Values.newHeapInstance(User.class);
                }
                User newMaxUser = e.value().getUsing(maxUser[0]);
                // assert the object is indeed reused
                assert newMaxUser == maxUser[0];
              }
            });
        }
    }

文档的链接Data.get()

上面例子的代码改编自这里

于 2019-12-05T16:56:49.767 回答