gpt4 book ai didi

java - 从 map 存储条目是否安全?它会导致内存泄漏吗?

转载 作者:行者123 更新时间:2023-12-01 19:56:46 27 4
gpt4 key购买 nike

我遇到了这段代码(用虚拟数据改编):

public Map<String, Integer> queryDatabase() {
final Map<String, Integer> map = new TreeMap<>();
map.put("one", 1);
map.put("two", 2);
// ...
return map;
}

public Map.Entry<String, Integer> getEntry(int n) {
final Map<String, Integer> map = queryDatabase();
for (final Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue().equals(n)) return entry; // dummy check
}
return null;
}
Entry然后存储到一个新创建的对象中,该对象在未定义的时间段内保存到缓存中:
class DataBundle {
Map.Entry<String, Integer> entry;

public void doAction() {
this.entry = Application.getEntry(2);
}
}

queryDatabase在一分钟内被多次调用,本地 Maps 应该在随后的 gc 循环中被丢弃。我有理由相信 DataBundle保持 Entry引用防止 Map完全不被收集。

此外,一个 java.util.TreeMap.Entry持有对 sibling 的多个引用:
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
// ...
}

问:是否存储 Map.Entry进入成员字段保留本地 Map实例进入内存?

最佳答案

我编写了一个基准测试应用程序,结果清楚地表明 JVM 是 无法收集本地Map实例 如果 Entry引用保持活跃。

这仅适用于 TreeMap s 虽然,原因可能是 TreeMap.Entry对它的兄弟有不同的引用。

正如@OldCurmudgeon 提到的,

you should not make any assumptions [and] if you wish to store Key-Value pairs derived from a Map.Entry then you should take copies



我相信在这一点上,如果你不知道自己在做什么,不管 Map您正在使用,持有 Map.Entry应该考虑 邪恶反模式 .

始终选择保存 Map.Entry 的副本或者直接存储key和value。

基准技术数据:

虚拟机
java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)

中央处理器
Caption           : Intel64 Family 6 Model 158 Stepping 9
DeviceID : CPU0
Manufacturer : GenuineIntel
MaxClockSpeed : 4201
Name : Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz
SocketDesignation : LGA1151

内存
Model Name                  MaxCapacity MemoryDevices
----- ---- ----------- -------------
Physical Memory Array 67108864 4

基准会做什么?
  • 主程序将(伪)查询数据库500
  • DataBundle 的新实例将在每次迭代中创建一个随机的 Map.Entry调用 getEntry(int)
  • queryDatabase将创建一个本地 TreeMap100_000每次通话的元素和getEntry只会返回一个 Map.Entry从 map 。
  • 全部 DataBundle实例将存储到 ArrayList缓存。
  • 如何DataBundle存储 Map.Entry在基准测试中会有所不同,以演示 gc履行职责的能力。
  • 100调用queryDatabase cache将被清除:这是看gc的效果在 visualvm


  • 基准 1:TreeMap 和存储 Map.Entry - CRASH
    DataBundle类(class):
    class DataBundle {
    Map.Entry<String, Integer> entry = null;
    public DataBundle(int i) {
    this.entry = Benchmark_1.getEntry(i);
    }
    }

    基准测试应用程序:
    public class Benchmark_1 {
    static final List<DataBundle> CACHE = new ArrayList<>();
    static final int MAP_SIZE = 100_000;

    public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 500; i++) {
    if (i % 100 == 0) {
    System.out.println("Clearing");
    CACHE.clear();
    }
    final DataBundle dataBundle = new DataBundle(new Random().nextInt(MAP_SIZE));
    CACHE.add(dataBundle);
    Thread.sleep(500); // to observe behavior in visualvm
    }
    }

    public static Map<String, Integer> queryDatabase() {
    final Map<String, Integer> map = new TreeMap<>();
    for (int i = 0; i < MAP_SIZE; i++) map.put(String.valueOf(i), i);
    return map;
    }
    public static Map.Entry<String, Integer> getEntry(int n) {
    final Map<String, Integer> map = queryDatabase();
    for (final Map.Entry<String, Integer> entry : map.entrySet())
    if (entry.getValue().equals(n)) return entry;
    return null;
    }
    }

    应用程序甚至无法到达第一个 100迭代(缓存清除)并抛出 java.lang.OutOfMemoryError :
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.lang.Integer.valueOf(Integer.java:832)
    at org.payloc.benchmark.Benchmark_1.queryDatabase(Benchmark_1.java:34)
    at org.payloc.benchmark.Benchmark_1.getEntry(Benchmark_1.java:38)
    at org.payloc.benchmark.DataBundle.<init>(Benchmark_1.java:11)
    at org.payloc.benchmark.Benchmark_1.main(Benchmark_1.java:26)
    Mar 22, 2018 1:06:41 PM sun.rmi.transport.tcp.TCPTransport$AcceptLoop executeAcceptLoop
    WARNING: RMI TCP Accept-0: accept loop for
    ServerSocket[addr=0.0.0.0/0.0.0.0,localport=31158] throws
    java.lang.OutOfMemoryError: Java heap space
    at java.net.NetworkInterface.getAll(Native Method)
    at java.net.NetworkInterface.getNetworkInterfaces(NetworkInterface.java:343)
    at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:86)
    at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:400)
    at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:372)
    at java.lang.Thread.run(Thread.java:748)

    *** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message can't create byte arrau at JPLISAgent.c line: 813
    visualvm图表清楚地显示了内存是如何保留的,尽管 gc在后台执行一些 Activity ,因此: 持单Entry保留整个 Map在堆中。

    Benchmark_1: Heap Memory

    Benchmark_1: GC Activity

    基准 2:HashMap 和存储 Map.Entry - PASS

    完全相同的程序,但不是 TreeMap我正在使用 HashMap .
    gc可以收集本地 Map尽管存储了 Map.Entry 的实例被保存在内存中,(如果您实际上尝试在基准测试之后打印 cache 结果,您将看到实际值)。
    visualvm图表:

    Benchmark_2: Heap Memory
    Benchmark_2: GC Activity

    应用程序不会引发任何内存错误。

    基准 3:TreeMap 并仅存储键和值(无 Map.Entry) - PASS

    仍在使用 TreeMap ,但这次,而不是 Map.Entry ,我将直接存储键和值数据。
    class DataBundle3 {
    String key;
    Integer value;

    public DataBundle3(int i) {
    Map.Entry<String, Integer> e = Benchmark_3.getEntry(i);
    this.key = e.getKey();
    this.value = e.getValue();
    }
    }

    安全的方法,因为应用程序正确到达终点, gc定期清理 map 。

    Benchmark_3: Heap Memory
    Benchmark_3: GC Activity

    基准测试 4:SoftReference 的 TreeMap 和缓存(存储 Map.Entry) - PASS

    不是最好的解决方案,但由于许多缓存系统使用 java.lang.ref.SoftReference我会用它发布一个基准。

    所以,仍然使用 TreeMap , 仍然存储 Map.Entry进入 DataBundle , 但使用 SoftReference<DataBundle> 的列表.

    所以缓存变成:
    static final List<SoftReference<DataBundle>> CACHE = new ArrayList<>();

    我通过以下方式保存对象:
    CACHE.add(new SoftReference<>(dataBundle));

    应用程序正确完成, gc可以随时免费收集 map 。
    发生这种情况是因为 SoftReference不保留其 referent (在我们的例子中是 Map.Entry )被收集。

    Benchmark_4: Heap Memory
    Benchmark_4: GC Activity

    希望这对某人有用。

    关于java - 从 map 存储条目是否安全?它会导致内存泄漏吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49424680/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com