Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

How the JVM uses and allocates memory

September 9, 2021
Aashish Patil
Related topics:
Java
Related products:
Red Hat OpenShiftRed Hat Enterprise Linux

Share:

    This is the second article in a series that explains garbage collection in Java and how to tweak it for optimal Java application performance. The previous article introduced the stages and levels of garbage collection (including generational garbage collection) and showed how to check garbage collection behavior in your applications. This article goes into more depth about memory use in the Java Virtual Machine (JVM) and how to control it.

    Tracking memory use in the JVM

    Garbage collection can have a negative and unpredictable impact on Java application performance if it is improperly tuned. For instance, when full collection events happen too often, they cause high CPU usage on the application server, resulting in poor application request processing.

    If garbage collections are happening too often or contributing to a significant percentage of your CPU, the first thing to do is check whether your application is allocating memory unnecessarily. Excessive allocation is often due to a memory leak.

    Next, test your application with the expected production load in a development environment to determine the maximum heap memory usage. Your production heap size should be at least 25% to 30% higher than the tested maximum to allow room for overhead.

    Detecting out-of-memory errors

    Enabling the -XX:HeapDumpOnOutOfMemoryError option generates a heap dump when an allocation from the Java heap could not be satisfied and the application fails with an OutOfMemoryError. The heap dump can help you find the cause.

    The -XX:HeapDumpOnOutOfMemoryError option provides critical information about out-of-memory errors. Configuring the option doesn't have any performance impact on your environment, so you can enable it in a production environment. Having this option on is always recommended.

    By default, the heap dump is created in a file called java_pidpid.hprof in the JVM's working directory. You can specify an alternative filename or directory with the -XX:HeapDumpPath option. You can also observe peak heap memory usage using JConsole. Also, see How to monitor Java memory usage of heap/permanent generation and gc activities.

    Analyzing the heap dump

    The heap dump can show whether large objects were retained for a long time. Sometimes, to find the cause of retention, you need to understand the application code and the circumstances that caused the out-of-memory error. The problem might be caused by a memory leak in the application code; otherwise, the problem is probably inadequate memory allocated for the application's peak loads.

    Reasons for retaining large objects include:

    • Single objects that absorb all the heap memory allocated to the JVM.
    • Many small objects retaining memory.
    • Large retention by a single thread, perhaps the thread associated with the OutOfMemoryError.

    After you've identified the source of the large retention, view the paths from the garbage collection roots to see what is keeping the objects alive. The garbage collection roots are objects outside the heap and therefore are never collected. The path to the garbage collection roots shows the reference chain that prevents the object on the heap from being garbage collected. The article 10 Tips for using the Eclipse Memory Analyzer offers advice for analyzing heap dumps and memory leaks in the Eclipse Memory Analyzer.

    Also, see How do I analyze a Java heap dump for more details on analyzing the heap dump. If the application code is not the problem, increase the size of the Java heap to meet the load requirement.

    JVM options that affect memory use

    Parameters affecting the memory available to the JVM include:

    • -Xms: Sets the minimum and initial size of the heap.
    • -Xmx: Sets the maximum size of the heap.
    • -XX:PermSize: Sets the initial size of the Permanent Generation (perm) memory area. This option was available prior to JDK 8 but is no longer supported.
    • -XX:MaxPermSize: Sets the maximum size of the perm memory area. This option was available prior to JDK 8 but is no longer supported.
    • -XX:MetaspaceSize: Sets the initial size of Metaspace. This option is available starting in JDK 8.
    • -XX:MaxMetaspaceSize: Sets the maximum size of Metaspace. This option is available starting in JDK 8.

    Production environments often set the -Xms and -Xmx options to the same value so that the heap size is fixed and pre-allocated to the JVM.

    See Oracle's list of java command options for more information about different JVM options and their use.

    Calculating JVM memory consumption

    Many programmers figure out the maximum heap value for their application's JVM correctly but discover that the JVM is using even more memory. The value of the -Xmx parameter specifies the maximum size of the Java heap, but that is not the only memory consumed by the JVM. Permanent Generation (the name prior to JDK 8) or Metaspace (the name from JDK 8 onward), the CodeCache, the native C++ heap used by other JVM internals, space for the thread stacks, direct byte buffers, garbage collection overhead, and other things are counted as part of the JVM's memory consumption.

    You can calculate the memory used by a JVM process as follows:

    JVM memory = Heap memory+ Metaspace + CodeCache + (ThreadStackSize * Number of Threads) + DirectByteBuffers + Jvm-native
    

    Therefore, JVM memory usage can be more than the -Xmx value under peak business load.

    Components of JVM memory consumption

    The following list describes three important components of JVM memory:

    • Metaspace: Stores information about the classes and methods used in the application. This storage area was called Permanent Generation or perm in the HotSpot JVM prior to JDK 8, and the area was contiguous with the Java heap. From JDK 8 onward, Permanent Generation has been replaced by Metaspace, which is not contiguous with the Java heap. Metaspace is allocated in native memory. The MaxMetaspaceSize parameter limits the JVM's use of Metaspace. By default, there is no limit for Metaspace, which starts with a very low size default and grows gradually as needed. Metaspace contains only class metadata; all live Java objects are moved to heap memory. So the size of Metaspace is much lower than Permanent Generation was. Usually, there is no need to specify the maximum Metaspace size unless you face a large Metaspace leak.
    • CodeCache: Contains native code generated by the JVM. The JVM generates native code for a number of reasons, including the dynamically generated interpreter loop, Java Native Interface (JNI) stubs, and Java methods that are compiled into native code by the Just-in-Time (JIT) compiler. The JIT compiler is the major contributor to the CodeCache area.
    • ThreadStackSize: Sets the thread stack size in bytes by using the -XX:ThreadStackSize=<size> option, which can also be specified as -Xss=<size>. Append the letter k or K to indicate kilobytes; m or M to indicate megabytes; or g or G to indicate gigabytes. The default value for -XX:ThreadStackSize depends on the underlying operating system and architecture.

    How to check the thread stack size

    You can check the current thread stack size with:

    $ jinfo -flag ThreadStackSize JAVA_PID

    Use the following command to check the default thread stack size:

    $ java -XX:+PrintFlagsFinal -version |grep ThreadStackSize

    Figure 1 shows the thread stack sizes displayed by the previous command.

    You can check the sizes of the thread stacks used by your program.
    Figure 1: Checking the sizes of the thread stacks used by your program.

    Conclusion

    Setting up the heap size in accordance with application requirements is the first step in configuring heap settings. The option limits the heap size. However, the JVM requires more memory beyond the heap size, including Metaspace, CodeCache, thread stack size, and native memory. So when you consider the JVM's memory usage, be sure to include these other parts of memory consumption.

    The next article in this series covers the different garbage collectors and guidelines for choosing the best one for your application.

    Last updated: October 20, 2023

    Related Posts

    • Stages and levels of Java garbage collection

    • Shenandoah garbage collection in OpenJDK 16: Concurrent reference processing

    • How the JIT compiler boosts Java performance in OpenJDK

    • Collect JDK Flight Recorder events at runtime with JMC Agent

    Recent Posts

    • How Kafka improves agentic AI

    • How to use service mesh to improve AI model security

    • How to run AI models in cloud development environments

    • How Trilio secures OpenShift virtual machines and containers

    • How to implement observability with Node.js and Llama Stack

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue