Java 中一个保存了 1 万个整数的 ArrayList 占多少内存?

2023-11-12 10:59:52 +08:00
 61162833
代码:
List<Integer> ints=new ArrayList();
for(int i=0;i<10000;i++){
ints.add(i);
}

gpt-3.5 的回答:
在 64 位的 Java 虚拟机中,一个空的 ArrayList 对象通常占用 24 字节。
在 Java 中,一个 Integer 对象通常占用 16 字节(在 32 位和 64 位的 Java 虚拟机中都是如此)。

在 64 位的 Java 虚拟机中:
ArrayList 对象本身:24 字节
整数对象:16 字节 × 10000 = 160000 字节
总内存占用:24 字节 + 160000 字节 = 160024 字节

也就是说,一个仅保存了 1 万个整数的 ArrayList 就要占 160KB 服务器内存?
一个保存了 10 万个整数的 ArrayList 就要占 1.6MB 内存?

怎么与实际感觉似乎不太相符,
有能用 gpt 4.0 的富哥问下这答案对吗?

另外,ArrayList 中保存的整数占内存数与数字大小有关系吗?(比如保存 1 万个 1 亿+的数字,与保存 1-10000 有区别吗?)
4476 次点击
所在节点    Java
16 条回复
cubecube
2023-11-12 11:20:20 +08:00
gpt 回答是正确的
和数字大小没关系,int 最大 21 亿
yfugibr
2023-11-12 11:24:28 +08:00
不止,ArrayList 的空间分配不是要多少就分配多少,而是在容量填满后扩充到之前的大约 1.5 倍(可以去看具体实现),你存 a 个对象,但它分配的空间可能多达 1.5a 个
61162833
2023-11-12 11:25:23 +08:00
@cubecube 谢谢,这么一算感觉内存很不经用啊,保存几十万个数字就要爆了
61162833
2023-11-12 11:26:22 +08:00
@yfugibr 就是保存 100 个数字要占 150 个数字的内存空间?
rabbbit
2023-11-12 11:27:57 +08:00
ArrayList 可以设定初始容量值,避免自动扩容。
int 占 4 字节
嫌多还有 short 2 字节 byte 1 字节
humpy
2023-11-12 11:32:11 +08:00
可以用 [JOL]( https://github.com/openjdk/jol) 工具测一下:

System.out.println(GraphLayout.parseInstance(ints).toFootprint());

---

java.util.ArrayList@5679c6c6d footprint:
COUNT AVG SUM DESCRIPTION
1 40016 40016 [Ljava.lang.Object;
10000 16 160000 java.lang.Integer
1 24 24 java.util.ArrayList
10002 200040 (total)

在我的机器上( 64 位 JDK8 ),大约 200040 / 1024 = 195kb
yeqizhang
2023-11-12 11:33:07 +08:00
没啥不相符合的,首先高并发的项目,出现大集合的情况少,其次集群下,单台机的业务并发也不会太高,有高并发的业务都堆机器堆内存去了。
还有就是,只要变成垃圾变的快,jvm 回收了也没啥压力。
yfugibr
2023-11-12 11:33:51 +08:00
@61162833 #4 不是这个意思,ArrayList 底层存储是数组,有一个初始容量(不确定是多少),会在数组被填满时建一个新数组替换旧数组来扩充容量,新数组的容量大约是之前的 1.5 倍(取决于具体实现),直到新数组再次被填满时再次扩容,所以多数情况下都是有空间“浪费”的,但是浪费了多少要看你的存入的容量和 ArrayList 的实现。

当然你也可以直接指定 ArrayList 的容量,这部分就自行搜索吧。
mazyi
2023-11-12 12:32:58 +08:00
这有啥不经用的,你可以去看看 python 的,和 js 的,让你对编程语言有全新认识
ruxuan1306
2023-11-12 12:35:52 +08:00
@yfugibr 自动扩容是在 append 时原容量不够才发生的,new 时候给定大小,然后直接用下标设置就不会触发扩容。
winglight2016
2023-11-12 13:23:28 +08:00
lz 如果觉得内存很容易不够用,可以去看看微机原理这本书,或者了解一下 linux 内核的内存寻址相关内容。

jvm32 位的时候,最大内存只有 1.5G ,的确很容易爆,但是 64 位已经支持到超过硬盘容量了,更大数量的并行计算(如果不需要并行,那限制只是时间问题)已经转变成了分布式计算的问题,而不是高效利用内存——这和 AI 的发展趋势相似,注意力放在了解决通用问题。
junkun
2023-11-12 14:31:58 +08:00
补充一下,如果要省内存可以用 int[]数组,或者自己用数组封装一个 int 类型的线性表。主要是因为 Java 泛型只能支持封装类型的元素,所以占用空间不理想。然而基本类型的数组除了头以外,内部是连续存储的。
lancelee01
2023-11-13 10:30:58 +08:00
10 万个整数,1.6MB 说实话不大啊。再优化也就是把 List 改成数组,数组本身占用 16 字节,节省 8 字节,整数 int 4 字节,累计 16 + 4 * 100000 ,相当于减少一半
zhouhu
2023-11-13 11:14:10 +08:00
@junkun 是的,很多开源的集合库可以用。比如 eclipse collection 。
Aresxue
2023-11-13 11:17:24 +08:00
所以有了 fastutil 这个库。。话说 java 自己的 Valhalla 项目正在做泛型特化以后你就可以用 List<int>了。
zhouhu
2023-11-13 11:19:37 +08:00
还可以开启指针压缩

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/991104

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX