性能微优化-优先局部变量
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