Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
The Dark Side Of Lambda
Expressions in Java 8
Java 8’s
biggest feature
in terms of the
language is
undoubtedly
Lambda
expressions.
The second biggest feature
(depending of course on who
you ask) is Nashorn – the new
JVM JavaScript engine that’s
supposed...
But these new
features have
a dark side
to them
www.takipi.com
The Java platform is built out of two main
components. The JRE, which JIT compiles and
executes bytecode, and the JDK whic...
The JVM was built to be
language agnostic in
the sense that it can
execute code written in
any language, and
bytecode comp...
But the farther away you
get from Java – the more
that distance grows.
www.takipi.com
When you look at
Scala which is a
functional
language, the
distance between
the source code and
the executed
bytecode is p...
When you look at fully dynamic
languages such as JavaScript,
that distance becomes huge.
www.takipi.com
And now with Java 8, this is
beginning to creep into Java
as well.
www.takipi.com
So why should
I care?
www.takipi.com
What you’re writing and
what you’re debugging will
be two different things.
See the Example below
www.takipi.com
This is the
traditional method
by which we would
iterate over a list of
strings to map their
lengths.
// simple check agai...
This will throw an exception if an empty string
is passed. The stack trace will look like –
This is what most Java devs ar...
Scala and Java 8
Let’s Look
www.takipi.com
Scala
val lengths = names.map(name => check(name.length))1
The iteration is carried
out by the framework
(i.e. internal it...
at Main$.check(Main.scala:6)
at Main$$anonfun$1.apply(Main.scala:12)
at Main$$anonfun$1.apply(Main.scala:12)
at scala.coll...
Let’s look at the
corresponding Java 8 code,
and the resulting call stack.
Stream lengths = names.stream().map(name -> che...
More concise code with more complex
debugging, and longer synthetic
call stacks.
The reason is that while javac has been e...
JavaScript in Java 8
Java 8 introduces a brand new
JavaScript compiler. Now we can
finally integrate Java + JS in an
effic...
In this case the bytecode code
is dynamically generated at
runtime using a nested tree of
Lambda expressions. There is
ver...
Additional Reading
https://plumbr.eu/blog/memory-leaks/reducing-memory-usage-with-string-intern
http://vanillajava.blogspo...
www.takipi.com
Originally Posted on
Takipi Blog
Takipi – God Mode in Production Code
Upcoming SlideShare
Loading in …5
×

The Dark Side Of Lambda Expressions in Java 8

46,455 views

Published on

The dark side of Java 8’s biggest features - Takipi

Published in: Software

The Dark Side Of Lambda Expressions in Java 8

  1. 1. The Dark Side Of Lambda Expressions in Java 8
  2. 2. Java 8’s biggest feature in terms of the language is undoubtedly Lambda expressions.
  3. 3. 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.
  4. 4. But these new features have a dark side to them www.takipi.com
  5. 5. The Java platform is built out of two main components. The JRE, which JIT compiles and executes bytecode, and the JDK which contains dev tools and the javac source compiler. I’ll explain. www.takipi.com
  6. 6. 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
  7. 7. But the farther away you get from Java – the more that distance grows. www.takipi.com
  8. 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
  9. 9. When you look at fully dynamic languages such as JavaScript, that distance becomes huge. www.takipi.com
  10. 10. And now with Java 8, this is beginning to creep into Java as well. www.takipi.com
  11. 11. So why should I care? www.takipi.com
  12. 12. What you’re writing and what you’re debugging will be two different things. See the Example below www.takipi.com
  13. 13. This is the traditional method by which we would iterate over a list of strings to map their lengths. // simple check against empty strings public 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 & 71 2 3 4 5 6 7 8 9 10 11 12 13 14 15 www.takipi.com
  14. 14. 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) 1 2 www.takipi.com
  15. 15. Scala and Java 8 Let’s Look www.takipi.com
  16. 16. 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
  17. 17. 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) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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
  18. 18. 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) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 And now in java 8 www.takipi.com
  19. 19. 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
  20. 20. 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); 1 2 3 4 5 6 7 www.takipi.com
  21. 21. In this case the bytecode code is dynamically generated at runtime using a nested tree of Lambda expressions. There is very little correlation between our source code, and the resulting bytecode executed by the JVM. The call stack is now two orders of magnitude longer. www.takipi.com
  22. 22. 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
  23. 23. www.takipi.com Originally Posted on Takipi Blog Takipi – God Mode in Production Code

×