我创建了一个包含100万个MyItem对象的ArrayList,消耗的内存为106mb(从任务管理器中检查)但是通过addAll()方法将相同的列表添加到另外两个列表后,需要259mb。我的问题是我只添加了对list的引用,在100万之后没有创建新对象。为什么在使用LinkedList时内存消耗增加了(因为它不需要连续的内存块所以不会进行重新分配)?
如何有效地实现这一目标?数据通过我的程序中的各种列表并消耗超过1GB的内存。上面介绍了类似的情况。
public class MyItem{
private String s;
private int id;
private String search;
public MyItem(String s, int id) {
this.s = s;
this.id = id;
}
public String getS() {
return s;
}
public int getId() {
return id;
}
public String getSearchParameter() {
return search;
}
public void setSearchParameter(String s) {
search = s;
}
}
public class Main{
public static void main(String args[]) {
List<MyItem> l = new ArrayList<>();
List<MyItem> list = new LinkedList<>();
List<MyItem> list1 = new LinkedList<>();
for (int i = 0; i < 1000000 ; i++) {
MyItem m = new MyItem("hello "+i ,i+1);
m.setSearchParameter(m.getS());
l.add(i,m);
}
list.addAll(l);
list1.addAll(l);
list1.addAll(list);
Scanner s = new Scanner(System.in);
s.next();//just not to terminate
}
}
答案 0 :(得分:4)
无论何时你在场景后面调用LinkedList.addAll
,它都会为每个添加的元素创建一个LinkedList.Node
,所以在这里你创建了 3百万个这样的节点,它们不是免费的,实际上:
4 bytes
上的32-bit JVM
和64-bit JVM
的{{1}}(-XX:+ UseCompressedOops),这是默认情况下,Java 7及更高版本中的堆小于UseCompressedOops
,32 GB
上的8 bytes
禁用64-bit JVM
(-XX:-UseCompressedOops)。因此,根据您的配置,它提供 12字节或 24字节。UseCompressedOops
上8 bytes
和32-bit JVM
添加16 bytes
标题字段的大小。因此,根据您的配置,它提供 8字节或 16字节。因此,如果我们总结一下:
64-bit JVM
上的每个实例
启用了32-bit JVM
的{{1}}上的每个实例64-bit JVM
上每个实例已禁用UseCompressedOops
当您在64-bit JVM
上调用3次UseCompressedOops
个100万个对象时,它会给出
addAll
LinkedList
的{{1}}上的32-bit JVM
64-bit JVM
已停用其余的可能是垃圾收集器尚未收集的对象,您应该在加载UseCompressedOops
后尝试调用64-bit JVM
以获取实际大小并在加载{{UseCompressedOops
后执行相同操作1}}。
如果您想获得给定对象的大小,可以使用SizeOf。
如果您使用System.gc()
并且想知道是否启用了ArrayList
,只需在仅包含LinkedList
个选项的终端中启动您的java命令,然后添加64-bit JVM
例如,如果我的命令是UseCompressedOops
,则启动-X
,输出的开头应如下所示:
-XX:+PrintFlagsFinal | grep UseCompressedOops
在这种情况下,标志java -Xms4g -Xmx4g -XX:MaxPermSize=4g -cp <something> <my-class>
已启用
答案 1 :(得分:3)
LinkedList
是doubly-linked list,因此列表中的元素由节点表示,每个节点包含3个引用。
来自Java 8:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
由于您使用了大量内存,因此可能没有使用压缩OOP,因此引用可能是64位,即每个8字节。
对象头为16字节+每个引用8字节,一个节点占用40个字节。拥有100万个元素,即40 Mb。
两个列表是80 Mb,然后记住Java内存被分段为池,对象(节点)被移动,你的内存消耗额外的153 Mb现在似乎是正确的。
注意:Arraylist
每个元素只使用8个字节,而不是40个字节,并且如果你预先分配了自从你知道大小就可以做的后备数组,那么就可以节省大量的内存