Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is repeatedly instantiating an anonymous class wasteful?

Tags:

java

I had a remark about a piece of code in the style of:

Iterable<String> upperCaseNames = Iterables.transform(
    lowerCaseNames, new Function<String, String>() {
        public String apply(String input) {
            return input.toUpperCase();
        }
    });

The person said that every time I go through this code, I instantiate this anonymous Function class, and that I should rather have a single instance in, say, a static variable:

static Function<String, String> toUpperCaseFn =
    new Function<String, String>() {
        public String apply(String input) {
            return input.toUpperCase();
        }
    };
...
Iterable<String> upperCaseNames =
    Iterables.transform(lowerCaseNames, toUpperCaseFn);

On a very superficial level, this somehow makes sense; instantiating a class multiple times has to waste memory or something, right?

On the other hand, people instantiate anonymous classes in middle of the code like there's no tomorrow, and it would be trivial for the compiler to optimize this away.

Is this a valid concern?

like image 954
user711413 Avatar asked Oct 31 '13 23:10

user711413


People also ask

Why would you use an anonymous class rather than an inner class?

Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.

Which of the following is true for an anonymous class?

18) Which of the following is true about the anonymous inner class? Explanation: Anonymous inner classes are the same as the local classes except that they don't have any name. The main use of it is to override methods of classes or interfaces.

Can an anonymous class be declared as implementing an interface and extending a class?

A normal class can implement any number of interfaces but the anonymous inner class can implement only one interface at a time. A regular class can extend a class and implement any number of interfaces simultaneously. But anonymous Inner class can extend a class or can implement an interface but not both at a time.


1 Answers

Fun fact about Hot Spot JVM optimizations, if you instantiate an object that isn't passed outside of the current method, the JVM will perform optimizations at the bytecode level.

Usually, stack allocation is associated with languages that expose the memory model, like C++. You don't have to delete stack variables in C++ because they're automatically deallocated when the scope is exited. This is contrary to heap allocation, which requires you to delete the pointer when you're done with it.

In the Hot Spot JVM, the bytecode is analyzed to decide if an object can "escape" the thread. There are three levels of escape:

  1. No escape - the object is only used within the method/scope it is created, and the object can't be accessed outside the thread.
  2. Local/Arg escape - the object is returned by the method that creates it or passed to a method that it calls, but none of those methods will put that object somewhere that it can be accessed outside of the thread.
  3. Global escape - the object is put somewhere that it can be accessed in another thread.

This basically is analogous to the questions, 1) do I pass it to another method or return it, and 2) do I associate it with something attached to a GC root like a ClassLoader or something stored in a static field?

In your particular case, the anonymous object will be tagged as "local escape", which only means that any locks (read: use of synchronized) on the object will be optimized away. (Why synchronize on something that won't ever be used in another thread?) This is different from "no escape", which will do allocation on the stack. It's important to note that this "allocation" isn't the same as heap allocation. What it really does is allocates space on the stack for all the variables inside the non-escaping object. If you have 3 fields, int, String, and MyObject inside the no-escape object, then three stack variables will be allocated: an int, a String reference, and a MyObject reference – the MyObject instance itself will still be stored in heap unless it is also analyzed to have "no escape". The object allocation is then optimized away and constructors/methods will run using the local stack variables instead of heap variables.

That being said, it sounds like premature optimization to me. Unless the code is later proven to be slow and is causing performance problems, you shouldn't do anything to reduce its readability. To me, this code is pretty readable, I'd leave it alone. This is totally subjective, of course, but "performance" is not a good reason to change code unless it has something to do with its actual running time. Usually, premature optimization results in code that's harder to maintain with minimal performance benefits.

Java 8+ and Lambdas

If allocating anonymous instances still bothers you, I recommend switching to using Lambdas for single abstract method (SAM) types. Lambda evaluation is performed using invokedynamic, and the implementation ends up creating only a single instance of a Lambda on the first invocation. More details can be found in my answer here and this answer here. For non-SAM types, you will still need to allocate an anonymous instance. The performance impact here will be negligible in most use cases, but IMO, it's more readable this way.

References

  • Escape analysis (wikipedia.org)
  • HotSpot escape analysis 14 | 11 | 8 (oracle.com)
  • What is a 'SAM type' in Java? (stackoverflow.com)
  • Why are Java 8 lambdas invoked using invokedynamic? (stackoverflow.com)
like image 188
Brian Avatar answered Nov 14 '22 03:11

Brian