Top Banner
The Dark Side Of Lambda Expressions in Java 8
23

The Dark Side Of Lambda Expressions in Java 8

Jul 15, 2015

Download

Software

Takipi
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: The Dark Side Of Lambda Expressions in Java 8

The Dark Side Of Lambda Expressions in Java 8

Page 2: The Dark Side Of Lambda Expressions in Java 8

Java 8’s biggest feature in terms of the language is undoubtedly Lambda expressions.

Page 3: The Dark Side Of Lambda Expressions in Java 8

The second biggest feature (depending of course on who you ask) is Nashorn – the new JVM JavaScript engine that’s supposed to bring Java up to par with other JS engines such as V8 and its node.js container.

Page 5: The Dark Side Of Lambda Expressions in Java 8

The Java platform is built out of two main components. The JRE, which JIT compiles and executes bytecode, and the JDK which contains devtools and the javac source compiler.

I’ll explain.

www.takipi.com

Page 6: The Dark Side Of Lambda Expressions in Java 8

The JVM was built to be language agnostic in the sense that it can execute code written in any language, and bytecode compiled from Java source will pretty much resemble it structurally.

www.takipi.com

Page 7: The Dark Side Of Lambda Expressions in Java 8

But the farther away you get from Java – the more

that distance grows.

www.takipi.com

Page 8: The Dark Side Of Lambda Expressions in Java 8

When you look at Scala which is a functional language, the distance between the source code and the executed bytecode is pretty big.

www.takipi.com

Page 9: The Dark Side Of Lambda Expressions in Java 8

When you look at fully dynamic languages such as JavaScript, that distance becomes huge.

www.takipi.com

Page 12: The Dark Side Of Lambda Expressions in Java 8

What you’re writing andwhat you’re debugging willbe two different things.

See the Example below

www.takipi.com

Page 13: The Dark Side Of Lambda Expressions in Java 8

This is the traditional method by which we would iterate over a list of strings to map their

lengths.

// simple check against empty stringspublic static int check(String s) {if (s.equals("")) {throw new IllegalArgumentException();}return s.length();}

//map names to lengths

List lengths = new ArrayList();

for (String name : Arrays.asList(args)) {lengths.add(check(name));}

Java 6 & 7123456789101112131415

www.takipi.com

Page 14: The Dark Side Of Lambda Expressions in Java 8

This will throw an exception if an empty string is passed. The stack trace will look like –

This is what most Java devs are used to.

at LmbdaMain.check(LmbdaMain.java:19)at LmbdaMain.main(LmbdaMain.java:34)

12

www.takipi.com

Page 16: The Dark Side Of Lambda Expressions in Java 8

Scala

val lengths = names.map(name => check(name.length))1

The iteration is carried out by the framework (i.e. internal iteration).

Lambda expression to map the string lengths

www.takipi.com

Page 17: The Dark Side Of Lambda Expressions in Java 8

at Main$.check(Main.scala:6)at Main$$anonfun$1.apply(Main.scala:12)at Main$$anonfun$1.apply(Main.scala:12)at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)at scala.collection.immutable.List.foreach(List.scala:318)at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)at scala.collection.AbstractTraversable.map(Traversable.scala:105)at Main$delayedInit$body.apply(Main.scala:12)at scala.Function0$class.apply$mcV$sp(Function0.scala:40)at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)at scala.App$$anonfun$main$1.apply(App.scala:71)at scala.App$$anonfun$main$1.apply(App.scala:71)at scala.collection.immutable.List.foreach(List.scala:318)at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)at scala.App$class.main(App.scala:71)at Main$.main(Main.scala:1)at Main.main(Main.scala)

123456789101112131415161718

The call stack is an order of magnitude longer, and much harder to understand.

Remember, this example is very simple. With real-world nested Lambdas and complex structures you’ll be looking at much longer synthetic call stacks, from which you’ll need to understand what happened.This has long been an issue with Scala, and one of

the reasons we built the Scala Stackifier.

www.takipi.com

Page 18: The Dark Side Of Lambda Expressions in Java 8

Let’s look at the corresponding Java 8 code, and the resulting call stack.

Stream lengths = names.stream().map(name -> check(name));

at LmbdaMain.check(LmbdaMain.java:19)at LmbdaMain.lambda$0(LmbdaMain.java:37)at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source)at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)at java.util.stream.LongPipeline.sum(LongPipeline.java:396)at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)at LmbdaMain.main(LmbdaMain.java:39)

123456789101112131415

And now in java 8

www.takipi.com

Page 19: The Dark Side Of Lambda Expressions in Java 8

More concise code with more complex debugging, and longer synthetic

call stacks.

The reason is that while javac has been extended to support Lambda functions, the JVM still remains

oblivious to them. This has been a design decision by the Java folks in order to to keep the JVM

operating at a lower-level, and without introducing new elements into its specification.

www.takipi.com

Page 20: The Dark Side Of Lambda Expressions in Java 8

JavaScript in Java 8

Java 8 introduces a brand new JavaScript compiler. Now we can finally integrate Java + JS in an efficient and straightforward manner. However, nowhere is the dissonance between the code we write and the code we debug bigger than here.

Here’s the same function in Nashorn

ScriptEngineManager manager = new ScriptEngineManager();

ScriptEngine engine = manager.getEngineByName("nashorn");

String js = "var map = Array.prototype.map \n";

js += "var a = map.call(names, function(name) { return Java.type(\"LmbdaMain\").check(name) }) \n";

js += "print(a)";

engine.eval(js);

1234567

www.takipi.com

Page 21: The Dark Side Of Lambda Expressions in Java 8

In this case the bytecode codeis dynamically generated atruntime using a nested tree ofLambda expressions. There isvery little correlation betweenour source code, and theresulting bytecode executed bythe JVM. The call stack is nowtwo orders of magnitude longer.

www.takipi.com

Page 22: The Dark Side Of Lambda Expressions in Java 8

Additional Reading

https://plumbr.eu/blog/memory-leaks/reducing-memory-usage-with-string-intern

http://vanillajava.blogspot.co.il/2013/04/low-gc-coding-efficient-listeners.html

http://java-performance.info/primitive-types-collections-trove-library/

www.takipi.com