【问题标题】:Creating Java object general question创建 Java 对象一般问题
【发布时间】:2011-01-03 20:04:07
【问题描述】:

对于内存管理哪个更好,或者任何其他原因,或者这两种情况是否相同:

Calendar currentDateTime = Calendar.getInstance();
int i= foo.getSomething(currentDateTime);
Bar bar= foo.getBar(currentDateTime);

另一个代码块:

int i= foo.getSomething(Calendar.getInstance());
Bar bar= foo.getBar(Calendar.getInstance());

一般的问题是,获取一个对象的实例,然后在需要时使用该实例,还是在每次需要时调用 getInstance() 更好。 而且,如果不处理单例,而是制作一个普通的 POJO,答案会改变吗?

【问题讨论】:

    标签: java memory object


    【解决方案1】:

    需要明确的是,Calendar 实际上并不是一个单例。 Calendar.getInstance() 每次调用都会返回一个新对象。

    这意味着您的问题的答案取决于函数 getSomething() 和 getBar() 是否具有导致 foo 存储新 Calendar 实例的副作用。一般来说,良好的编程实践表明情况并非如此。

    编辑:但是,每次调用 Calendar.getInstance() 时,您最终可能会得到不同的日期。这可能是一个重要的细节,具体取决于您的功能正在做什么。

    编辑 2:这还取决于您执行上述过程的频率。正如另一个答案指出的那样,实例化 Calendar 对象可能会很费力。如果你只做两次,那么你是否缓存它并不重要。如果你经常这样做,那么你可能会考虑改变你的方法或做一些缓存。

    【讨论】:

      【解决方案2】:

      日历是一个非常昂贵的对象(我所知道的任何库中最昂贵的日期对象之一)。调用 getInstance() 也非常昂贵。如果您必须使用日历,您可以查看缓存它。这真的取决于你为什么需要它。

      获取和存储当前时间最有效的方法是使用长原语。

      long currentDateTime = System.currentTimeMillis();
      

      如果您在内部使用 GMT 时间,则可以将当前日期存储为

      int currentDay = (int)(System.currentTimeMillis() / 86400000);
      

      编辑:值得在您的机器上进行测试,虽然 getInstance() 相对昂贵,但仍然相当快。在我的旧盒子上大约需要 20 微秒。在快速机器上 currentTimeMillis() 可能需要 140 纳秒。

      以下打印件

      Calendar.getInstance() took on average 20088 ns. java.util.GregorianCalendar[time=1294086899359 ... deleted ...]
      System.currentTimeMillis() took on average 938 ns. 1294086899377
      

      代码

      int runs = 10000;
      long start = System.nanoTime();
      Calendar cal = null;
      for(int i=0;i<runs;i++)
          cal = Calendar.getInstance();
      long time = System.nanoTime() - start;
      System.out.println("Calendar.getInstance() took on average "+time/runs+" ns. "+cal);
      
      long start2 = System.nanoTime();
      long now = 0;
      for(int i=0;i<runs;i++)
          now = System.currentTimeMillis();
      long time2 = System.nanoTime() - start2;
      System.out.println("System.currentTimeMillis() took on average "+time2/runs+" ns. "+now);
      

      【讨论】:

      • 是什么让日历如此昂贵?
      • 它包含一个时区、语言环境、一个表示其日/月/年/小时/分钟/秒的 int[] 和一个布尔数组,该数组表示已设置了哪些值。它有一个“指定每个字段的设置时间的伪时间戳”。这是一个 int[] 和一个最小值字段。它以毫秒为单位缓存时间,一个布尔值表示是否设置了任何字段,另一个表示是否设置了所有字段,另一个记录宽松是否为真,一个 int 记录是一周的第一天,另一个用于 minimumDaysInFirstWeek,一个用于解码旧序列化​​版本的 serialVersionOnStream....
      • .. 一个长字段表示公历截止日期,另一个字段表示儒略日期结束的那一天,即截止年份。关于如何计算 Gregorian 和 Julian 日期的三个参考,一个 int[] 用于时区偏移,一个 int[] 用于字段的原始值......可能是这样,但很难说。 ;)
      【解决方案3】:

      对于单身人士来说,差别不大。通过使用临时变量,您可以节省函数调用的开销,但仅此而已 - 每次都返回相同的对象。

      如果您正在创建 POJO,无论是通过调用构造函数还是使用创建对象的静态方法,您都在创建一个新对象。这意味着您有函数调用的运行时开销,以及正在创建的另一个对象的内存开销。

      一般来说,如果我打算在一个方法体中多次使用同一个对象,我会使用一个临时变量。这样,无论是否需要避免内存开销,我都在做同样的事情,并且我的代码将更加一致。

      【讨论】:

      • FWIW,日历不是单例。我不确定这就是你或 OP 的意思,但我只是想我会提出来,因为他们在同一个问题中同时提到了 Calendar 和 getInstance 。
      【解决方案4】:
      1. 使用更多的内存和更少的 CPU
      2. 使用更少的内存和更多的 CPU

      您必须决定要为手术提供什么。 您的应用程序的瓶颈是什么。

      【讨论】:

      • 您可能应该添加原因。原因在于 1. 我们将内存分配给 currentDateTime 以存储值以供以后使用,而在 2. 我们 Calendar.getInstance() 两次占 CPU 更少的内存。
      • 两者都可能消耗更多内存,具体取决于调用方式和频率。重复调用 getInstance() 会产生更多需要清理的垃圾。
      • 我的猜测是 1. 使用较少的内存,因为“currentDateTime”是通过引用方法传递的,是不是这样想错了,我错过了什么?
      • 您的第二种情况并不完全正确。通过 Calendar.getInstance() 调用,Calendar 对象被创建了两次,因此它使用了更多的内存和 CPU。
      • 你不能肯定地说。优化器可以为所欲为。
      【解决方案5】:

      您基本上是在看两种不同的场景: 1. Calendar 是一个单例,在这种情况下,您必须调用辅助方法来获取内存中的单个实例。 2. 日历是 POJO。如果 getInstance() 只是调用构造函数,那么每次调用它时都会创建一个新实例。因此,在后一种情况下,您可能会得到不同的意外结果。

      不过,归根结底是编码风格和可读性。一些开发者更喜欢使用factory methods,一些更喜欢直接调用构造函数。在我看来,如果对象是一个简单的实体(即不需要构建),那么调用 new 是创建对象的最直接的方法。另一方面,如果涉及到一些构建,并且您想要通常更具可读性的代码,那么工厂方法是更可取的。

      从内存管理的角度来看,每次调用构造函数时,都会得到一个新的类实例。你打算使用那个新实例吗?您是否引用了可能阻止其及时被垃圾收集的新实例?这些问题有点元。

      【讨论】:

        【解决方案6】:

        如果您只对从日历实例中读取感兴趣,则第一种方法稍微好一点,因为垃圾收集器不会有太多工作要做,而且您只会存储/制作一次重量级对象。

        【讨论】:

          【解决方案7】:

          第一个块查询当前日期和时间一次。这很好,因为您可能希望对两者使用相同的时间戳。

          第二个示例速度较慢,因为它必须初始化一个新的Calendar 对象。也可能有两个不同的时间戳。问问自己,当第一个方法以23:59:59.875 的时间调用而第二个方法以00:00:00.007 的时间调用时,午夜前后会发生什么。你真的想要吗?

          严格来说,第一个代码 sn-p 需要较长时间的内存。但在几乎所有情况下,您都应该对此感到满意,并使用第二个代码 sn-p。

          顺便说一句:您可以假设局部变量不占用任何内存。尤其是在经常使用的代码中,它们会被优化掉。

          【讨论】:

            【解决方案8】:
            1. 使用更少的内存和 CPU
            2. 使用更多内存和更多 CPU

            垃圾收集器可以在最后一次使用对它的引用之后立即收集 Calendar 实例。

            【讨论】:

              猜你喜欢
              • 2015-03-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-06-13
              • 2016-04-06
              • 1970-01-01
              • 1970-01-01
              • 2017-12-04
              相关资源
              最近更新 更多