Performance & the JVM

GC choice (G1 default, ZGC/Shenandoah for low latency), heap sizing (-Xms, -Xmx), -XX:+UseZGC, JIT warm-up, GraalVM native-image for fast startup.

38.1 Tips

Choose the right collection (Vector/ArraySeq for indexed access, Array for hot numeric loops).

Avoid repeated :+ on List; build with :: + reverse or use builders (List.newBuilder).

Use view/iterator to avoid intermediate collections in long pipelines.

Beware boxing in generic code; use Array[Int], specialized libraries (Spire), or inline.

Use while loops in performance-critical inner loops.

Prefer StringBuilder/mkString over repeated concatenation.

Use lazy val judiciously (it has synchronization cost).

Opaque types instead of wrapper classes.

Profile before optimizing.

38.2 Benchmarking & profiling

JMH via sbt-jmh:

Profilers: async-profiler, VisualVM, JDK Flight Recorder / Mission Control, YourKit.

import org.openjdk.jmh.annotations.*
@State(Scope.Thread)
class Bench:
  val xs = (1 to 10000).toList
  @Benchmark def sumList: Int = xs.sum
  @Benchmark def sumWhile: Int = { var i = 0; var s = 0; val a = xs.toArray; while i < a.length do { s += a(i); i += 1 }; s }
sbt "Jmh/run -i 5 -wi 3 -f1"

38.3 JVM tuning

GC choice (G1 default, ZGC/Shenandoah for low latency), heap sizing (-Xms, -Xmx), -XX:+UseZGC, JIT warm-up, GraalVM native-image for fast startup.

38.4 Compile times

Keep implicits/givens scoped, avoid huge derivations, modularize builds, use incremental compilation (Zinc), Bloop, and remote caching.