对象创建优化

时间:2014-06-20 21:17:51

标签: java android object anonymous

我目前正在观看有关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个字节。

我在这里错过了什么吗?

4 个答案:

答案 0 :(得分:4)

这可能过于简单,但它会给你基本的想法。

这是一个测试类,将方法与额外引用进行比较,将等效方法与内联调用进行比较:

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 

如果仔细观察, only 区别是字节码中的额外参考存储/加载指令。

现在,如果此代码按原样执行,您可能会在很多调用之后发现差异 - 例如在循环中。额外的CPU周期可能会被烧毁,你必须在堆栈上使用一个位置来存储引用(因为GC只处理堆,所以不应该打扰GC,并且堆栈中的项自动释放,来自this回答)。但我不会将费用称为重要

然而,几乎每个JVM(以及Android使用的Dalvik VM,如果内存服务)都有称为JIT编译器的神奇实体。 JIT编译器能够内联额外的引用,因此带有额外引用的代码基本上与没有额外引用的代码完全相同。这应该是JIT编译器执行的相对简单的优化,尤其是对于更现代的VM。

所以最后,如果存在差异,你可以放心地忽略它,因为JITC。在这种情况下,您应该选择更易读的代码样式。

答案 1 :(得分:1)

在视频中,他提到摆脱对象的引用,从

改变
if (cursor.moveToFirst()) {
    do {
        Contact contact = new Contact(...);
        contacts.add(contact);
    while(cursor.moveToNext());
}

Contact contact = null;
if (cursor.moveToFirst()) {
    do {
        contact = new Contact(...);
        contacts.add(contact);
    while(cursor.moveToNext());
}

然后到

if (cursor.moveToFirst()) {
    do {
        contacts.add(new Contact(...));
    while(cursor.moveToNext());
}

在任何情况下,我认为他的意思是(我不能确定)这是对可读性的改进,因为内存会在创建对象时消耗,而不是在变量分配上消耗。

答案 2 :(得分:0)

通过这种更改,代码对某些代码看起来更“优化”,但不会显着减少内存使用量。

答案 3 :(得分:0)

优化也可能发生在编译器端,这不是通过读取代码揭示的。 这里的任何形式的代码都不会改变对象的生命周期,所以对我来说没有优化,只需要更少的代码。