如果在java里频繁存取变量,那么就需要考虑从何处存取这些变量?变量是 static 变量,还是堆栈变量,或者是类的实例变量?变量的存储位置对存取它的代码的性能有明显的影响?例如,请考虑下面这段代码:
class StackVars
{
private int instVar;
private static int staticVar;
//存取堆栈变量
void stackAccess(int val)
{
int j=0;
for (int i=0; i<val; i++)
j += 1;
}
//存取类的实例变量
void instanceAccess(int val)
{
for (int i=0; i<val; i++)
instVar += 1;
}
//存取类的 static 变量
void staticAccess(int val)
{
for (int i=0; i<val; i++)
staticVar += 1;
}
}
这段代码中的每个方法都执行相同的循环,并反复相同的次数。唯一的不同是每个循环使一个不同类型的变量递增。方法 stackAccess 使一个局部堆栈变量递增,instanceAccess 使类的一个实例变量递增,而 staticAccess 使类的一个 static 变量递增。
instanceAccess 和 staticAccess 的执行时间基本相同。但是,stackAccess 要快两到三倍。存取堆栈变量如此快是因为,JVM 存取堆栈变量比它存取 static 变量或类的实例变量执行的操作少。请看一下为这三个方法生成的字节码:
Method void stackAccess(int)
0 iconst_0 //将 0 压入堆栈。
1 istore_2 //弹出 0 并将它存储在局部分变量表中索引为 2 的位置 (j)。
2 iconst_0 //压入 0。
3 istore_3 //弹出 0 并将它存储在局部变量表中索引为 3 的位置 (i)。
4 goto 13//跳至位置 13。
7 iinc 2 1 //将存储在索引 2 处的 j 加 1。
10 iinc 3 1 //将存储在索引 3 处的 i 加 1。
13 iload_3//压入索引 3 处的值 (i)。
14 iload_1//压入索引 1 处的值 (val)。
15 if_icmplt 7//弹出 i 和 val。如果 i 小于 val,则跳至位置 7。
18 return //返回调用方法。
Method void instanceAccess(int)
0 iconst_0 //将 0 压入堆栈。
1 istore_2 //弹出 0 并将它存储在局部变量表中索引为 2 的位置 (i)。
2 goto 18//跳至位置 18。
5 aload_0//压入索引 0 (this)。
6 dup//复制堆栈顶的值并将它压入。
7 getfield #19 <Field int instVar>
//弹出 this 对象引用并压入 instVar 的值。
10 iconst_1 //压入 1。
11 iadd //弹出栈顶的两个值,并压入它们的和。
12 putfield #19 <Field int instVar>
//弹出栈顶的两个值并将和存储在 instVar 中。
15 iinc 2 1 //将存储在索引 2 处的 i 加 1。
18 iload_2//压入索引 2 处的值 (i)。
19 iload_1//压入索引 1 处的值 (val)。
20 if_icmplt 5//弹出 i 和 val。如果 i 小于 val,则跳至位置 5。
23 return //返回调用方法。
Method void staticAccess(int)
0 iconst_0 //将 0 压入堆栈。
1 istore_2 //弹出 0 并将它存储在局部变量表中索引为 2 的位置 (i)。
2 goto 16//跳至位置 16。
5 getstatic #25 <Field int staticVar>
//将常数存储池中 staticVar 的值压入堆栈。
8 iconst_1 //压入 1。
9 iadd //弹出栈顶的两个值,并压入它们的和。
10 putstatic #25 <Field int staticVar>
//弹出和的值并将它存储在 staticVar 中。
13 iinc 2 1 //将存储在索引 2 处的 i 加 1。
16 iload_2//压入索引 2 处的值 (i)。
17 iload_1//压入索引 1 处的值 (val)。
18 if_icmplt 5//弹出 i 和 val。如果 i 小于 val,则跳至位置 5。
21 return //返回调用方法。
查看字节码揭示了堆栈变量效率更高的原因。JVM 是一种基于堆栈的虚拟机,因此优化了对堆栈数据的存取和处理。所有局部变量都存储在一个局部变量表中,在 Java 操作数堆栈中进行处理,并可被高效地存取。存取 static 变量和实例变量成本更高,因为 JVM 必须使用代价更高的操作码,并从常数存储池中存取它们。(常数存储池保存一个类型所使用的所有类型、字段和方法的符号引用。)
通常,在第一次从常数存储池中访问 static 变量或实例变量以后,JVM 将动态更改字节码以使用效率更高的操作码。尽管有这种优化,堆栈变量的存取仍然更快。
考虑到这些事实,就可以重新构建前面的代码,以便通过存取堆栈变量而不是实例变量或 static 变量使操作更高效。请考虑修改后的代码:
class StackVars
{
//与前面相同...
void instanceAccess(int val)
{
int j = instVar;
for (int i=0; i<val; i++)
j += 1;
instVar = j;
}
void staticAccess(int val)
{
int j = staticVar;
for (int i=0; i<val; i++)
j += 1;
staticVar = j;
}
}
方法 instanceAccess 和 staticAccess 被修改为将它们的实例变量或 static 变量复制到局部堆栈变量中。当变量的处理完成以后,其值又被复制回实例变量或 static 变量中。这种简单的更改明显提高了 instanceAccess 和 staticAccess 的性能。这三个方法的执行时间现在基本相同,instanceAccess 和 staticAccess 的执行速度只比 stackAccess 的执行速度慢大约 4%。
这并不表示您应该避免使用 static 变量或实例变量。您应该使用对您的设计有意义的存储机制。例如,如果您在一个循环中存取 static 变量或实例变量,则您可以临时将它们存储在一个局部堆栈变量中,这样就可以明显地提高代码的性能。这将提供最高效的字节码指令序列供 JVM 执行。
分享到:
相关推荐
"java 变量命名心得" Java 变量命名是一个非常重要的概念,它直接影响着程序的可读性和维护性。下面我们将通过一个实例来展示局部变量和实例变量的区别,并总结 java 变量命名的几个典型问题。 首先,让我们来看...
23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23.java变量.zip23....
很多Java版本的软件和一些Java工具都需要用到该变量,设置`PATH`和`CLASSPATH`的时候也可以使用该变量以方便设置。 - **PATH**:指定一个路径列表,用于搜索可执行文件的。执行一个可执行文件时,如果该文件不能在...
Java环境变量配置 在"系统变量"下进行如下配置: (1)新建->变量名:JAVA_HOME变量值:D:\Java\jdk1.6.0_12(这只是我的JDK安装路径) (2)编辑->变量名:Path在变量值的最前面加上:%JAVA_HOME%\bin;%JAVA_HOME%\jre\...
Java 语言规定每个变量必须先声明,然后才能使用,声明变量时必须指定该变量的数据类型。在使用 new 操作符创建一个类的实例对象的时候,开始分配空间并将成员变量初始化为默认的数值。在这里并不是指将变量初始...
本文将深入探讨如何使用“Java环境变量设置器”来简化这一过程。 首先,我们要理解Java环境中主要的几个环境变量: 1. **JAVA_HOME**:这是最基础的环境变量,用于指向JDK的安装目录。设置这个变量,系统就能知道...
在Windows系统中,添加`JAVA_HOME`到`PATH`变量可以方便地在命令行中使用`java`和`javac`等命令。 **配置步骤:** 1. **打开环境变量编辑页面:** - Windows操作系统:同上文所述。 - Linux/macOS操作系统:无需...
- 有时这两个变量可以互换使用,但最好根据具体需求进行配置。 - 新建变量`CATALINA_HOME`或`CATALINA_BASE`,值同样为Tomcat的安装目录。 3. **更新Path变量**: - 编辑`Path`变量,添加Tomcat的bin目录:`%...
Java环境变量的配置是安装和使用Java开发工具(如JDK,Java Development Kit)的基础步骤,这对于运行Java程序、编译Java源代码以及使用各种Java开发工具至关重要。下面将详细解释如何配置Java环境变量,以及这些...
#### 一、什么是环境变量? 环境变量是操作系统用来传递信息给应用程序的一组变量,它们对于程序的执行非常重要。例如,通过环境变量,我们可以告诉系统在哪里可以找到特定的应用程序或库文件等。 #### 二、Java...
### Java成员变量覆盖问题详解 在Java编程语言中,成员变量是指定义在类中的变量,它们可以在类的方法、构造器或类的其他成员方法中访问和操作。成员变量的覆盖(有时会被误认为是“重写”)是理解Java继承机制的...
最后,CLASSPATH变量用于指示Java虚拟机(JVM)查找类文件的位置,但随着Java版本的更新,对于现代的Java应用,这个变量的使用已经减少,大多数情况下由"-cp"或"classpath"命令行选项替代。 Java环境变量添加工具的...
### JAVA环境变量设置详解 #### 一、概述 在计算机编程领域中,Java作为一种广泛使用的高级编程语言,其运行环境的正确配置对于开发者来说至关重要。本文将详细介绍如何在Windows操作系统中进行JAVA环境变量的设置...
### Java环境变量的配置 #### 一、概述 在计算机编程领域中,Java作为一种广泛使用的编程语言,其环境搭建对于初学者来说尤为重要。正确配置Java环境变量是确保系统能够识别并运行Java程序的基础。本文将详细介绍...
在Windows操作系统中安装Java环境变量是...在Linux中,可能需要编辑如.bashrc或/etc/profile等文件,然后使用export命令来设置环境变量。无论在哪种操作系统中,设置环境变量都是让系统能够识别Java命令的重要步骤。
java环境变量设置 java环境变量的配置是一个非常重要的步骤,因为它直接影响到java程序的运行和编译。...同时,在配置环境变量时,需要使用正确的路径和变量名,否则可能会导致java程序无法正确地运行和编译。
Java环境变量设置,.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; %JAVA_HOME%\bin %JAVA_HOME%\jre\bin
### JAVA读取环境变量 #### 知识点概述 在Java程序中,有时我们需要根据不同的运行环境(如Windows或Linux)来获取系统环境变量。这些环境变量包含了操作系统配置的重要信息,比如路径设置、用户信息等。本文将...