使垃圾收集器放弃更快

时间:2017-08-26 10:40:12

标签: java memory garbage-collection

我正在考虑用Java编写一个会反复尝试计算的程序,注意它耗尽内存,改变计算,重试直到成功。 (内存耗尽是不可避免的;粗略地说,我想到的就像遗传编程一样,你不能总是预先知道生成的程序是否会耗尽内存。)所以看看主循环是否会出现问题内存错误。

以下简单的测试程序:

public static void main(String[] args) {
    HashMap<String, Integer> hashMap = new HashMap<>();
    for (int i = 0; i < 1000000000; i++) {
        hashMap.put(Integer.toBinaryString(i), i);
    }
    System.out.println(hashMap.size());
}

内存耗尽并按预期退出未捕获的异常,但大约需要20分钟,大部分时间似乎都是由垃圾收集器花费大量精力来寻找足够的内存来继续运行。

如何告诉垃圾收集器我预计会耗尽内存并且它应该早点放弃?

3 个答案:

答案 0 :(得分:1)

我不知道某个特定方法通知垃圾收集器提前通知内存不足

但是一个下面的棘手问题可以立即通知你内存不足错误

为HashMap定义 initialCapacity ,在运行时会出现内存不足错误

HashMap<String, Integer> hashMap = new HashMap<>(1000000000); // define initialCapacity
            for (int i = 0; i < 1000000000; i++) {
                  hashMap.put(Integer.toBinaryString(i), i);
            }
            System.out.println(hashMap.size());

答案 1 :(得分:1)

您可以计算所需的内存量,并将其与Runtime.getRuntime().freeMemory()进行比较,以确定您的算法是否有足够的可用内存。要计算所需的记忆,你必须考虑:

  • 作为Raju说的HashMap的容量必须传递给HashMap构造函数以防止生成垃圾
  • 样本中二进制字符串的平均大小约为29个字符或58个字节
  • 每件商品Integer的尺寸
  • 参考任何Object的尺寸,包括StringsIntegers

例如,以下代码需要大约75 GB的内存:

    HashMap<String, Integer> hashMap = new HashMap<>(1000000000);
    for (int i = 0; i < 1000000000; i++) {
        hashMap.put(Integer.toBinaryString(i), i);
    }
    System.out.println(hashMap.size());

此外,如果您动态更改Map的大小并且未确定其近似容量,则不应使用HashMap,因为其大小调整会产生垃圾,并且您可能会在调整期间进行多次调整大小你的循环。相反,您应该使用另一个Map TreeMap,而不会产生任何垃圾。

答案 2 :(得分:1)

不要这样做。不要让JVM抛出OOME而不要让它接近它,因为它总是浪费时间。

不要等待OOME,而是定期或在循环中询问Runtime.getRuntime().freeMemory()并在你太接近零之前拯救出来(这需要一些实验)。

不知道调用freeMemory()的开销是多少,但是如果你需要一个紧凑的循环,你可以总是这样做

if (i % 1000 == 0) {
    ... do the test
}

(或使用i << 22 == 0作为模数1024的更快的测试。)

我可能会选择一个计时器。您还可以使用GarbageCollectorMXBean来确定在GC中花费的时间(它不完全是用户友好的)。

我不会尝试计算预期的内存使用量,因为这很难,并且当代码更改时可能很容易破坏。对地图和类似技巧进行预设肯定是有用的,但不要因为增益有限而不要太努力。考虑改进算法和/或使用更紧凑的数据结构 - 这也很难但可能会获得更多。

  

我想告诉垃圾收集器,当内存达到90%时 - 事先无法预测但事情发生时很容易被GC测量 - 它应该放弃

当然,只是不要打扰GC,自己测量记忆并拯救。