Skip to content

性能微优化-优先局部变量

About 522 wordsAbout 2 min

性能新书

2026-08-20

6.8 优先用局部变量

当存取类变量的时候,Java使用虚拟机指令 GETFIELD 获取类变量,如果存取方法的变量,则通过出栈操作获取到变量,GETFIELD,从Heap里取值,有较慢的速度,而出栈操作有较快的速度,因此,如果需要频繁操作类变量的时候,最好先赋值给一个局部变量,如下代码是一个简单测试

public class CharArrayTest {
   char[] array = "hello,this is effective java code....".toCharArray();
   public int testGetField() {
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == 't') {
                count++;
            }
        }
        return count;
    }
}

array是一个字符数组,为了测试效果明显,需要设置一个较长的内容,testGetField用于判断array包含多少个字母't',array是一个类变量,每次循环比较都将使用此变量,如果我们改成局部变量,则代码如下

public void testLocalVariable() {
    int count = 0;
    // 使用局部变量
    char[] locaArray = array;
    for (int i = 0; i < locaArray.length; i++) {
        if (locaArray[i] == 't') {
            count++;
        }
    }
}

使用JMH运行CharArrayTest 测试,会发现testGetField和testLocalVariable性能几乎是一样的,这是什么原因呢?这主要是尽管从HEAP中获取类变量理论上较慢,但由于CPU缓存的原因,并不是每次都需要从HEAP取变量,有可能是从CPU缓存里取,因此俩者在JMH测试中很难验证哪一个性能更好。

Benchmark                                   Mode       Score     Units     
c.i.c.c.CharArrayTest.testGetField         thrpt   69736.098    ops/ms     
c.i.c.c.CharArrayTest.testLocalVariable    thrpt   71601.632    ops/ms

但在实际项目代码,并不是每次都会从CPU缓存里取出(比如,CPU缓存里保存了其他变量),我们在2.5节分析过String.replace方法,便是迭代局部变量,避免getfield指令

//Java 8
public String replace(char oldChar, char newChar) {
  int len = value.length;
  int i = -1;
  char[] val = value; /* 避免使用getfield指令 */
  .......
}

ReplaceTest代码的replaceByLocal测试String.replace方法,而replaceByGetField则直接使用类变量迭代

replaceByLocal方法的性能会更好点

Benchmark                                 Mode        Score     Units
c.i.c.c.ReplaceTest.replaceByGetField    thrpt    29516.138    ops/ms
c.i.c.c.ReplaceTest.replaceByLocal       thrpt    25117.313    ops/ms

知行合一