代码之家  ›  专栏  ›  技术社区  ›  pjj

如果GC'ed是弱可达对象,那么为什么会出现OOM错误

  •  -2
  • pjj  · 技术社区  · 6 年前

    我的理解是,弱可访问对象在下一个GC周期中被垃圾收集,如果这是真的,那么对于下面的代码,我永远不会得到OutOfMemoryError,但在运行10-15分钟后,我会得到OOM错误。

        List<Object> list = new ArrayList<>();
        while (true) {
            for(int i = 0; i < 1000000; i++){
                list.add(new WeakReference<Object>(Calendar.getInstance()));
            }
        }
    

    有人能解释一下为什么我仍然得到OOM,我知道列表的大小会不断增加,但我这里的重点是我正在使用日历对象创建 Calendar.getInstance() 将在 1000000 迭代,所以如果我没有得到OOM错误 1000000 迭代之后,我不应该为任何后续循环获取它,因为所有以前的日历对象都会在下一个循环中进行GC'ed?

    2 回复  |  直到 6 年前
        1
  •  3
  •   Stephen C    6 年前

    这个 ArrayList 类使用简单的策略来管理空间。当列表的后备数组已满且您添加了另一个元素时 阵列列表 分配一个较大的数组,并将元素从旧数组复制到新数组。新阵列的大小将比旧阵列大50%,达到阵列大小的架构限制。以下是Java 8实现:

         private void grow(int minCapacity) {
             // overflow-conscious code
             int oldCapacity = elementData.length;
             int newCapacity = oldCapacity + (oldCapacity >> 1);
             if (newCapacity - minCapacity < 0)
                 newCapacity = minCapacity;
             if (newCapacity - MAX_ARRAY_SIZE > 0)
                 newCapacity = hugeCapacity(minCapacity);
             // minCapacity is usually close to size, so this is a win:
             elementData = Arrays.copyOf(elementData, newCapacity);
         }
    

    在您的示例中,您反复向列表中添加元素,这导致列表的备份数组不断增长。

    接下来,a Reference 类有4个字段(在Java 8中),其中一个字段是 referenent 。。。它指向的对象是 参考 。当 参考 坏了,发生的是 referent 设置为 null ,从而允许对目标对象进行GC(通常在下一个GC循环中……但不一定)。但是 参考 对象本身未被释放。。。除非你让它变得遥不可及。

    在您的示例中,您没有做任何事情来 WeakReference 无法访问的对象。相反,它们正在列表中累积。

    最终,列表和(断开的)消耗的内存 弱引用 它所持有的对象将填满堆,你会得到一个OOME。(可能会在 Arrays.copyOf 呼叫但在分配 弱引用 或a Calendar )


    如果您希望列表实际释放空间以响应内存压力,那么需要检测并删除损坏的 弱引用 列表中的对象。这不是小事。

    • 能够 让每个人排队 弱引用 在a中使用 ReferenceQueue ,并让队列处理器删除每个 弱引用 因为它坏了。然而,这将是低效的,因为列表删除是O(N)。

    • 另一个想法是让队列处理器增加一个专用计数器,并让 add 方法使用计数器来决定何时扫描和删除断开的引用。

    • 你也应该考虑打电话 ArrayList::trimToSize 。。。或同等产品。。。减少后备存储区大小。但请记住,这会暂时提高内存利用率。

    这可能需要自定义 List 实施

        2
  •  0
  •   user158037    6 年前

    列表和WeakReference都会在代码中单独导致OOM,即使所有日历实例都是由GC收集的。