这篇文章https://blog.youkuaiyun.com/javazejian/article/details/71333103
深入讲解了enum类型的使用,虽然在声明enum类型的时候比较简单,但是编译器为我们编译成功后的enum类型对应的class文件是很复杂的,里面至少包含了enum类型声明的每个元素的实例对象,以及这些对象的一个数组集合。
所以在内存方面会成倍增加,具体是如何增加的参考这篇文章:https://www.liaohuqiu.net/cn/posts/android-enum-memory-usage/
原文如下:
Android 中的 Enum 到底占多少内存?该如何用?
28 Apr 2016
听说过一些论调,Enum 不该用啊,占用了很大的 dex 文件,占用很多内存。而到底确切占用了多少内存,没说。本文分析了枚举所占用的精确的内存大小,方便大家权衡选择,希望对大家有帮助。
关于 Enum 的使用
Enum
需要占用较大的内存,如果对内存敏感,请尽量少使用 Enum
,换用做静态常量。
文档 提到:
Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
关于具体要占用多少内存呢?说得比较模糊。
内存占用对比
我 在 《Dalvik 中的对象大小》一文中, 介绍过如何衡量对象的大小,这个文章非常详细,建议大家看看 ,现举例说明。
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;
}
我们不考虑 MonthEnum
和 MonthConst
他们对于 dex 大小的影响,这个没什么意义,几十个 Enum
占用的大小,也不及一张图片。
我们要对比的是 UseMonth
中这两种写法所占用的内存大小在 Dalvik 虚拟机下的区别。
在 UseMonth
中,他们一个是 int
类型,一个是对象引用,都是 4 字节,没有区别。
我们对比的大小,指的是对象本身的大小加上对象成员指向的其他对象大小,即 shadow heap + maintain heap。
MonthEnum
对于一个
MonthEnum
,JANUARY
和FEBRUARY
是两个指向MonthEnum
实例的引用。他们分别占用 4 个字节。他们指向的实例对象还要占用额外的内存。
我们看看
enum
的定义:class Enum { private final String name; private final int ordinal; }
作为
Enum
成员变量name
(对象引用) 和ordinal
(int) 他们各占用 4 个字节,该对象实例占用:12 + 4 + 4 = 20 bytes,对齐之后是 24 字节。但是,
name
是字符串,空字符串对象本身就是 32 字节,加上其中的字符数组最少也会占据 24 个字节, 对字符串加字符数组最少会占据 56 个字节。故一个Enum
实例,最少 80 个字节。MonthEnum.JANUARY
,含有 7 个字符,87 个字节;MonthEnum.FEBRUARY
,8 个字符,88 个字节。枚举编译完之后 会有一个
values()
数组,两个对象引用的数组占用: 24 + 4 + 4 = 32 bytes。总计是: 4 + 4 + 87 + 88 + 32
MonthConst
JANUARY
和FEBRUARY
各占 4 个字节。共计 8 个字节。总计是: 4 + 4
上面我们对比了只具有两个枚举值的枚举和常量,如果数量更多的话,枚举的命名更长的话,这个差距会更大。
文档所说的两倍
所以实际占用的内存,并非 文档 所说的两倍左右。
假设有 n 个枚举值,仅仅考虑枚举类,静态占用的内存,n 个引用 + n 个数组 + 24 空数组长度: 8n + 24。
而对于 n 个值的常量,则有 4n 字节。当 n 很大时,这样的关系是两倍,但是枚举引用所指向的内存(retained heap)没有考虑进来。
该用不该用?
文档 提到:
You should strictly avoid using enums on Android.
枚举有其其他的特性,如果你需要这些特性,比如:非连续数值的判断,重载等时,可以用。
另外,内存用量也并非那么地可怕,枚举带来的编码的便捷,代码可读性的提升也是很大的利好。
看到这里,你应该了解了所有的细节了,是否该用,各位自己权衡。
更多的讨论,可以看这里: 该不该用枚举。
如何更好地使用常量
如果应用确实对内存用量敏感,或者你就是追求极致,可用常量来代替枚举。
常量一般会和 Bit Mask 结合起来用,这样可以极致地减少了内存使用,同时使代码有较好的可读性。
关于android中是否推荐使用enum的说明:
https://www.cnblogs.com/zgz345/p/5871351.html
原文如下:
一、Enum的产生
Java1.5中引入了枚举的语法,包括Enum,EnumSet,EnumMap等。其中Enum就是我们在C或C++中见过的枚举类型,但是Java中的枚举又比C或C++中的枚举更成熟和复杂。在Java中,枚举算是一种特殊的类,也就是class,因此它可以做很多类相关的事情,而不仅仅是定义几个枚举值
二、Android官方建议
Android官网不建议使用enums,占用内存多(Enums often require more than twice as much memory as static constants.)。 Android中当你的App启动后系统会给App单独分配一块内存。App的DEX code、Heap以及运行时的内存分配都会在这块内存中。 比如:
1.使用Int表示状态
public static final int VALUE1 =1;
public static final int VALUE2 =2;
public static final int VALUE3 =3;
2.使用Enums表示状态
public static enum Value{
VALUE1,
VALUE2,
VALUE3
}
2中使用Enums,DEX size增加是情形1中的13倍之多。这只是DEX code的增加,同样,运行时的内存分配,一个enum值的声明会消耗至少20 bytes,不算其中的对象数组需要保持对enum值的引用。使用javap反编译情形二中生成的class文件,去掉汇编代码后如下:
public final class VALUE extends java.lang.Enum{
public static final VALUE VALUE1;
public static final VALUE VALUE2;
public static final VALUE VALUE3;
private static final VALUE[] values[];
static{}
}
enum类型继承java.lang.Enum,每个枚举项都会被声明成一个静态变量,并被赋值。VALUE value1 = VALUE.VALUE1则会引起对静态变量的引用。
三、Android 中的 Enum 到底占多少内存?该如何用?
四、替代方案
个人倾向与接口变量,因为接口会自动将成员变量设置为静态的(static)、不可变的(final),这一点可以防止某些情况下错误地添加新的常量。这也使得代码看起来更简单和清晰。
同时,一个的简单测试显示,同样的接口(字节码文件)占用的空间是209个字节左右,而类(字节码文件)占用的空间是366个字节左右。更少的字节码文件意味着加载和维护的成本更低。此外,JVM 加载接口的时候,不需要担心类提供的额外特征(如重载、方法的动态绑定等),因此加载更快。
五、总结
不可否认enums会使得代码更易读更安全,但是在编写高效Android代码时避免使用枚举,我们在很多经典的Java书已经看到推荐使用枚举来代替int常量了,但是在Android开发中我不建议使用枚举,特别是大型的App中,能不用则不用。因为它会牺牲执行的速度和并大幅增加文件体积。这也是性能优化中减少OOM的一个方面。
例如:
public class Foo {
public enum Shrubbery { GROUND, CRAWLING, HANGING }
}
会产生一个900字节的.class文件(Foo$Shubbery.class)。在它被首次调用时,这个类会调用初始化方法来准备每个枚举变量。每个枚举项都会被声明成一个静态变量,并被赋值。然后将这些静态变量放在一个名为"$VALUES"的静态数组变量中。而这么一大堆代码,仅仅是为了使用三个整数。