简体   繁体   中英

Why static/member variable are slower than local variable?

I've seen this thread: Speed of if compared to conditional

Made my own class for checking the speed

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");
}

}

Result:

Type Global: 45693028 ns
Type Static: 43853723 ns
Type Local: 2057505 ns

In the answer @Rod_algonquin answered that it is because of the getstatic / putstatic bytecode for static variable and getfield / putfield bytecode for the member variable, that it is calculating through bit-shifting and some addition.

At first I thought that only Objects causes this, but upon trying to reference primitive the result is the same local variable is still faster .

Why is that local variable is faster? except the bytecode explanation.

In general, jvm uses three different memory segments

  1. Heap - contains all created objects in runtime, objects only plus their object attributes (instance variables)
  2. Stack - contains local variables and Reference variables(variables that hold the address of an object in the heap)
  3. Code segment - the segment where the actual compiled Java bytecodes resides when loaded

Stack values only exist within the scope of the function they are created in. Once it returns, they are discarded.

Heap values however exist on the heap. They are created at some point in time, and destructed at another (either by GC or manually). Java only stores primitives on the stack. This keeps the stack small and helps keeping individual stack frames small, thus allowing more nested calls. Objects are created on the heap, and only references (which in turn are primitives) are passed around on the stack.

Java uses three different kind of variables

  • Static Variable : - Class variables are called static variables. There is only one occurrence of a class variable per JVM per class loader. When a class is loaded the class variables (aka static variables) are initialized. They reside where the class(bytecode) resides, which is in the Code Segment
  • Instance variables:- Instance variables are non-static and there is one occurrence of an instance variable in each class instance (ie each object). Also known as a member variable or a field.
  • Local variables: Local variables have a narrower scope than instance variables. The lifetime of a local variable is determined by execution path

Accessing the Stack is relatively faster (though, it is purely JVM implementation specific) than Code segment and hence theistically accessing local variable is faster than global.

For details, you can check http://blog.jamesdbloom.com/JVMInternals.html or it-haggar_bytecode

There is another article that analyze the performance of static vs local variable

youre a victim of runtime optimization :-)

if you change your code a little:

Question q = new Question();
for (int i=0; i<2; i++) {
    q.executeGlobal();
    q.executeStatic();
    q.executeLocal();
}

you get this:

Type Global: 38331943 ns
Type Static: 57761889 ns
Type Local: 3010189 ns
Type Global: 46249688 ns
Type Static: 52745009 ns
Type Local: 0 ns

what happens is that pretty soon the runtime realizes that your local variable keeps getting assigned, but never gets read (or used), and optimizes out the whole loop.

as for the difference between class instance fields and static fields, theyre both on the heap, but the static fields are shared across all object instances, so there's an extra level of indirection

Another reason could be the cache missing.

Cache misses bring a ~250% penalty , when CPU needs to refresh it's cache (that means the variable you are trying to access isn't in the CPU cache is in RAM).

Looking at the results of your test, it seems to be a cache miss problem :

Your local variables (mLocal and i ) are accessed in every cycle, but they are close to one another in memory since they were added to the stack recently.

mPublic and mStatic aren't within the same page of memory as "i". So your for loop should switch memory pages between the page where "i" with the page where mPublic / mStatic is .

Ofc you cannot know how memory actually gets mapped, so this is just a guess.

If you are at it, could you do another experiment ? Declare a static integer next to the static mStatic variable and use that integer in the loop. Is performance improving ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM