I'm working on a high-performance project where Java 8's lambda functions are enormously useful. I've found, however, that they're memory inefficient when used en masse. For example, suppose I need to apply a lambda function 1,000,000 times:
for (int i = 0; i < 1_000_000; i += 1) {
collection.forEach(thing -> thing.foo());
}
In this form, thing -> thing.foo()
seems to instantiate 1,000,000 objects, which has nasty speed ramifications when done repeatedly. My standard optimization, where necessary, has been to do the following refactoring:
Consumer<Thing> DoFoo = thing -> thing.foo();
for (int i = 0; i < 1_000_000; i += 1) {
collection.forEach(DoFoo);
}
This removes the memory overhead and is trivial to do. This brings me to my question: if something so simple results in such big performance gains, why doesn't the Java compiler perform this refactoring for me? Are there other optimizations that could be made such that lambda performance/overhead is more predictable and efficient? If so, are they planned for release in future versions? Lambdas have proven wonderfully effective, but when I need to worry about low-level stuff when using them, it degrades the usefulness of their abstraction.
thing -> thing.foo()
gets instantiated on each pass? I tried writing a loop where each iteration ranSystem.out.println((Supplier<Object>) (() -> new Object()));
twice, and I found that each iteration printed the same two instances. (As in, it appeared that each lexical occurrence of() -> new Object()
resulted in only one lambda object being instantiated.) See ideone.com/4O0qqE. [continued]IdentityHashMap
), and still see how many distinct instances there are (by printing out theIdentityHashMap
at the end). As expected, it still shows that there are only two instances: ideone.com/4O0qqE.