1

我最近被一位开发人员问到这个问题,但无法提供答案......

在 Java 中,如何/何时处理变量?他们是吗?

假设我们有一个每秒被调用 50-75 次的 run() 方法(高度简化的示例)......

public void run() {
    long lastTime = System.nanoTime();
    double unprocessed = 0;
    int ticks = 0;
    int frames = 0;
    double nsPerTick = 1000000000.0 / 60.0;
    long now = System.nanoTime();
    unprocessed += (now - lastTime) / nsPerTick;
    lastTime = now;

    ticks++;
    tick();
    unprocessed -= 1;
}

将这些变量(lastTime、unprocessed、tick 等)带入更高级别的范围是否会使程序运行得更好/更有效——到一个每秒不会多次创建和使用它们的地方?

4

5 回答 5

4

由于这些变量是在stack.

阅读本文以获取有关 Java 中对象破坏的更多信息:

Java中的什么方法用于销毁您的对象

于 2013-01-09T01:11:47.310 回答
4

Java 使用垃圾收集器,因此未使用的对象会被自动处理掉。在您的示例中,您仅使用局部变量,它们在堆栈上分配,这非常快(这里不涉及垃圾收集)。将这些变量移动到外部函数(“范围更高”)将无济于事。

于 2013-01-09T01:12:00.340 回答
2

在 Java 中,如何/何时处理变量?他们是吗?

首先,让我们弄清楚一些事情。在Java中,有些东西叫做变量,有些东西叫做对象。变量和对象不是一回事。

Java 中的变量总是其他东西的一部分:

  • 局部变量是方法调用状态的一部分。当它超出范围时,它会被处理掉。这通常意味着当方法调用结束并且方法调用的堆栈帧从调用堆栈中弹出时。

  • 实例变量(或字段或属性)是对象的一部分。当对象被处置时,它会被处置。

  • 静态变量(或类变量)是类的一部分,只有在类被卸载时才会被丢弃。

run()在您的示例中,所有变量都是局部变量,并且在当前对该方法的调用返回(或由于异常而终止)时将被“处理” 。

将这些变量(lastTime、unprocessed、tick 等)带入更高级别的范围是否会使程序运行得更好/更有效——到一个每秒不创建和使用多次的地方?

它没有帮助。创建或处置局部变量的增量成本为零1。使用局部变量的成本(如果有的话)小于使用实例变量的成本。唯一的成本可能是初始化它,但无论如何你都会承担这个成本;例如在每个方法调用上重新初始化一个实例变量。

事实上,将局部变量转换为实例变量实际上可能是一件坏事。这意味着该方法现在使用对象变量来表示其状态,使其不可重入。(目前尚不清楚这是否重要。这取决于该run()方法是否以需要可重入的方式使用......通常情况并非如此。)


1 - 这有点过于简单了。如果您有足够多的局部变量和/或深度递归方法,那么您可能需要使用比正常更大的线程堆栈大小。额外的内存使用将是成本。

于 2013-01-09T01:45:51.610 回答
0

Id 取决于 JVM 运行模式和 JVM 版本。

但是大多数 JVM 版本会这样执行:

当 JVM 在-client模式下运行时,代码将逐行运行。

但是当 JVM 在-server模式下运行时,代码会被优化:

假设 tick() 方法是:

public void tick(){
   System.out.println("tick");
}

优化后的最终代码可能:

public void run() {
  long lastTime = System.nanoTime();
  int ticks = 0;
  int frames = 1;
  double unprocessed = (System.nanoTime() - lastTime) / 16666666.66 -1 ;
  // tick method will inline here instead of invoking tick();
  System.out.println("tick");
}
于 2013-01-09T01:18:56.410 回答
0

这个问题的答案取决于 Java 的垃圾收集,它有一个称为“nursery gc”的较小切片,它被非常频繁地调用并且不会导致 jvm 停止。所有的局部变量都进入这个 Nursery gc。当此方法执行完成时;所有这些变量都符合 GC 条件;并在调用nursery gc 时被删除。

回答您的问题是否将这些变量移出运行方法会更好。尽管 JVM 在每个版本中都变得越来越智能;创建对象对性能的影响较小。但是即使影响很小,我仍然感到;我觉得使对象全球化并没有什么坏处。

尽管在这种情况下,所有变量都是原始的;它不会产生任何影响。

于 2013-01-09T01:11:21.230 回答