-3

在下面的代码中,我们在循环中将复数旋转了某个角度,然后确认得到的数字与我们开始时的数字相同。

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)而不是编写Line Y clone(),并确定64个对象不会被垃圾收集?

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

4

5 回答 5

2

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

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

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

老一代的故事不同,但您的本地对象不会达到那个阶段。

参考 - Brian Goetz,强调我的:

解除分配呢?

但是分配只是内存管理的一半——释放是另一半。事实证明,对于大多数对象,直接垃圾回收成本为零。这是因为复制收集器不需要访问或复制死对象,只需要访问或复制活对象。因此,在分配后不久变成垃圾的对象不会对收集周期造成任何工作负载。

? 事实证明,典型的面向对象程序中的绝大多数对象(根据各种研究在 92% 到 98% 之间)“早逝”,这意味着它们在分配后不久即成为垃圾,通常在下一次垃圾回收之前。(这个属性被称为世代假设,并且已经过经验测试,发现对于许多面向对象的语言都是正确的。)因此,不仅分配速度很快,而且对于大多数对象来说,释放是免费的。

于 2013-01-23T23:27:47.040 回答
1

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

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

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

于 2013-01-23T23:24:13.517 回答
1

您的问题(和评论)有点困惑……但这可能只是您的书面英语技能有问题。所以我只是假设我明白你的意思。我还假设您的示例“有效”......它目前没有。

简短的回答是,您可以通过使Complex对象可变来减少对象流失(即“临时”对象的创建和释放)。通常,您通过添加允许您更改对象状态的设置器操作来执行此操作。但这会使您的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 周期中创建然后回收的临时对象的成本非常低。昂贵的对象是不会变成垃圾的对象。而且很可能对于在正常环境中运行的正常程序来说,创建临时对象实际上总体上 更有效。

还有一个问题是最新的 HotSpot JVM 可以做一些被称为“逃逸分析”的事情,它(如果它有效)可以确定给定的临时对象在其创建范围之外永远不可见,因此不需要完全分配在堆中。当可以应用该优化时,就会提出“对象流失”问题。

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


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

  • 需要调整,并且
  • 分析证据表明Complex该类是一个重要的性能瓶颈。
于 2013-01-23T23:59:40.510 回答
1

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

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

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

缺少这两个条件:忽略垃圾收集。

一般而言,Java 中的性能调优也是如此。除非你已经证明有真正的理由,否则不要这样做。

于 2013-01-24T00:41:04.993 回答
-1

如果您想提高 wrt GC 的效率,请尽量减少对 new 的使用。

因此,在您的示例中,您可以重新使用“Y 行”中的变量,并简单地使用新值设置字段。就像是:

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

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


编辑:为什么这被否决了?我支持上面关于通过最小化使用 new 来降低 GC 成本的说法。是的,我同意在大多数情况下 GC 不是问题 - 但如果您的代码确实经常调用 GC,可能是因为您的代码在循环中花费了大量时间(例如 CPU 密集型算法),那么您可能希望重用对象.

于 2013-01-23T23:10:33.580 回答