我目前正在观看有关Android代码优化的视频(https://www.youtube.com/watch?v=w9taB0yUwjs)
在此视频中,他正在优化以下代码:
List<Contact> contacts = new ArrayList<Contact>();
if (cursor.moveToFirst()) {
do {
Contact contact = new Contact(...);
contacts.add(contact);
while(cursor.moveToNext());
}
他建议以下内容可以释放内存.
List<Contact> contacts = new ArrayList<Contact>();
if (cursor.moveToFirst()) {
do {
contacts.add(new Contact(...));
while(cursor.moveToNext());
}
我不太明白为什么这会释放内存.我(有限)的理解是,contact变量只是存储在堆栈中的对象引用.匿名创建对象实际上会显着减少内存使用量吗?从阅读this answer看来,对象引用似乎只需要4-8个字节.
我在这里错过了什么吗?
解决方法:
这可能过于简单,但它会给你基本的想法.
这是一个将方法与额外引用进行比较的测试类,以及使用内联调用的等效方法:
public class Test {
static List<String> list = new ArrayList<>();
public static void extraReference() {
String s = new String();
list.add(s);
}
public static void noReference() {
list.add(new String());
}
}
这是方法的字节码,它们的声明顺序如下:
public static void extraReference();
Code:
0: new #2 // class java/lang/String
3: dup
4: invokespecial #3 // Method java/lang/String."<init>":()V
7: astore_0
8: getstatic #4 // Field list:Ljava/util/List;
11: aload_0
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public static void noReference();
Code:
0: getstatic #4 // Field list:Ljava/util/List;
3: new #2 // class java/lang/String
6: dup
7: invokespecial #3 // Method java/lang/String."<init>":()V
10: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
15: pop
16: return
如果仔细观察,唯一的区别是字节码中的额外参考存储/加载指令.
现在,如果这个代码按原样执行,你可能会注意到很多调用后的差异 – 例如在循环中.额外的CPU周期可能会被烧毁,你必须在堆栈上使用一个位置来存储引用(因为GC只处理堆,所以不应该打扰GC,并且堆栈中的项自动释放,来自this回答).但我不会称这笔费用很重要.
然而,几乎每个JVM(以及Android使用的Dalvik VM,如果内存服务)都有称为JIT编译器的神奇实体. JIT编译器能够内联额外的引用,因此带有额外引用的代码基本上与没有额外引用的代码完全相同.这应该是JIT编译器执行的相对简单的优化,尤其是对于更现代的VM.
所以最后,如果存在差异,你可以放心地忽略它,因为JITC.在这种情况下,您应该选择更易读的代码样式.