ArrayList 扩容导致的OOM 异常

本篇特别有意思,看完后收获不菲。

我们先来思考一个问题,new Object[1] 到底占用多少字节内存?

一个object大概占用8字节,我猜想也是占用8字节,下面我来验证下。首先设置JVM参数最大内存100M。

-Xmx100M -Xms100M

然后我先打印当前剩余可用内存

System.err.println( Runtime.getRuntime().freeMemory());
// 打印结果 大概99M ,与我们的 最大设置内存相符合
99598304

如果按照一个Object占用8字节来算,上面99598304字节最多容纳 :

99598304 / 8 =12449788 个Object 。

于是我们这样:

Object[] obj = new Object[12449788];
System.err.println("完美构建");

发现程序没问题, 但是我加到对象为 18449788 个,就抛出异常了,简单得出了 new Object[1] 占用大概8字节。

于是我们 找到了 12449788 这个值,刚好内存不溢出。然后下面我们就跟ArrayList扯上关系了。

在看下面一段代码

ArrayList<Byte> list = new ArrayList<>();
byte g = "1".getBytes()[0];
for(int i=0;i<12449788;i++){
    list.add(g);
}
System.err.println("end 完美走完");

灵魂拷问, 请问这个代码会打印出 "end 完美走完" 吗???????

答案是打不出的

但是我调整到程序为这样:

不要觉得神奇,这两者的差别就在于

  • 直接指定ArrayList大小时,导致add 没有进行扩容,于是内存刚好适合运行。
  • 不指定ArrayList大小,导致add进行扩容,但是扩容时会需要额外的内存,于是报了OOM了。


总结

在使用ArrayList的时候,尽量对其进行容量大小的初始化,避免其频繁扩容,造成OOM异常,线上出现该问题真的很恐怖。


强烈推荐一个 进阶 JAVA架构师 的博客

Java架构师修炼

本文完~

支付宝打赏 微信打赏

如果文章对您有帮助,您可以鼓励一下作者