在使用 Streams 时使用 Chronicle Map 产生垃圾 API
Using Chronicle Map producing garbage while using Streams API
今天我正在试验编年史地图。这是一个代码示例:
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 监视它时,我得到以下信息:
在 Chronicle Map 中使用流似乎无论如何都会产生垃圾。
所以我的问题是:
* 这是否意味着我不应该将 Streams API 与 Chronicle Map 一起使用。
* 还有其他 solutions/ways 这样做吗?
* 如何以正确的方式 filter/search Chronicle Map 因为我有其他用例
里面只有 putting/getting 个数据。
ChronicleMap
的 entrySet().iterator()
(以及 keySet()
和 values()
上的迭代器)已实现,以便它转储 Chronicle Map 的 [=33] 中的所有对象=]在遍历它们之前将分段到内存中。
您可以通过调用map.segments()
查看您有多少段。您也可以在 ChronicleMap 构建阶段配置它,查看 ChronicleMapBuilder javadoc.
因此,在迭代期间,您应该期望定期将大约 numEntries / numSegments
个条目转储到内存中 一次 ,其中 numEntries 是您的 Chronicle Map 的大小。
您可以通过 Segment Context 重用对象,在 Chronicle Map 上实现流式处理,避免产生大量垃圾 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];
}
});
}
}
Link 记录 Data.get()
.
上面例子的代码改编自here.
今天我正在试验编年史地图。这是一个代码示例:
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 监视它时,我得到以下信息:
在 Chronicle Map 中使用流似乎无论如何都会产生垃圾。
所以我的问题是:
* 这是否意味着我不应该将 Streams API 与 Chronicle Map 一起使用。
* 还有其他 solutions/ways 这样做吗?
* 如何以正确的方式 filter/search Chronicle Map 因为我有其他用例
里面只有 putting/getting 个数据。
ChronicleMap
的 entrySet().iterator()
(以及 keySet()
和 values()
上的迭代器)已实现,以便它转储 Chronicle Map 的 [=33] 中的所有对象=]在遍历它们之前将分段到内存中。
您可以通过调用map.segments()
查看您有多少段。您也可以在 ChronicleMap 构建阶段配置它,查看 ChronicleMapBuilder javadoc.
因此,在迭代期间,您应该期望定期将大约 numEntries / numSegments
个条目转储到内存中 一次 ,其中 numEntries 是您的 Chronicle Map 的大小。
您可以通过 Segment Context 重用对象,在 Chronicle Map 上实现流式处理,避免产生大量垃圾 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];
}
});
}
}
Link 记录 Data.get()
.
上面例子的代码改编自here.