Accessor Method Performance and Optimization

2019-04-08 07:36发布

Often, I come across code where the Getter method is repeatedly used/abused to get some value or pass it as a method parameter, for ex:

public class Test {
   public void someMethod() {
      if(person.getName() != null && person.getName().equalsIgnoreCase("Einstein")) {
           method1(person.getName());
      }
      method2(person.getName());
      method3(person.getName());
      method4(person.getName());
   }
}

I usually code it, as below:

public class Test {
   public void someMethod() {
      String name = person.getName();
      if(name != null && name.equalsIgnoreCase("Einstein")) {
           method1(name);
      }
      method2(name);
      method3(name);
      method4(name);
   }

In my opinion, there is considerable memory/performance advantage in assigning the getter to a variable and using it, as Getters are Java methods and use stack frames. Is there really a considerable advantage in coding that way? }

6条回答
Animai°情兽
2楼-- · 2019-04-08 08:08

Have you profiled your opinion lately.

Performance:

This might have been a micro-optimization that was something to be concerned with in 1999-2001 in a pre-1.2 JVM and even then I would have questioned it unless some serious numbers showed otherwise.

Modern JIT implementations would tell you that today that your opinion is out of place.

Modern compiler implementations do all kinds of optimizations that make thinking about things like this a waste of time in Java. The JIT just makes it even more of a waste of concern.

Logic:

In a concurrent situation, your two code blocks are not logically equivalent, if you want to see changes, making the local copy will prevent that. Depending on what you want to do, one or the other approaches could create very subtle non-deterministic bugs that would be very hard to pin down in more complicated code.

Especially if what was return was something mutable unlike a String which is immutable. Then even the local copy could be changing, unless you did a deep clone, and that gets really easy to get wrong really quickly.

Concern yourself with doing it correctly, then measure and then optimize what is important as long as it doesn't make the code less maintainable.

The JVM will inline any calls to final instance members and remove the method call if there is nothing in the method call other than the return this.name; It knows that there is not logic in the accessor method and it knows the reference is final so it knows it can inline the value because it will not be changing.

To that end

person.getName() != null && person.getName().equalsIgnoreCase("Einstein") 

is expressed more correctly as

person != null && "Einstein".equalsIgnoreCase(person.getName())

because there is no chance of having a NullPointerException

Refactoring:

Modern IDE refactoring tools remove any arguments about having to change code in a bunch of places as well.

查看更多
爷的心禁止访问
3楼-- · 2019-04-08 08:15

It is certainly a lot more readable if you put the value in a local variable, which is why you should do it and not because it performs better.

Another massive difference is if getName() has a side effect (which it shouldn't have but you can't trust other classes blindly), or if you're working in a multi-threaded system. In the first case the difference is obvious, in the second case another thread might change the value getName() returns while you're executing your statement.

In both cases what you probably want is to only invoke getName() once.

查看更多
We Are One
4楼-- · 2019-04-08 08:15

I would agree in principle that pre-optimizing is evil, but I still prefer using the 2nd form. The main reason is that it is consistent with other cases other than getters where it would be foolish to repeatedly call it, such as an expensive operation:

if(person.expensiveOp() != null && person.expensiveOp().equalsIgnoreCase("Einstein")) {
       method1(person.expensiveOp());
  }
  method2(person.expensiveOp());
  method3(person.expensiveOp());
  method4(person.expensiveOp());

Also, with syntax highlighting IDEs, I can isolate all usages of a value. Someone will reply that you could also highlight all usages of the method. But there may be multiple calls to the same method on different instances of the object (i.e. toString())

查看更多
【Aperson】
5楼-- · 2019-04-08 08:21

There is no performance difference. If there is, it's trivially small.

More importantly, the two code samples actually behave entirely differently in the presence of multiple threads.

Say that halfway through, someone calls setName and changes the name. Now your code is going through an entirely unexpected path! This has actually been the root cause of a couple historical (even kernel-level) security vulnerabilities.

Note that I am not advocating blindly copying all results you get into local variables. I'm just pointing out a potential pitfall.

查看更多
该账号已被封号
6楼-- · 2019-04-08 08:23

I did some research on this topic, after asking the question and found what I was looking for. The problem was that I didn't use the right terminology, when framing the question. 'Inline Expansion' (Compiler Inlining) is what I was looking for. Here's a quote from Wiki:

In computing, inline expansion, or inlining, is a manual or compiler optimization that replaces a function call site with the body of the callee. This optimization may improve time and space usage at runtime, at the possible cost of increasing the final size of the program (i.e. the binary file size).

I also found that this topic has already been discussed on this site:

1) Do getters and setters impact performance in C++/D/Java?

Here's a quote from the above link:

In Java, the JIT compiler will probably inline it sooner or later. As far as I know, the JVM JIT compiler only optimizes heavily used code, so you could see the function call overhead initially, until the getter/setter has been called sufficiently often.

2) Inlining In Java

查看更多
Rolldiameter
7楼-- · 2019-04-08 08:25

If your reasoning for doing something in Java is because "it might perform better", you're doing it wrong. The JIT compiler will optimize the trivial case public String getName(){return name;} away. Instead, you should be making decisions based on, "is this the right OOP way to do it"

From an OOP standpoint, only use the getter. Then, a subclass can override the method as needed to do other fancy things (like ignore the name "Einstein").

查看更多
登录 后发表回答