java – 对象创建优化

我目前正在观看有关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.在这种情况下,您应该选择更易读的代码样式.

上一篇:在JavaScript中为匿名函数放置参数


下一篇:匿名函数中的PHP变量