【问题标题】:Java memory management while returning the new object返回新对象时的 Java 内存管理
【发布时间】:2015-07-02 10:25:51
【问题描述】:

我想知道在内存管理方面更好的编码方式,

方法一:

Obj temp;
public static Obj fun1() {
      ......
      ......
      Obj temp = new Obj();
      return temp;
}

方法二:

public static Obj fun1() {
     ........
     .........
     return new Obj;
}

哪种方法好?两种方法都做同样的工作。但是第二种方法返回创建新的Object,而第一种方法创建Object将其存储在变量中并返回变量。

请考虑我的函数将有大量这样的函数返回不同类的Object。并且在执行自动测试程序期间它们将被多次调用。那么我应该使用哪一个?为什么?

【问题讨论】:

    标签: java memory-management


    【解决方案1】:

    使用单例模式。如果可以使用相同的实例,则创建一次静态对象 会是这样的

    static Object temp;
    public Object getObjectInstance(){
    if(temp==null){
    temp = new Object();
    }
    
    return temp; 
    }
    

    【讨论】:

    • OP 明确指出,他/她想要 * 大量此类函数返回不同类的对象*。我不认为单例模式是 OP 想要的。
    【解决方案2】:

    两种方式都可以,对内存的影响是一样的。如果您想在返回之前对函数内部的对象执行某些操作,则使用第一个。第二个假设你不想这样做。

    【讨论】:

      【解决方案3】:

      回答:您指定的两种方法差别很小。正如您也知道,在这两种方法中都会创建一个新对象,因此这对您很有效。现在您担心在第二种方法中将使用一个新变量来存储对象引用 - 但它确实无关紧要,因为一旦方法结束,该方法创建的所有局部变量都将从堆栈中删除。

      概念:

      • 对象存在于内存区域的堆中。
      • 变量存在于堆栈中,一旦方法完成,由该方法创建的所有局部变量都会从堆栈中删除。

      简而言之:对于您给定的方法,在内存管理方面几乎没有任何区别。

      【讨论】:

      • 你有你的答案吗,如果没有,请写下你的答案,以便其他人可以从中受益。 stackoverflow.com/help/accepted-answer
      【解决方案4】:

      字节码内部存在实际差异。让我们上下面的课。

      public class Test {
          public static void main(String... args) {
              final int LIMIT = 10_000_000;
              final int RUNS = 10;
              for (int run = 0; run < RUNS; ++run) {
                  Test[] t = new Test[LIMIT];
                  long start = System.nanoTime();
                  for (int i = 0; i < LIMIT; ++i) {
                      t[i] = fun1();
                  }
      
                  System.out.println(  "fun1: "
                                     + ((System.nanoTime() - start) / 1_000_000_000d)
                                     + "s");
      
                  t = new Test[LIMIT];
                  start = System.nanoTime();
                  for (int i = 0; i < LIMIT; ++i) {
                      t[i] = fun2();
                  }
      
                  System.out.println(  "fun2: "
                                     + ((System.nanoTime() - start) / 1_000_000_000d)
                                     + "s");
      
              }
          }
      
          public static Test fun1() {
              return (new Test());
          }
      
          public static Test fun2() {
              Test t = new Test();
              return (t);
          }
      }
      

      当您查看方法 fun1()fun2() 的字节码时,它们将如下所示:

      public static Test fun1();
        Code:
          0: new           #1                  // class Test
          3: dup
          4: invokespecial #20                 // Method "<init>":()V
          7: areturn
      
      public static Test fun2();
        Code:
          0: new           #1                  // class Test
          3: dup
          4: invokespecial #20                 // Method "<init>":()V
          7: astore_0 // those lines are
          8: aload_0  // unique to fun2()
          9: areturn
      

      当您执行此代码时,您可能会注意到执行时间越来越快。我认为这是由于 JIT 编译器。然而,最终在性能方面几乎没有差异。

      【讨论】:

      • OP 想从内存管理的角度来理解.. 是的,字节码会有所不同,因为对象分配正在发生,但它确实有非常微不足道的影响..
      • 我的意思是:JIT 编译器似乎优化了这段代码。优化很可能会对内存产生影响(例如,JIT 编译器“通知”,astore_0aload_0 是不必要的)。因此,这是一个不容易回答的问题(如果它甚至可以回答的话)。
      • 我真的不确定在这种特殊情况下是否有任何优化,只是编译器生成了与其各自的 Java 语句相对应的汇编代码指令。两者都知道 OP 的两种方法在内存和性能方面的影响确实微不足道..
      【解决方案5】:

      这两种方法几乎相似。在方法 1 中,如果您在将对象分配给引用变量后立即返回,那么将其分配给引用变量是没有意义的。在这种情况下应该选择方法2。

      Obj temp = new Obj(); //if you are not doing anything with temp but just returning then go with method 2
        return temp;
      

      但是,如果您必须改变对象以执行某些操作,那么 method1 应该很好,因为您将在 'temp' 中获取对该对象的引用

      Obj temp = new Obj(); //if temp is used before return, then go with method 1
        return temp;
      

      注意:只有对象占用堆上的内存而不是引用变量。

      【讨论】:

        【解决方案6】:

        好吧,我与专家讨论过,发现使用以下方法会在每次创建新对象时在堆栈中创建内存分配。

        method1 () {
        return new ClassObj();
        }
        method2 () {
        return new ClassObj();
        }
        method3 () {
        return new ClassObj();
        }
        method4 () {
        return new ClassObj();
        }
        method5 () {
        return new ClassObj();
        }
        

        这个堆栈内存继续分配。如果我们在运行期间多次调用这个函数(就像在运行自动化脚本中通常发生的那样)同样的函数一次又一次地被调用,这个堆栈积累会继续增加。

        所以为了避免这种情况,请使用一些临时对象来捕获方法返回的对象。所以不是在堆栈中分配内存,而是删除临时对象以前的内存数据并用新的替换它。

        ClassObj temp;
        method1 () {
        temp = new ClassObj();
        return temp;
        }
        method2 () {
        temp = new ClassObj();
        return temp;
        }
        method3 () {
        temp = new ClassObj();
        return temp;
        }
        method4 () {
        temp = new ClassObj();
        return temp;
        }
        method5 () {
        temp = new ClassObj();
        return temp;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-20
          • 1970-01-01
          • 2011-09-28
          • 2018-02-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多