【问题标题】:Java object reusabilityJava 对象的可重用性
【发布时间】:2014-05-08 14:13:19
【问题描述】:
class MyNetPack {
    Long count;

    public MyNetPack() {
        count = Long.valueOf(0);
    }

    public void reinit(Long count) {
        this.count = count;
    }

    public void process() {
        /* Some calculation */
    }
}

public class MyWork {

    public static void main(String[] args) {

        MyNetPack my = new MyNetPack();
        for (long i = 1; i < 100000000; i++) {
            my.reinit(i);
            my.process();
        }
    }
}

我正在使用

创建单个对象
MyNetPack my=new MyNetPack();

之后用 reinit 方法重用同一个对象,如下所示,

for (long i = 1; i < 100000000; i++) {
    my.reinit(i);
    my.process();
}

请说明堆栈和堆级别的初始内存分配和内存重用。

据我了解

MyNetPack 引用持有者将在堆栈中分配,对象将在堆中分配(使用引用持有者进行计数)。每次在 for 循环中,count 的实际值(比如 1,2,3..)都会在堆中重新分配,并且引用会放在 MyNetPack->count 引用持有者中。

指导我尽量减少新对象和内存分配..

谢谢 约瑟夫

【问题讨论】:

  • 我不认为它会通过循环的迭代为每个数字保留新的空间,它只会改变已经在保留堆内存中的值。
  • 引用类型或多或少是正确的,但由于 long 是原始类型,因此并非如此。
  • 我想尽量减少GC...

标签: java performance


【解决方案1】:

如果您想减少此应用程序创建的垃圾量,请在 MyNetPack 类中使用 long 而不是 Long

虽然 longLong 的自动装箱可能会重用现有的 Long 对象,但它不会对大整数值执行此操作。

(顺便说一句,在您的示例中,您实际上从未多次自动装箱任何long 值。这意味着Long 缓存实际上并没有节省任何内存或减少分配数量。如果有的话,它会增加两者您的应用程序的内存使用情况,以及分配的数量。)


另一方面,您可能有充分的理由使用Long 而不是long。在这种情况下,您会很高兴知道 JVM 的内存管理已调整,因此分配和垃圾收集短期对象的开销很小。开销通常小于 C 中的 malloc / free 或 C++ 中的 new / dispose

【讨论】:

    【解决方案2】:
    my.reinit(i);
    

    是的,每次你都会在堆中获得新对象。自动装箱和拆箱http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

    【讨论】:

      【解决方案3】:

      由于long 是一个原始类型,count 字段包含数字本身,而不是对数字的引用。

      因此在每次迭代中不会有任何额外的堆分配。

      更新:我刚刚看到了问题,该字段的类型为 Long。在这种情况下,数字确实会自动装箱到一个新创建的对象中,除非数字是 the same reference from a pool。

      正如斯蒂芬所说,解决方案不是使用Long,而是使用原语long。我的经验法则是永远不要使用原始类型的包装类(IntegerLongFloatDouble 等),除非我明确需要一个可空字段。据我所知,这是使用它们的唯一正当理由。

      例如,如果您有这样的事情:

      public class Foo {
        private Long bar;
      
        public void setBar( long bar ) {
          this.bar = bar; // Autoboxing (and object allocation takes places here!!
        }
      
        public void doStuff() {
          if (bar == null) { //if the type of bar was long, its uninitialised value would be 0 but what if 0 is a valid value?
            throw new IllegalStateException( "bar not initialised" );
          //...do stuff here with bar, safe in the knowledge that it had been set to a meaningful value
        }
      }
      

      (请注意,这只是一个示例,我不建议将其作为设计模式。)

      【讨论】:

      • 谢谢 bizclop,有什么办法可以避免创建新的对象(对于 >128)?
      • 我想尽可能的减少GC...有谁能帮忙吗?
      • @user2170066 那你为什么要使用Long
      • 如果null在任何操作中都不合法,那么应该尽快禁止它,即在setter中。下一步是使用原始 long 代替并删除检查。
      • @maaartinus 如您所见,在 setter 中是禁止的。但它仍然是相当糟糕的设计,因为doStuff() 只有在你之前调用setBar() 时才有效。这就是我在末尾添加免责声明的原因:这只是一个简短的示例,说明了为什么要使用包装类。
      猜你喜欢
      • 2015-05-22
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多