Android 中的 Enum 到底占多少内存?该如何用?

Zul99T 8年前
   <blockquote>     <p>听说过一些论调,Enum 不该用啊,占用了很大的 dex 文件,占用很多内存。而到底确切占用了多少内存,没说。本文分析了枚举所占用的精确的内存大小,方便大家权衡选择,希望对大家有帮助。</p>    </blockquote>    <h2>关于 Enum 的使用</h2>    <p><code>Enum</code> 需要占用较大的内存,如果对内存敏感,请尽量少使用 <code>Enum</code>,换用做静态常量。</p>    <p><a href="/misc/goto?guid=4959671945415088577">文档</a> 提到:</p>    <blockquote>     <p>Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.</p>    </blockquote>    <p>关于具体要占用多少内存呢?说得比较模糊。</p>    <h2>内存占用对比</h2>    <p><a href="/misc/goto?guid=4959671945505743167">我</a> 在 《<a href="http://www.open-open.com/lib/view/open1462022744932.html">Dalvik 中的对象大小</a>》一文中, 介绍过如何衡量对象的大小,这个文章非常详细,建议大家看看 ,现举例说明。</p>    <pre>  <code>public enum MonthEnum {      // 4 bytes      JANUARY,  // -> 87 bytes      // 4 bytes      FEBRUARY  // ->  88 bytes        // 生成的数组 24 + 4 + 4      // MonthEnum[] values  }    public class MonthConst {      // 4 bytes      public static final int JANUARY = 1;      // 4 bytes      public static final int FEBRUARY = 2;  }    public class UseMonth {        // 4 bytes      private int mMonth = MonthConst.JANUARY;        // 4 bytes      private MonthEnum mMonthEnum = MonthEnum.JANUARY;  }  </code></pre>    <p>我们不考虑 <code>MonthEnum</code> 和 <code>MonthConst</code> 他们对于 dex 大小的影响,这个没什么意义,几十个 <code>Enum</code> 占用的大小,也不及一张图片。</p>    <p>我们要对比的是 <code>UseMonth</code> 中这两种写法所占用的内存大小在 Dalvik 虚拟机下的区别。</p>    <p>在 <code>UseMonth</code> 中,他们一个是 <code>int</code> 类型,一个是对象引用,都是 4 字节,没有区别。</p>    <p>我们对比的大小,指的是对象本身的大小加上对象成员指向的其他对象大小,即 shadow heap + maintain heap。</p>    <ul>     <li> <p><code>MonthEnum</code></p> <p>对于一个 <code>MonthEnum</code>, <code>JANUARY</code> 和 <code>FEBRUARY</code> 是两个指向 <code>MonthEnum</code> 实例的引用。他们分别占用 4 个字节。</p> <p>他们指向的实例对象还要占用额外的内存。</p> <p>我们看看 <code>enum</code> 的定义:</p> <pre>  <code>class Enum {      private final String name;      private final int ordinal;  }  </code></pre> <p>作为 <code>Enum</code> 成员变量 <code>name</code>(对象引用) 和 <code>ordinal</code>(int) 他们各占用 4 个字节,该对象实例占用:12 + 4 + 4 = 20 bytes,对齐之后是 24 字节。</p> <p>但是,<code>name</code> 是字符串,空字符串对象本身就是 32 字节,加上其中的字符数组最少也会占据 24 个字节, 对字符串加字符数组最少会占据 56 个字节。故一个 <code>Enum</code> 实例,最少 80 个字节。</p> <p><code>MonthEnum.JANUARY</code>,含有 7 个字符,87 个字节;<code>MonthEnum.FEBRUARY</code>,8 个字符,88 个字节。</p> <p><a href="/misc/goto?guid=4959671945599495570">枚举编译完之后</a> 会有一个 <code>values()</code> 数组,两个对象引用的数组占用: 24 + 4 + 4 = 32 bytes。</p> <p>总计是: 4 + 4 + 87 + 88 + 32</p> </li>     <li> <p><code>MonthConst</code></p> <p><code>JANUARY</code> 和 <code>FEBRUARY</code> 各占 4 个字节。共计 8 个字节。</p> <p>总计是: 4 + 4</p> </li>    </ul>    <p>上面我们对比了只具有两个枚举值的枚举和常量,如果数量更多的话,枚举的命名更长的话,这个差距会更大。</p>    <p>文档所说的两倍</p>    <p>所以实际占用的内存,并非 <a href="/misc/goto?guid=4959671945415088577">文档</a> 所说的两倍左右。</p>    <p>假设有 n 个枚举值,仅仅考虑枚举类,静态占用的内存,n 个引用 + n 个数组 + 24 空数组长度: 8n + 24。</p>    <p>而对于 n 个值的常量,则有 4n 字节。当 n 很大时,这样的关系是两倍,但是枚举引用所指向的内存(retained heap)没有考虑进来。</p>    <h2>该用不该用?</h2>    <p><a href="/misc/goto?guid=4959671945415088577">文档</a> 提到:</p>    <blockquote>     <p>You should strictly avoid using enums on Android.</p>    </blockquote>    <p>枚举有其其他的特性,如果你需要这些特性,比如:非连续数值的判断,重载等时,可以用。</p>    <p>另外,内存用量也并非那么地可怕,枚举带来的编码的便捷,代码可读性的提升也是很大的利好。</p>    <p>看到这里,你应该了解了所有的细节了,是否该用,各位自己权衡。</p>    <p>更多的讨论,可以看这里: <a href="/misc/goto?guid=4959671945709049009">该不该用枚举</a>。</p>    <h2>如果更好地使用常量</h2>    <p>如果应用确实对内存用量敏感,或者你就是追求极致,可用常量来代替枚举。</p>    <p>常量一般会和 Bit Mask 结合起来用,这样可以极致地减少了内存使用,同时使代码有较好的可读性。</p>    <p>下一篇文章会提到。</p>    <p>来自:http://www.liaohuqiu.net/cn/posts/android-enum-memory-usage/</p>