为什么static / member变量比局部变量慢?

时间:2014-08-01 05:00:21

标签: java performance bytecode

我见过这个帖子:Speed of if compared to conditional

制作我自己的班级来检查速度

public class Question {
static long startTime;
static long elapsedTime;

static String mStatic;
private String mPublic;

public static void main(String[] args) {
    Question q = new Question();
    q.executeGlobal();
    q.executeStatic();
    q.executeLocal();
}

public void executeLocal() {
    String mLocal;
    startTime = System.nanoTime();
    for (int i = 0; i < 1000000000; i++) {
        mLocal = "";
    }
    elapsedTime = System.nanoTime() - startTime;
    System.out.println("Type Local: " + elapsedTime + " ns");

}

public void executeGlobal() {
    startTime = System.nanoTime();
    for (int i = 0; i < 1000000000; i++) {
        mPublic = "";
    }
    elapsedTime = System.nanoTime() - startTime;
    System.out.println("Type Global: " + elapsedTime + " ns");

}

public void executeStatic() {
    startTime = System.nanoTime();
    for (int i = 0; i < 1000000000; i++) {
        mStatic = "";
    }
    elapsedTime = System.nanoTime() - startTime;
    System.out.println("Type Static: " + elapsedTime + " ns");
}

}

结果:

Type Global: 45693028 ns
Type Static: 43853723 ns
Type Local: 2057505 ns

在答案中@Rod_algonquin回答说,这是因为静态变量的 getstatic / putstatic 字节码和 getfield / putfield 成员变量的字节码,它通过位移和一些加法来计算。

起初我认为只有对象会导致这种情况,但在尝试引用原语时结果是相同的local variable仍然更快

为什么局部变量更快?除字节码解释外。

3 个答案:

答案 0 :(得分:9)

通常,jvm使用三个不同的内存段

  1. 堆 - 包含运行时中的所有已创建对象,仅包含对象 他们的对象属性(实例变量)
  2. Stack - 包含局部变量和引用变量(保存堆中对象地址的变量)
  3. 代码段 - 加载时实际编译的Java字节码所在的段
  4. 堆栈值仅存在于它们创建的函数范围内。一旦返回,它们就会被丢弃。

    堆上存在堆值。它们是在某个时间点创建的,并在另一个时间点被破坏(通过GC或手动)。 Java仅在堆栈上存储基元。这使堆栈保持较小并有助于保持单个堆栈帧较小,从而允许更多嵌套调用。对象是在堆上创建的,只有引用(它们又是基元)在堆栈中传递。

    Java使用三种不同的变量

    • 静态变量: - 类变量称为静态变量。 每个类每个JVM只出现一次类变量 装载机。当一个类加载类变量(又名静态 变量)被初始化。它们驻留在类(字节码)的位置 驻留在代码段
    • 实例变量: - 实例变量是非静态的,有 每个类实例中出现一个实例变量(即 每个对象)。也称为成员变量或字段。
    • 局部变量:局部变量的范围比实例窄 变量。局部变量的生命周期由下式确定 执行路径

    访问堆栈的速度相对较快(但它纯粹是JVM实现特定的),而不是代码段,因此访问本地变量的速度比全局快。

    有关详细信息,您可以查看http://blog.jamesdbloom.com/JVMInternals.htmlit-haggar_bytecode

    还有另一篇文章分析了static vs local variable

    的表现

答案 1 :(得分:5)

你是运行时优化的受害者: - )

如果您稍微更改一下代码:

Question q = new Question();
for (int i=0; i<2; i++) {
    q.executeGlobal();
    q.executeStatic();
    q.executeLocal();
}

你明白了:

Type Global: 38331943 ns
Type Static: 57761889 ns
Type Local: 3010189 ns
Type Global: 46249688 ns
Type Static: 52745009 ns
Type Local: 0 ns

发生的事情是,运行时很快就会意识到你的局部变量一直被分配,但永远不会被读取(或使用),并优化整个循环。

至于类实例字段和静态字段之间的区别,它们都在堆上,但静态字段在所有对象实例之间共享,因此存在额外的间接级别

答案 2 :(得分:2)

另一个原因可能是缺少缓存。

当CPU需要刷新缓存(这意味着您尝试访问的变量不在CPU缓存中的RAM中)时,缓存未命中会带来约250%的惩罚。

查看测试结果,似乎是缓存未命中问题:

您的局部变量(mLocal和i)在每个循环中被访问,但它们在内存中彼此接近,因为它们最近被添加到堆栈中。

mPublic和mStatic与“i”不在同一内存页面内。所以你的for循环应该在“i”的页面和mPublic / mStatic所在的页面之间切换内存页面。

Ofc你不知道内存实际上是如何映射的,所以这只是猜测。

如果你在这,你可以做另一个实验吗? 声明静态mStatic变量旁边的静态整数,并在循环中使用该整数。绩效在提高吗?