【问题标题】:Default memory allocation on object creation for ArrayList为 ArrayList 创建对象时的默认内存分配
【发布时间】:2014-05-28 16:39:12
【问题描述】:

假设我们正在创建一个 ArrayList 对象,如

ArrayList<User> list=new ArrayList<User>();
System.out.println(list.size());   // it will be 0 at this time

现在我的问题是,当在堆中创建此列表对象时,它将分配什么内存。如果列表的大小动态增长,我想知道在运行时如何分配此内存大小。

【问题讨论】:

  • ArrayList 的初始容量为 10。 10*(引用User对象的内存大小)+(开销)可能是初始分配的内存
  • @Cyber​​neticTwerkGuruOrc 从技术上讲,这将是 10 *(引用的内存大小)+(开销)
  • 好的@Cyber​​neticTwerkGuruOrc,但你能告诉我这是如何工作的吗?我的意思是在运行时如何选择??
  • @user3580294 啊,是的,你是对的。让我编辑一下
  • 您的问题令人困惑(不清楚)。 size() 方法与 JVM 为您的列表分配的实际内存没有任何关系。您可能应该先阅读一下docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html,然后再提出一些明确的问题。

标签: java memory-management collections arraylist


【解决方案1】:
ArrayList<User> list=new ArrayList<User>();

将创建一个空的ArrayList。此时不会创建或添加任何 User 对象到 ArrayList,并且在您明确添加之前永远不会。

此时size() 将返回0,因为ArrayList 中没有元素。

ArrayList 有一个大小并且有一个capacityArrayList 由数组支持。容量是数组在某一点的大小。

每个ArrayList 实例都有一个capacity。容量是大小 用于存储列表中元素的数组。它总是在 至少与列表大小一样大。随着元素被添加到 ArrayList,它的容量会自动增长。成长的细节 除了添加元素具有 固定摊销时间成本。

当通过default constructor 创建一个ArrayList 时,会创建一个容量为10 的ArrayList。

所以从技术上讲,列表实际占用的内存将基于容量。但请注意,数组/列表是空的。因此,容量的增加只会对占用的整体规模产生边际影响。

您可以使用Java VisualVM(包含在JDK 中)之类的工具来检查对象占用的大小。

【讨论】:

    【解决方案2】:

    第 1 部分: 创建列表对象时分配了哪些内存:

    当创建一个没有默认大小的 ArrayList 时,会调用以下构造函数:

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }
    

    super() 这里调用了一个什么都不做的 AbstractList 构造函数。 this.elementData 设置为一个空的对象数组。因此,分配的内存将是 ArrayList 类的不同属性的大小之和 + 分配给类本身的 16 个字节:

    public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
    private static final long serialVersionUID = 8683452581122892189L;
    
    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;
    
    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    
    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
    
    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
    
     ....
    

    您可以查看default sizes of primitive data types 并进行数学运算以了解所使用的确切内存。

    但是,由于初始化了一个空的 elementData 数组,因此没有使用内存来存储任何数据。

    第 2 部分:如果列表的大小动态增长,如何在运行时分配此内存大小。

    首先我们看到 ArrayList 使用数组elementData 来保存对象。有一种方法可以摊销常数时间,但我记不清它的名字了。

    但是想法是,当ArrayList类使用的数组被填充时,数组的大小会增加一倍。当 ArrayList 元素被删除,只填充了数组的 1/4 时,数组的大小减半。

    【讨论】:

      【解决方案3】:

      要回答问题的第二部分,是的,当您向 ArrayList 添加元素时,分配的内存会动态增长。您可以通过查看源代码来估计空 ArrayList 的大小。 ArrayList 本身只声明了三个字段:

      private static final long serialVersionUID = 8683452581122892189L;
      private transient Object[] elementData;
      private int size;
      

      对于一个空列表,它将在 64 位 jvm 中消耗 20 个字节。 ArrayList 扩展了 AbstractList 声明(包括它的私有类):

      int cursor = 0;
      int lastRet = -1;
      int expectedModCount = modCount;
      protected transient int modCount = 0;
      private int offset;
      private int size;
      private int expectedModCount;
      

      增加了 28 个字节,所以总共 48 个字节。

      【讨论】:

        猜你喜欢
        • 2023-03-18
        • 2012-02-21
        • 1970-01-01
        • 1970-01-01
        • 2014-03-31
        • 2012-01-08
        • 1970-01-01
        • 2014-08-05
        • 1970-01-01
        相关资源
        最近更新 更多