Java and Memory Management
- Introduction
- Java Performance Optimization
- List of Performance Analysis Tools
- Garbage Collection and Heap Offloading
- Java Tracing Tools. JDK Flight Recorder
- Cambios importantes en la gestión de memoria de Java 8 de Oracle (2014)
- Slides
- Tweets
Introduction
- javarevisited.blogspot.com: 10 Things Java Programmers Should Learn in 2022
- freecodecamp.org: Learn the Basics of Java Programming
- freecodecamp.org: Advanced Object-Oriented Programming in Java – Full Book
Java Performance Optimization
- DZone refcard: java performance optimization 🌟 Tools and Techniques for Turbocharged Apps
- DZone: String Concatenation’s Effect on Performance Don’t use the string concatenation operator to combine more than a few strings unless performance is irrelevant. Use StringBuilder’s append method instead.
- DZone: Performance Improvement in Java Applications: ORM/JPA 🌟
- DZone: The JVM Architecture Explained 🌟 An overview of the different components of the JVM, along with a very useful diagram
- DZone: How to Troubleshoot Sudden CPU Spikes Your Java application has been running fine, but all of a sudden CPU consumption starts to go higher and higher… sound familiar?
- DZone refcard: Java Caching
- Dzone: 7 JVM Arguments of Highly Effective Applications 🌟🌟🌟 How to use 7 JVM arguments to help increase your application’s performance and avoid common memory pitfalls.
- developers.redhat.com: Get started with JDK Flight Recorder in OpenJDK 8u 🌟 Deploy JDK Flight Recorder with JDK Mission Control, a new monitoring and profiling tool that exposes a high level of information without adding a tax on the runtime system
- blog.heaphero.io: HeapHero - Java & Android Heap Dump Analyzer
- blog.heaphero.io: What is GC Log, thread dump and Heapdump? 🌟 Java Virtual Machine (JVM) generates 3 critical #artifacts that are useful for optimizing the performance and troubleshooting production problems. Those artifacts & their differences are explained in this PDF.
- developers.redhat.com: Shenandoah garbage collection in OpenJDK 16: Concurrent reference processing
- developers.redhat.com: JDK Flight Recorder support for GraalVM Native Image: The journey so far 🌟
- OpenHFT/Java-Thread-Affinity Bind a java thread to a given core. A library that lets you pin the threads of your Java application to specific CPU cores. Looks like an interesting part of the performance engineering toolbox, e.g. helping to reduce the number of cache misses.
- dzone.com: Flight Recorder: Examining Java and Kotlin Apps Learn how to use Java’s Flight Recorder to profile Java and Kotlin apps and get a close look at JVM internals.
- kstefanj.github.io: GC progress from JDK 8 to JDK 17 JVM with <5ms GC pauses (ZGC). JDK17 is a huge leap forward in benchmark after benchmark. Upgrade as fast as you can. Amazon’s Corretto builds are available for a huge number of platforms and distribution channels. The JRE disappeared with jdk9: use jlink to assemble exactly the JRE you need.
- developers.redhat.com: How to choose the best Java garbage collector
- linkedin.com/pulse: Difference between Executor, ExecutorService, and Executors class in Java! - original article - javarevisited.blogspot.com
- vladmihalcea.com: Caching best practices
- vladmihalcea.com: 14 High-Performance Java Persistence Tips
- speakerdeck.com: Profiling a Java Application @DevDays 2023 | Victor Rentea
- freecodecamp.org: How to Write Unit Tests in Java
Java on Kubernetes. Java Memory Arguments for Containers
- medium: How to reduce your JVM app memory footprint in Docker and Kubernetes 🌟
- tech.olx.com: Improving JVM Warm-up on Kubernetes 🌟 Vikas Kumar explains why you should not run your Java applications with a fixed quota of a single CPU core. Instead, use Burstable QoS to allow for increased CPU usage during start-up.
- dzone: Best Practices: Java Memory Arguments for Containers 🌟 In this article, we will discuss the possible JVM arguments that can be used to specify the Java heap size and the best option to choose.
- medium.com/@anurag2397: Tuning JVM containers for better CPU and memory utilisation in K8s environment In this article, you’ll discuss JVM warmup issues, high heap memory utilisation and how those affect Java apps deployed in Kubernetes. You’ll then learn how to work around them.
- danoncoding.com: Tricky Kubernetes memory management for Java applications 🌟 Running Java applications in a container environment requires an understanding of both — JVM memory mechanics and Kubernetes memory management. In this article, you will discuss the settings and optimizations necessary to run Java apps in Kubernetes.
- medium.com/nordnet-tech: Setting Java Heap Size Inside a Docker Container
- danoncoding.com: Tricky Kubernetes memory management for Java applications 🌟 How to use the Kubernetes memory requests and limits in combination with JVM Heap and stay out of trouble.
- medium.com/@sharprazor.app: Memory settings for Java process running in Kubernetes pod Managing the memory usage of a Java process running in a Kubernetes pod is more challenging than one might expect. Even with proper JVM memory configurations, OOMKilled issues can still arise and you wonder why.
- There is no way to guarantee the complete memory bundary of a Java process since the JVM respects only the heap size limit; not non-heap memory, which will depend on various factors. Start with a 75% ratio of heap to non-heap memory, and keep a close watch on how your memory behaves. If things get out of hand, you can tweak your pod’s memory limits or fiddle with the heap-to-non-heapratio to dodge the OOMKilled mishaps.
- medium.com/codex: Running JVM Applications on Kubernetes: Beyond java -jar Discover some important tips about running JVM applications in containerized environments orchestrated by Kubernetes. The article provides essential tips for optimizing JVM applications running on Kubernetes, focusing on ergonomics, memory sizing, CPU overbooking, and HPA configuration
- lalitchaturveditech.medium.com: Optimize Java Performance On Kubernetes
- blog.flipkart.tech: The Art of System Debugging — Decoding CPU Utilization 🌟
- Learn how to debug CPU utilization issues in a Java app using asynchronous programming techniques like CompletableFuture
- Discover how to identify and resolve CPU bottlenecks using JVM arguments and container resource allocation
- Another workaround for this issue was to set the “-XX: ActiveProcessorCount” JVM argument to the number of cores that are allocated to the java container. We found this recommendation in a comment on the openjdk issue tracker. The application team validated this and the central Load Tests were run with this workaround. Post the load tests, the application team upgraded the java version to 17 where these issues were already resolved.
Benchmarking modern Java Virtual Machines and the next-generation garbage collectors
- jet-start.sh: Performance of Modern Java on Data-Heavy Workloads, Part 1 🌟 The Java runtime has been evolving more rapidly in recent years and, after 15 years, we finally got a new default garbage collector: the G1. Two more GCs are on their way to production and are available as experimental features: Oracle’s ZGC and OpenJDK’s Shenandoah. We at Hazelcast thought it was time to put all these new options to the test and find which choices work well with workloads typical for our distributed stream processing engine, Hazelcast Jet.
Relevant JVM Metrics
Metric | Details / Reference |
---|---|
GC Throughput | Repeated Full GC happens way before OutOfMemoryError ref1 ref2 |
etc |
Common JVM Errors
JVM Error | Details / Reference |
---|---|
OutOfMemoryError | Repeated Full GC happens way before OutOfMemoryError ref1 ref2 |
StackOverflowError | ref |
etc |
Tuning Jenkins GC
- jenkins.io - Tuning Jenkins GC For Responsiveness and Stability with Large Instances 🌟
- Running Jenkins on Java 11 🌟
Tuning Java Containers
- blog.openshift.com: Scaling Java Containers 🌟
- blog.openshift.com: Performance Metrics (APM) for Spring Boot Microservices on OpenShift
- dzone.com: Java RAM Usage in Containers: Top 5 Tips for Not Losing Your Memory
- dzone.com: Running a JVM in a Container Without Getting Killed: Starting in JDK 9, and earlier if you use JDK 8u131, your JVM can detect how much memory is available when running inside a Docker container.
- dzone.com: Java Inside Docker: What You Must Know to Not FAIL If you’ve tried Java in containers, particularly Docker, you might have encountered some problems with the JVM and heap size. Here’s how to fix it.
- itnext.io: How to cold start fast a java service on k8s (EKS)
- blog.gceasy.io: Best practices: Java memory arguments for Containers 🌟
Debugging java applications on OpenShift and Kubernetes
List of Performance Analysis Tools
- en.wikipedia.org/wiki/List_of_performance_analysis_tools
- InspectIT
- VisualVM
- OverOps
- FusionReactor
- etc
Threadumps, Heapdumps and GC Analysis Tools
- geekflare.com: What is Thread Dump and How to Analyze them? 🌟
- baeldung.com: How to Analyze Java Thread Dumps 🌟
- baeldung.com: Capturing a Java Thread Dump
- tier1app.com
- fastthread.io
- gceasy.io
- heaphero.io
Garbage Collection and Heap Offloading
- Tecnologías de Heap-Offloading son EHcache, Memcached, Jillegal library, etc.
- Jillegal OffHeap Module
- Free eGuide: JVM Troubleshooting Guide
- Cambios importantes en la gestión de memoria de Java 8 de Oracle
- PermGen eliminado
- On heap vs off heap memory usage
- How Garbage Collection differs in the three big JVMs
- cubrid.org: How to Tune Java Garbage Collection
- DZone: Revisiting the Advanced Theories of ‘Java Garbage Collection’ 🌟
- DZone: Understanding the Java Memory Model and Garbage Collection 🌟 In this article we will try to understand the Java memory model and how garbage collection works. In this article I have used JDK8 Oracle Hot Spot 64 bit JVM. First let me depict the different memory areas available for Java processes.
- DZone: Memory Leaks and Java Code When you aren’t using objects, but they aren’t touched by GC, a memory leak happens. Here are six ways memory leaks happen to look for and avoid.
- javarevisited.blogspot.com: How Garbage Collection works in Java? Explained (2011)
Java Tracing Tools. JDK Flight Recorder
- Byteman
- developers.redhat.com: Collect JDK Flight Recorder events at runtime with JMC Agent 🌟
- developers.redhat.com: Checkpointing Java from outside of Java
- developers.redhat.com: A faster way to access JDK Flight Recorder data
- Detect JPA and Hibernate performance issues with Hypersistence Optimizer:
- piotrminkowski.com: Java Flight Recorder on Kubernetes
Cambios importantes en la gestión de memoria de Java 8 de Oracle (2014)
PermGen no pertenece al heap y los objetos no son promocionados a esta sección de memoria gestionada durante un GC. Como bien dices es un espacio contiguo al heap, pero también se limpia cada vez que la tenured/old generation procede a un GC. No es una generación separada del mismo modo que es la young generation, y no hay un mecanismo específico para un GC separado de PermGen. La tenured/old generation y la permanent generation proceden a un GC cuando una de las dos se llena.
De todos modos no me queda claro si incorporaron PermGen dentro del heap en Java 7, aunque poco importa ya con los cambios en Java 8.
Mejor empiezo por introducir qué implementación de JVM es Java 8 de Oracle. Existen numerosas implementaciones de JVM y cada una utiliza diferentes soluciones para la gestión de memoria.
Dos de las soluciones más conocidas y populares de JVM han sido HotSpot de Sun (habitual en Tomcat) y JRockit de BEA (Weblogic). Ambas compañias fueron compradas por Oracle y Java 8 viene a ser la integración definitiva de ambas soluciones.
Históricamente se consideraba que HotSpot es el JVM con mejor rendimiento de las dos, si bien JRockit es valorada como la más escalable.
Originalmente en HotSpot no había generación permanente. Objetos y clases de JVM se almacenaban juntas. Las clases de ésta JVM eran estáticas y prácticamente no se utilizaban ‘Class Loaders’ (Load y Unload/Collection de Clases). PermGen surgió como una mejora de rendimiento. Por defecto los datos en la generación permanente no se eliminan nunca (son datos de JVM y no de aplicación, pudiendo variar según la pólítica de garbage collection). Esto podía llenar la generación permanente generando un OutOfMemoryErrors si se producía un elevado número de classloading. En muchos casos un problema con una generación permanente implica reiniciar regularmente la JVM y la aplicación Java.
Actualmente las clases de JVM son dinámicas y el espacio requerido para metadatos puede cambiar fácilmente.
A diferencia de HotSpot VM, JRockit carece de generación permanente y en cambio almacena los metadatos ‘off the heap’ en memoria nativa. Estos buffers de código son liberados constantemente cuando sus ClassLoaders no se utilizan. El problema de OutOfMemory en JRockit no es diferente a HotSpot, excepto por el hecho de ser memoria nativa en lugar de memoria heap. Hay dos diferencias significativas. Primero, en JRockit la limpieza de metadatos está habilitada siempre por defecto y segundo, no hay tamaño límite fijo para el espacio de metadatos. Uno de los principales problemas con HotSpot es su dificultad para seleccionar un tamaño adecuado para la generación permanente. ¿128MB, 256MB? Es muy difícil acertar para cada aplicación. JRockit es dinámico en la gestión de memoria reservada para metadatos y sin límites de tamaño (a excepción de la memoria del sistema). JRockit es también el único JVM con soporte de heaps no contiguos (uso de memoria por encima y por debajo del alojamiento del kernel y otras librerías), importante en el caso de Windows donde su kernel a menudo se ubica en mitad del espacio de direcciones.
Java 8 (HotRockit?) incorpora todas las herramientas de monitorización de HotSpot (Java VisualVM, jstat, jmap) y JRockit (Java Mission Control, Java Flight Recorder). Muy interesante.
Un inconveniente de Java 8 es la fragmentación de la memoria nativa para metadatos, pero probablemente incluya compactación en un futuro próximo.
En el 2016 saldrá Java 9 con la funcionalidad de auto-tuning y soporte de tamaños Heap multi-gigas.
En cualquier caso hay una tendencia al Heap-Offloading. El consumo de memoria en Java tiene un coste y las pausas/latencias causadas por los Full GC son proporcionales al tamaño del heap. Estas pausas son notables en tamaños de heap > 1Gb, con un considerable impacto en aplicaciones de tiempo real donde un proceso que no responde rápido puede ser descartado del cluster. Aún así, los servidores actuales hacen uso de frameworks muy pesados y fácilmente requieren heaps > 4Gb. Una solución a este problema es alojar fuera del heap los objetos poco utilizados mediante técnicas de serialización/deserialización (caché). El heap de memoria se mantiene pequeño y el Full GC se completa en milisegundos. Ejemplos:
- caché de sesión de usuarios, donde un fichero mapeado en memoria almacena gigabytes de sesiones de usuarios inactivos. Una vez que el usuario hace log-in, la aplicación dispone de todos sus datos sin ser necesaria una consulta a la BBDD.
- caché de resultados computacionales como queries, páginas html, etc (donde el coste computacional es mayor a la deserialización)
Slides
Click to expand!
Tweets
Click to expand!
#Java on #Kubernetes pic.twitter.com/MRP0RwJWaG
— Bruno Borges (@brunoborges) October 17, 2021
Once, I spent 6 months of my adult life as a full time JVM tuner. I was hired to work on data processing pipelines but the job became being a JVM tuning machine.
— Jaana Dogan ヤナ ドガン (@rakyll) November 19, 2021
Who knew that a @Java developer as the best job in the UK according to @Glassdoor. Feel lucky to be in the industry! pic.twitter.com/IIQAmJA95l
— George Adams (@gdams_) February 3, 2022
If you don't set a Garbage Collector for your #Java application, don't think the JVM will pick a good one for you either, no matter how many CPUs you give.
— Bruno Borges 🇧🇷🇺🇦🇨🇦 (@brunoborges) March 2, 2022
2 CPUs? 6 CPUs? It doesn't matter. If your container has less than 1792 MB and you don't set a GC, your app will use Serial pic.twitter.com/06mr9TKkKn