在Java中复制对象时保证对象重用

时间:2013-01-23 22:59:16

标签: android garbage-collection jvm immutability

在下面的代码中,我们在一个循环中以一个角度旋转一个复数,然后确认结果数字与我们开始的数字相同。

public class Complex {
    private float r, i;
    ...
    public Complex(Complex other) {
        r = other.r;
        i = other.i;
    }

}

Complex z1 = new Complex(..);
Complex z1_save = new Complex(z1);
Complex z2 = new Complex();
Complex k = new Complex();
k.set_to_first_root_of_unity(8);
int n = 64;
while(n-- != 0) {
    z1.multiply(k, z2);
    z1 = new Complex(z2); // Line Y
}
Assert.assertEquals(true, z1.equals(z1_save));

在Java中是否有办法使用构造函数public Complex(Complex other)而不是clone()编写第Y行,并确保64个对象不会被垃圾回收?

更新:如果不参考交互式应用程序的上下文,似乎无法以简化的方式提出此问题。到目前为止,当前问题的最佳答案(assylias's)是人们不应该担心90%的时间都会创建对象和垃圾收集。在重绘期间,有必要在100%的时间内担心它。我现在重申了here的问题。

5 个答案:

答案 0 :(得分:2)

  

我担心GC不必要地运行64次效率低下。

这是一种不必要的担忧。如果您的对象属于年轻一代(他们将考虑其范围),GC将是免费的(如0成本)。

当GC在年轻代上运行时,它只会通过活动对象(不会访问符合GC条件的对象),因此GC时间只是活动对象的函数。

老一代的故事不同,但你的本地物品不会达到那个阶段。

参考 - Brian Goetz,强调我的:

  

如何解除分配?

     

但是分配只是内存管理的一半 - 解除分配是另一半。事实证明,对于大多数对象,直接垃圾收集成本为零。这是因为复制收集器不需要访问或复制死对象,只有活对象。因此,在分配后不久就成为垃圾的对象不会给收集周期带来任何工作量。

     

?事实证明,典型的面向对象程序中的绝大多数对象(根据各种研究,在92%到98%之间)“年轻”,这意味着它们在分配后不久就会变成垃圾,通常在下一次之前垃圾收集。 (这个属性被称为世代假设,经过实证检验,发现许多面向对象语言也是如此。)因此,不仅分配速度快,而且对于大多数对象,释放是免费的。

答案 1 :(得分:1)

对于具有十(或大)字段的对象执行构造函数64次,即使对于像手机这样的设备也不是什么大事。

目前尚不清楚你的任务是什么。

如果你真的担心多次调用构造函数并创建太多相同的对象,你可以尝试使用Flightweight模式。

答案 2 :(得分:1)

你的问题(和评论)有点混乱......但这可能只是你的书面英语技能问题。所以我只是假设我明白你的意思。我也假设你的例子"工作" ......它目前没有。

简短的回答是,您可以通过使Complex对象变为可变来减少对象流失(即创建和释放"临时"对象)。通常,您可以通过添加允许您更改对象状态的setter操作来执行此操作。但这会使您的Complex课程更难以正确使用。例如:

    public static final ZERO = new Complex(0, 0);

    // somewhere else

    Complex counter = ZERO;
    while (counter.lessThan(10)) {
        // ....
        counter.setRealPart(counter.getRealPart() + 1);  // Ooops!!
    }

......还有更多类似的错误。

然后问题是这是否会实际减少垃圾收集开销,以及减少多少。

正如@assylias指出的那样,在下一个GC周期中创建然后回收的临时对象的成本非常低。昂贵的物品是DON&T成为垃圾的物品。对于在正常环境中运行的正常程序,很有可能实际上更高效来创建临时对象。

然后存在一个问题,即最新的HotSpot JVM可以做一些已知的事情"转义分析",(如果它有效)可以确定给定的临时对象永远不会在其创建范围之外可见,因此根本不需要在堆中分配。当可以应用该优化时,"对象流失"关注是有争议的。

但是,运行GC可能会导致实时"实时"性能;例如在游戏编程中,用户会注意到游戏是否冻结了几分之一秒。在这种情况下,值得考虑"调整"你的代码减少对象流失。但是还有其他可能的方法......比如使用低暂停垃圾收集器......如果你的平台可以使用它。


@ assylias的评论使另一个重要。小心过早优化。您对Complex对象的使用直觉......以及由此产生的对象流失......可能非常错误。在所有条件相同的情况下,最好延迟优化工作,直到您分析了应用程序并确定:

  • 需要进行调整,
  • 分析证据表明Complex类是一个重要的性能瓶颈。

答案 3 :(得分:1)

根本没有理由关注垃圾收集,除非

  • 用户(也许是您)感知应用程序的性能问题;

  • 分析应用程序表明垃圾收集是感知性能问题的根源。

缺少这些条件的:忽略垃圾收集。

通常,Java中的性能调优也是如此。除非你已经证明它有真正的原因,否则不要这样做。

答案 4 :(得分:-1)

如果你想要高效率的w.r.t GC,尽量减少你对新的使用。

因此,在您的示例中,您可以在“行Y”中重复使用该变量,只需使用新值设置字段即可。类似的东西:

while(n-- != 0) {
    z1.multiply(k, z2);
    z1.setValue(z2); // Line Y
}

其中z1.setValue(X)以与构造函数new Complex(x)相同的方式设置对象的状态。


编辑:为什么这个被投票?我坚持上述声明,通过尽量减少使用新的来降低GC的成本。是的,我同意在大多数情况下GC不是问题 - 但是如果您的代码经常调用GC,可能是因为您的代码在循环中花了很多时间(比如CPU重算法),那么您可能希望重用对象。