Java中合理使用局部变量替代成员变量静态变量

Posted Danny_姜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中合理使用局部变量替代成员变量静态变量相关的知识,希望对你有一定的参考价值。

故事起因

一个Java类中可以存在多种形式的变量,可以是最一般的成员变量、或静态变量、或临时变量。如下图:

public class VariableDemo 
    static int staticValue = 0; // 静态变量
    int fieldValue;             // 全局变量

    public int getMultiValue() 
        int localValue = 10;    // 局部变量
        return localValue * 2;
    

图中的staticValuefieldValue和方法内部的localValue分别为静态变量、成员变量和局部变量。

但是在我阅读android或者是JDK源码时,经常看到以下形式的代码:

public class Hashtable 

    private transient HashtableEntry<?,?>[] table;

    private void addEntry(int hash, K key, V value, int index) 
        HashtableEntry<?,?> tab[] = table;
        ...
        // Creates the new entry.
        HashtableEntry<K,V> e = (HashtableEntry<K,V>) tab[index];
        tab[index] = new HashtableEntry<>(hash, key, value, e);
        count++;
    

上图中,在HashTable源码的 addEntry 方法内部创建了一个临时的局部变量tab,用来替代成员变量table,并且后续的操作都由tab对象来执行。

那么这么做的意义是什么呢?


实践检验

这3种变量的存取效率是有区别的,合理使用这3种变量能够帮助我们实现更高性能的代码。可以用一个简单的例子来展示这3种变量各自的存取效率,如下代码:

package com.subject.variable;
/**
 * 使用临时变量提高Java代码性能
 */
public class VariableDemo 

    private long instVar;// 成员变量
    private static long staticVar; // 静态变量

    // 存取类方法中的临时变量
    void tempAccess(long val) 
        int j = 0;// 临时变量
        long startTime = System.currentTimeMillis();
        for (long i = 0; i < val; i++) 
            j += 1;
        
        long endTime = System.currentTimeMillis();
        System.out.println("temp var: " + (endTime - startTime) + " milli seconds");
    

    // 存取类的成员变量
    void instanceAccess(long val) 
        long startTime = System.currentTimeMillis();
        for (long i = 0; i < val; i++) 
            instVar += 1;
        
        long endTime = System.currentTimeMillis();
        System.out.println("instance var: " + (endTime - startTime) + " milli seconds");
    

    // 存取类的 static 变量
    void staticAccess(long val) 
        long startTime = System.currentTimeMillis();
        for (long i = 0; i < val; i++) 
            staticVar += 1;
        
        long endTime = System.currentTimeMillis();
        System.out.println("static var: " + (endTime - startTime) + " milli seconds");
    

    public static void main(String[] args) 
        VariableDemo test = new VariableDemo();
        long count = 10 * 1000 * 1000 * 1000L;
        test.tempAccess(count);
        test.instanceAccess(count);
        test.staticAccess(count);
    

解释说明

这段代码中的每个方法都执行相同的循环,并反复相同的次数。唯一的不同是每个循环使一个不同类型的变量递增:

  • tempAccess 使一个方法的局部变量递增

  • instanceAccess 使类的一个成员变量递增

  • staticAccess 使类的一个静态变量递增

计算结果如下:

temp var: 2987 milli seconds
instance var: 4039 milli seconds
static var: 3996 milli seconds

从结果中可以发现,instanceAccess 和 staticAccess 的执行时间基本相同。但是,tempAccess 要快两到三倍。

原理分析

存取堆栈变量如此快是因为JVM 存取局部变量的存取效率比它成员变量静态变量更高。

JVM 是一种基于堆栈的虚拟机,每一个方法对应一个栈帧,每个方法在线程中执行时,都会通过栈帧的形式进行运算。如下图:

所有局部变量都存储在一个栈帧中的局部变量表中,通过局部变量表与操作数栈的协同合作,局部变量可以被高效地存取。

相比之下,成员变量静态变量的存取成本就相对较高。因为 JVM 必须使用代价更高的操作码,并从常数存储池中存取它们。(常数存储池保存一个类型所使用的所有类型、字段和方法的符号引用)。通常,在第一次从常数存储池中访问成员变量静态变量以后,JVM 将动态更改字节码以使用效率更高的操作码。尽管有这种优化,堆栈变量的存取仍然更快。

注意:这也是为什么JVM会自动优化成员变量转化为局部变量,详细内容可以参考链接:不要让你的 Java 对象"逃逸"了!


代码优化

具体优化措施可以参考文章开头介绍的Android或Java源码中的做法,使用局部变量替换成员变量或者静态变量,以便通过基于栈操作使代码运行更加高效。

package com.subject.variable;

/**
 * 使用临时变量提高Java代码性能
 */
public class VariableDemo2 

    private long instVar;// 成员变量
    private static long staticVar; // 静态变量

    // 存取类方法中的临时变量
    void tempAccess(long val) 
        int j = 0;// 临时变量
        long startTime = System.currentTimeMillis();
        for (long i = 0; i < val; i++) 
            j += 1;
        
        long endTime = System.currentTimeMillis();
        System.out.println("temp var: " + (endTime - startTime) + " milli seconds");
    

    // 存取类的成员变量
    void instanceAccess(long val) 
        long startTime = System.currentTimeMillis();
        long tmp=instVar;
        for (long i = 0; i < val; i++) 
            tmp += 1;
        
        instVar=tmp;
        long endTime = System.currentTimeMillis();
        System.out.println("instance var: " + (endTime - startTime) + " milli seconds");
    

    // 存取类的 static 变量
    void staticAccess(long val) 
        long startTime = System.currentTimeMillis();
        long tmp=staticVar;
        for (long i = 0; i < val; i++) 
            tmp += 1;
        
        staticVar=tmp;
        long endTime = System.currentTimeMillis();
        System.out.println("static var: " + (endTime - startTime) + " milli seconds");
    

    public static void main(String[] args) 
        VariableDemo2 test = new VariableDemo2();
        long count=10*1000*1000*1000L;
        test.tempAccess(count);
        test.instanceAccess(count);
        test.staticAccess(count);
    

计算结果:

temp var: 2973 milli seconds
instance var: 3074 milli seconds
static var: 3018 milli seconds

如果你喜欢本文

长按二维码关注

以上是关于Java中合理使用局部变量替代成员变量静态变量的主要内容,如果未能解决你的问题,请参考以下文章

Java中成员变量局部变量全局变量静态变量成员变量

Java中成员变量局部变量全局变量静态变量成员变量

java中静态变量的默认值是0?

java语言中,类的成员变量分配在哪个内存区?

Java 成员变量局部变量和静态变量的区别

Java中静态变量(类变量)实例变量局部变量和成员变量