学无先后,达者为师

网站首页 编程语言 正文

ArrayList源码中的MAX_ARRAY_SIZE

作者:hygge999 更新时间: 2022-10-11 编程语言
/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

看过 Arraylist 代码的小伙伴是不是看到这个总会有疑惑:

  • 为什么 MAX_ARRAY_SIZE 是等于 Integer.MAX_VALUE - 8,为啥不是-16或者其他数字。

我看了很多回答,他们总是把这个 十进制的8 和 java的对象头联系在一起说,我到现在都不能理解这两个怎么画等号的。

思索了很久,再结合源码的注释,我开始想是不是兄弟们把这个问题想复杂了啊。

注释清清楚楚写着:Some VMs(一些VM)在数组中会有保留字,尝试分配更大的数组可能会导致OutOfMemoryError。

所以这个 Integer.MAX_VALUE - 8 其实是为了保证这些(Some VMs)减少报错的几率(?)

这里我们打个问号,下面我们分析分析。

这个变量只在 grow() 和 hugeCapacity() 两个方法里面用到了。

先看看 grow() 方法:

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);
}

里面第二个if里面会判断 newCapacity 是否大于了 MAX_ARRAY_SIZE,大于了就调用 hugeCapacity() 方法。

再看看 hugeCapacity() 方法:

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

为什么第一个if判断 minCapacity 小于 0 就是代表溢出,minCapacity 什么时候会溢出了,就是当我们的数组已经达到 Integer 的最大值 2^31-1 的时候,他增加一个元素后, 即 minCapacity 等于 2^31 时,这个数从二进制变成十进制就是个负数,所以直接判断 minCapacity 是否小于 0 就能判断是否溢出了。

我再强调一下几个变量的意义:

  • newCapacity :预计扩容后的数组长度。
  • minCapacity : 原数组增加元素后需要的数组长度。
  • MAX_ARRAY_SIZE : Integer.MAX_VALUE - 8

看到return里面的三元表达式,再结合 grow() 方法里面的第二个if判断,思路是不是清晰起来了。

  • 首先 预计扩容后的数组长度(newCapacity) 大于了 MAX_ARRAY_SIZE
  • 再判断 原数组增加元素后需要的数组长度(minCapacity ) 是不是大于 MAX_ARRAY_SIZE
  • 如果大于了 MAX_ARRAY_SIZE,那还是会将 预计扩容后的数组长度(newCapacity) 置为 Integer.MAX_VALUE
  • 如果不大于 MAX_ARRAY_SIZE ,则将 预计扩容后的数组长度(newCapacity) 置为 MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)

你会发现他依旧可能会扩容到 Integer.MAX_VALUE,所以我们可以得知:

Arraylist 的最大容量依旧是 Integer.MAX_VALUE ,而不是 Integer.MAX_VALUE - 8。

很多人看到 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 这句代码,就说 Arraylist 的最大容量是 Integer.MAX_VALUE - 8 ,这是不对的。


到这里可能还会有人会接着问,那最后都是会到 Integer.MAX_VALUE ,那用一个 MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8) 做判断有啥意义。

所以我这里就有了文章开始的那些猜测,这个 MAX_ARRAY_SIZE 值只是为了保证这些(Some VMs)减少报错的几率的。

如果没有这个 MAX_ARRAY_SIZE 的话,这些(Some VMs)在 数组长度为 Integer.MAX_VALUE 的 2/3 左右的时候,一旦触发扩容,newCapacity 直接大于 Integer.MAX_VALUE - 8,然后报错 。

如果有了 MAX_ARRAY_SIZE 的话,那么这些(Some VMs)会在 数组长度为 Integer.MAX_VALUE - 8 的时候,再增加元素才会报错,其他VM会正常的扩容到 nteger.MAX_VALUE。

这样极大的减少了这些(Some VMs)的报错几率。并且为这些(Some VMs)增加了接近1/3的实际储存容量。


最后说明,以上绝大部分都是我的推理,我至此也没查到为啥一定是 8 这个数字,我只能想到这个十进制的8代表二进制数组的最后四位为0,除此之外我并不晓得在这里的具体含义。

望大家一起探讨探讨。

原文链接:https://blog.csdn.net/z55947810/article/details/127249701

栏目分类
最近更新