ArrayList动态增长方式
ensureCapacityInternal-> ensureExplicitCapacity->grow-> hugeCapacity
- ensureCapacityInternal:判断数组是否非空,若为空,比较DEFAULT_CAPACITY(ArrayList默认容量为10)与minCapacity设最大值为容量值
- ensureExplicitCapacity:modCount++,若minCapacity大与数组长度时调用grow扩容
- grow扩容原数组长度50%,与minCapacity比较取最大值,若最终取值大与MAX_ARRAY_SIZE,调用hugeCapacity最大容量函数扩容
注:上两步的比较均采用相减与0比较的方式,针对最大值溢出Integer.MAX_VALUE为负的情况 - hugeCapacity判断容量值是否<0(若int值大与Integer.MAX_VALUE,则内存溢出),是则抛出OOM异常。设最终容量为(minCapacity >MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
注:试图分配更大数组时可能导致OutOfMemoryError:被请求数组的size超出VM界限 - grow调用Arrays.copyOf()复制数组
总结:
分配次数
1千需要分配 11次
1万一级需要分配17次
10万 需要分配23次
100万需要分配28次
因扩容为自增0.5倍,为避免不必要的内存泄漏,应该考虑用ensureCapacity方法
申请扩容(大与DEFAULT_CAPACITY)的四种结果:
1. 原容量的1.5倍
2. 用户自定义minCapacity值
3. MAX_ARRAY_SIZE(等于Integer.MAX_VALUE-8)
4. Integer.MAX_VALUE
补充相关小知识点:
1.前面grow方法中为什么要采用相减与0比较的方式
public static void main(String[] args) {
// Integer的取值范围-2147483648 ~ 2147483647 (-2^31 ~ 2^31-1)
int i = Integer.MAX_VALUE + 1;
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
System.out.println(i > max-8);// false
System.out.println(i - max > 0);// true
System.out.println(min == i);// true
System.out.println(min == i);// true
arraytest();
}
2.Arrays.copyof(···)与System.arraycopy(···)方法比较
//Arrays.copyof方法
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)//判断是否为Object类型
? (T[]) new Object[newLength]//若为Object类型生成一个Object数组
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
//否则生成一个newType数组类型的数组
//Class.getComponentType():返回表示数组组件类型的 Class。如果此类不表示数组类,则此方法返回 null。
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
//System.arraycopy方法
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
总结:
System.arraycopy可以指定复制目标数组与起始复制位置。嗯~思考一下作用:可以实现向数组中部插入删除耶~
而Arrays.copyof只是返回一个新数组,只能复制原数组从0到newLength的值,内部调用System.arraycopy实现
3.关于MAX_ARRAY_SIZE
/**
* 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;
这个为什么要取一个Integer.MAX_VALUE - 8呢?
- 原注释提到说这块区域是为了存放一些头数据
- 其实也是为了避免一些机器内存溢出,-8 是为了减少出错的几率
- 数组的长度太大了,会导致JVM内存溢出,所以当你的内存足够大时,数组的扩容接近内存的最大值时,数组不会直接扩满,还会留一些空间(8这一个字节),保证这时候JVM还能有些内存空间去做GC,清除其他不用的内存来缓冲
关于这个问题的答案,保留个人想法,欢迎大家指正错误