Java 使用历史记录映射在使用Streams API时生成垃圾

Java 使用历史记录映射在使用Streams API时生成垃圾,java,stream,garbage-collection,chronicle,chronicle-map,Java,Stream,Garbage Collection,Chronicle,Chronicle Map,今天我在试验编年史地图。下面是一个代码示例: 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

今天我在试验编年史地图。下面是一个代码示例:

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());
        }
    }
}
封装实验;
导入net.openhft.chronicle.core.values.IntValue;
导入net.openhft.chronicle.map.chronicmap;
导入net.openhft.chronicle.values.values;
公共级Tmp{
公共静态void main(字符串[]args){
try(编年史地图用户=编年史地图)
.of(IntValue.class,User.class)
.名称(“用户”)
.参赛作品(10万)
.create();){
User User=Values.newHeapInstance(User.class);
IntValue id=Values.newHeapInstance(IntValue.class);
对于(int i=1;i<100_000;i++){
user.setId(i);
user.setBalance(Math.random()*1_000_000);
id.setValue(i);
users.put(id,user);
如果(i%100==0){
System.out.println(i+“)+
users.values()
.stream()
.max(用户::比较)
.map(用户::getBalance)
.get());
}
}
}
}
公共界面用户扩展{
int getId();
void setId(int id);
双getBalance();
无效余额(双倍余额);
@凌驾
默认整数比较(用户其他){
返回Double.compare(getBalance(),other.getBalance());
}
}
}
正如您在上面的代码中看到的,我只是创建用户对象并将其放入历史地图中,在每个第100条记录之后,我只是用最大余额打印用户。但不幸的是,它产生了一些垃圾。当我用VisualVM监控它时,我得到了以下信息:

在编年史地图中使用流似乎无论如何都会产生垃圾

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

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

您可以通过调用
map.segments()
来检查有多少段。您还可以在编年史地图构建阶段对其进行配置

因此,在迭代过程中,您应该期望定期将大约
numEntries/numSegments
条目一次转储到内存中,其中numEntries是您的历史记录映射的大小

您可以通过段上下文API重用对象,在历史记录映射上实现流式处理,从而避免创建大量垃圾:

    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];
              }
            });
        }
    }
User[]maxUser=新用户[1];
对于(int i=0;i{
User=e.value().get();
if(maxUser[0]==null | | user.compareTo(maxUser[0])>0){
//请注意,您不能只分配`maxUser[0]=user`:
//该对象稍后将被SegmentContext重用
//在迭代中,它的内容将被重写。
//检查文档中的数据。get()。
if(maxUser[0]==null){
maxUser[0]=Values.newHeapInstance(User.class);
}
User newMaxUser=e.value().getUsing(maxUser[0]);
//断言确实重用了该对象
断言newMaxUser==maxUser[0];
}
});
}
}
链接到文档以获取更多信息


上述示例的代码改编自。

我根据您的代码片段修改了我的代码,它创建了太多的
CompiledMapIterationContext$$Lambda$$
对象,但是没有调用GC,甚至堆大小也在增长。它在18分钟后调用了GC。这应该是一个缺陷,请随时提交,行
User=e.value().get()产生垃圾?我刚刚通过
double max=-1解决了这个问题;max=Math.max(max,e.value().getUsing(maxUser[0]).getBalance());返回最大值,即获得双倍的最大余额;是的,这是可能的,但是它应该被视为缺陷。