Is extraction up to the static finale needed to optimize Java?

Consider this method:

private void iterate(List<Worker> workers) { SortedSet<Worker> set = new TreeSet<>(new Comparator<Worker>() { @Override public int compare(Worker w0, Worker w1) { return Double.compare(w0.average, w1.average); } }); // ... } 

As you can see, the set creates a new TreeSet with a custom comparator.

I was wondering if this makes no difference to the performance / memory / garbage collection / any point of view if I did this and instead pollute outer space:

 static final Comparator<Worker> COMPARATOR = new Comparator<Worker>() { @Override public int compare(Worker w0, Worker w1) { return Double.compare(w0.average, w1.average); } }; private void iterate(List<Worker> workers) { SortedSet<Worker> set = new TreeSet<>(COMPARATOR); // ... } 

The reason I'm asking is because I believe that the compiler should already figure this out and optimize it for me, so I don't need to extract it, right?

The same applies to strings or other temporary immutable objects declared in a method.

It doesn't matter to extract the final variable?

Note. I am aware of the small performance impact that this can give. The question is whether there is any difference how insignificant.

+5
source share
2 answers

There will be a difference.

  • CPU Impact: Assigning to a static one reduces the amount of work needed to distribute a new comparator each time
  • GC effect: each time assigning a new object, and then immediately discarding it, it will not have any young GC value; however, assigning it to a variable will increase GC times (very very slightly), since this is an additional set of links that you will need to go through. Dead objects cost nothing, living objects do.
  • Memory effect: assigning a comparator constant will reduce the amount of memory that each method call will need, in exchange for low constant overheads that will be moved to a space with a limited amount of GC.
  • Risk of reference screens: Inner classes contain pointers to the class that built it. If the inner class (Comparator) ever came back from the method that created it, then a strong reference to the parent could avoid and prevent the parent's GC. Purely getcha, which can creep into code, in this example this is not a problem.

An access point is very good for inlining, but it is unlikely that the comparator can be allocated on the heap or moved to a constant. But this will depend on the contents of the TreeSet. If the implementation of TreeSet was very simple (and small), then it could be embedded, however we all know that this is not so. Also, the TreeSet is encoded as general if it was used with only one type of object (Worker), then there are some optimizations that the JVM can use, however, we must assume that the TreeSet will also be used by other types, and therefore the TreeSet will not be able to do any suggestion that the comparator is in it.

Thus, the difference between the two versions is mainly in the distribution of objects. Using the final keyword is unlikely to improve performance, since Hotspot basically ignores the last keyword.

Java 8 has a very interesting behavior when using lambdas. Consider the following version of your example:

 import java.util.*; public class T { public void iterate(List<String> workers) { SortedSet<Double> set = new TreeSet<>( Double::compare ); } } 

Run 'javap -c T.class' and you will see the following jvm code:

  public void iterate(java.util.List<java.lang.String>); Code: 0: new #2 // class java/util/TreeSet 3: dup 4: invokedynamic #3, 0 // InvokeDynamic #0:compare:()Ljava/util/Comparator; 9: invokespecial #4 // Method java/util/TreeSet."<init>":(Ljava/util/Comparator;)V 12: astore_2 13: return 

It is great to note here that there is no object construction for lambda. invokedynamic will have a higher cost on the first call, and then it will be cached efficiently.

+2
source

One big difference is caused by the β€œhidden” implication that anonymous classes contain an implicit reference to the contained class, so if you pass this TreeSet to another process, the reference to the class instance is stored through the TreeSet through the anonymous Comparator to another piece of code, so your instance will not collect rubbish.

This may cause a memory leak.

Option 2, however, does not suffer from this problem.

Otherwise, it is a matter of style.


In java 8, you can use the lambda expression instead, which is the best of both worlds.

+1
source

Source: https://habr.com/ru/post/1233220/


All Articles