为什么static / member变量比局部变量慢?
我见过这个post: 速度与条件相比
我自己上课检查速度
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
仍然更快 。
为什么局部变量更快? 字节码解释除外。
你是运行时优化的牺牲品:-)
如果你稍微改变你的代码:
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
发生的事情很快就是运行时很快意识到你的局部变量不断被分配,但永远不会被读取(或使用),并优化整个循环。
至于类实例字段和静态字段之间的区别,它们都在堆上,但静态字段在所有对象实例之间共享,因此有一个额外的间接级别
通常,jvm使用三个不同的内存段
- 堆 – 包含运行时中的所有已创建对象,仅包含对象及其对象属性(实例变量)
- 堆栈 – 包含局部变量和引用变量(保存堆中对象地址的变量)
- 代码段 – 加载时实际编译的Java字节码所在的段
堆栈值仅存在于它们创建的函数范围内。一旦返回,它们将被丢弃。
然而,堆值存在于堆上。 它们是在某个时间点创建的,并在另一个时间点被破坏(通过GC或手动)。 Java仅在堆栈上存储基元。 这使堆栈保持较小并有助于保持单个堆栈帧较小,从而允许更多嵌套调用。 在堆上创建对象,并且只在堆栈上传递引用(反过来是基元)。
Java使用三种不同的变量
- 静态变量: – 类变量称为静态变量。 每个类加载器每个JVM只出现一次类变量。 加载类时,初始化类变量(也称为静态变量)。 它们位于类(字节码)所在的位置,位于代码段中
- 实例变量: – 实例变量是非静态的,每个类实例(即每个对象)中都会出现一个实例变量。 也称为成员变量或字段。
- 局部变量:局部变量的范围比实例变量的范围窄。 局部变量的生命周期由执行路径决定
访问堆栈的速度相对较快(但是,它纯粹是JVM实现特定的),而不是代码段,因此访问本地变量比全局更快。
有关详细信息,您可以查看http://blog.jamesdbloom.com/JVMInternals.html或it-haggar_bytecode
还有另一篇文章分析静态与局部变量的性能
另一个原因可能是缺少缓存。
当CPU需要刷新它的缓存时(这意味着您尝试访问的变量不在CPU缓存中,在RAM中),缓存未命中会带来约250%的惩罚。
查看测试结果,似乎是缓存未命中问题:
您的局部变量(mLocal和i)在每个循环中都被访问,但它们在内存中彼此接近,因为它们最近被添加到堆栈中。
mPublic和mStatic不在“i”的内存页面内。 所以你的for循环应该在“i”的页面和mPublic / mStatic所在的页面之间切换内存页面。
你不知道内存实际上是如何映射的,所以这只是猜测。
如果你在这,你可以做另一个实验吗? 声明静态mStatic变量旁边的静态整数,并在循环中使用该整数。 性能在提高吗?