4 Tuning the Memory Management System

This chapter describes the most important options available for tuning the memory management system in the JVM.

Memory management is about allocation of objects. One part of the memory management system finds a free spot for a new object, while another part garbage collects old objects to create even more free space for new objects. The more objects a Java application allocates, the more resources are required for memory management. When correctly tuned, a memory management system minimizes the overhead caused by garbage collection and makes object allocation fast.

This chapter includes information about the following topics:

For more information about memory management in the Oracle JRockit JVM, see the "Understanding Memory Management" section in Oracle JRockit Introduction to the JDK.

4.1 Setting the Heap and Nursery Size

The heap is the area where the Java objects reside. The heap size has an impact on the performance of the JVM and the Java application.

When the JVM uses a generational garbage collection strategy, part of the heap is reserved for the nursery. All small objects are allocated in the nursery (young space). When the nursery becomes full, a young collection is performed, where objects that have been active in the nursery are moved to old space, which is the rest of the heap.

To distinguish between recently allocated objects and objects that have been in the nursery for a while, the JVM uses a keep area. The keep area contains the most recently allocated objects in the nursery and is not garbage collected until the next young collection.

4.1.1 Setting the Heap Size

Command-line options: -Xms:size -Xmx:size

The heap size affects allocation speed, garbage collection frequency and garbage collection times. A small heap fills quickly and must be garbage collected more often. A large heap results in overhead at garbage collection times. A heap that is larger than the available physical memory in the system must be paged out to disk, which leads to long access times or even application freezes, especially during garbage collection.

In general, the additional overhead caused by a larger heap is smaller than the gains in garbage collection frequency and allocation speed, as long as the heap does not get paged to disk. A good heap size setting is a heap that is as large as possible within the available physical memory.

There are two parameters for setting the heap size:

  • -Xms:size sets the initial and minimum heap size.

  • -Xmx:size sets the maximum heap size.

Example:

java -Xms:1g -Xmx:1g MyApplication

This command starts the JVM with a heap size fixed to 1 GB.

If the optimal heap size for the application is known, Oracle recommends that you set -Xms and -Xmx to the same value. This gives you a controlled environment where you get an appropriate heap size right from the start.

For more information, see the Oracle JRockit Command-Line Reference.

4.1.1.1 Setting the Heap Size on 64-bit Systems

On 64-bit systems a memory address is 64 bits long, which makes it possible to address much more memory than with a 32-bit address. Each reference also requires twice as much memory. To reduce the memory usage on 64-bit systems, the JRockit JVM can use compressed references. Compressed references reduce the references to 32 bits, and can be used if the entire heap can be addressed with 32 bits. Compressed references are enabled by default whenever applicable.

You can modify compressed references for various address bits and heap sizes by using the -XXcompressedrefs command-line option. For more information about -XXcompressedrefs and its parameters, see the Oracle JRockit Command-Line Reference.

4.1.2 Setting the Nursery and Keep Area Size

Command-line option: -Xns:size

The size of the nursery affects allocation speed, garbage collection frequency and garbage collection times. A small nursery fills quickly and must be garbage collected more often, while garbage collection of a large nursery takes slightly longer time. A nursery that is so small that few or no objects have died before a young collection is started is not useful, and neither is a nursery that is so large that no young collections are performed between garbage collections of the whole heap that are triggered due to allocation of large objects in old space.

An optimal nursery size for maximum application throughput is approximately half of the free heap. The goal is to have as many objects as possible be garbage collected by young collection rather than old collection. In the JRockit JVM, the dynamic garbage collection mode optimized for throughput, -Xgc:throughput, and the generational parallel garbage collector, -Xgc:genpar, dynamically sets the nursery size to an approximation of the optimal value.

The optimal nursery size for throughput is often quite large, which can lead to long young collection times. Because all Java threads are paused while the young collection is performed, consider reducing the nursery size below the optimal value to reduce the young collection pause times.

Example:

java -Xns:100m MyApplication

This command starts the JVM with a fixed nursery size of 100 MB.

For more information, see the Oracle JRockit Command-Line Reference.

4.1.2.1 Keep Area

Command-line option: -XXkeepAreaRatio:percentage

The keep area size affects both old collection and young collection frequency. A large keep area causes more frequent young collections, while a keep area that is too small causes more frequent old collections when objects are promoted prematurely.

An optimal keep area size is as small as possible while maintaining a low promotion ratio. The promotion ratio can be seen in the Flight Recorder recordings and verbose outputs from -Xverbose:memory=debug, and in the garbage collection report printed by -Xverbose:gcreport.

The keep area size is defined as a percent of the nursery.

Example:

java -XXkeepAreaRatio:10 MyApplication

This command starts the JVM with a keep area that is 10 percent of the nursery size.

For more information, see the Oracle JRockit Command-Line Reference.

4.2 Selecting and Tuning a Garbage Collector

The effect of garbage collection can be distributed in different ways depending on the choice of the garbage collection method. Use the command-line option -Xgc:mode to specify a garbage collection mode.

For more information about the -Xgc option, see the Oracle JRockit Command-Line Reference.

This section includes the following topics:

4.2.1 Selecting a Garbage Collection Mode

The garbage collection modes adjust the memory management system at run time, optimizing for a specific goal depending on the mode used.

The following are the garbage collection modes:

  • throughput: Optimizes the garbage collector for maximum application throughput.

  • pausetime: Optimizes the garbage collector for short and even pause times.

  • deterministic: Optimizes the garbage collector for very short and deterministic pause times.

The garbage collection modes use advanced heuristics to tune the following parameters at run time:

  • Nursery size

  • Compaction amount and type

4.2.1.1 Throughput Mode

Command-line option: -Xgc:throughput

The throughput mode can either use a generational garbage collector (if -Xgc:genpar or only -Xgc:throughput is specified) or a nongenerational garbage collector (if -Xgc:singlepar or -Xgc:parallel is specified).

The throughput mode uses as little CPU resources as possible for garbage collection, giving the Java application as many CPU cycles as possible. The JRockit JVM achieves this by using a parallel garbage collector that stops the Java application during the entire garbage collection duration and uses all CPUs available to perform the garbage collection. Each individual garbage collection pause might be long, but in total, the garbage collector takes as little CPU time as possible.

Use the throughput mode for applications that demand a high throughput but are not very sensitive to the occasional long garbage collection pause.

The throughput mode is the default when the JVM runs in the -server mode (which is default) or can be enabled by using the -Xgc:throughput option.

Example:

java -Xgc:throughput MyApplication

This command starts the JVM with the garbage collection mode optimized for throughput.

For more information, see the Oracle JRockit Command-Line Reference.

4.2.1.2 Pausetime Mode

Command-line option: -Xgc:pausetime

The pausetime mode can use either a generational garbage collector (if -Xgc:gencon or only -Xgc:pausetime is specified) or a non-generational garbage collector (if -Xgc:singlecon is specified).

The pausetime mode aims to keeps the garbage collection pauses below a given pause target while maintaining as high a throughput as possible. The JRockit JVM achieves this with a mostly concurrent garbage collection strategy that allows the Java application to continue running during large portions of the garbage collection duration.

Use the pausetime mode for applications that are sensitive to long latencies (for example, transaction-based systems where transaction times are expected to be stable).

Example:

java -Xgc:pausetime MyApplication

This command starts the JVM with the garbage collection mode optimized for short pauses.

For more information, see the Oracle JRockit Command-Line Reference.

4.2.1.2.1 Setting a Pause Target for Pausetime Mode

Command-line option: -XpauseTarget:time

The pausetime mode uses a pause target for optimizing the pause times. The pause target affects the application throughput. A lower pause target inflicts more overhead on the memory management system.

Set the pause target as high as your application can tolerate.

Example:

java -Xgc:pausetime -XpauseTarget:300 MyApplication

This command starts the JVM with the garbage collection optimized for short pauses and a pause target of 300 milliseconds.

The default pause target for the pausetime mode is 500 milliseconds.

For more information, see the Oracle JRockit Command-Line Reference.

4.2.1.3 Deterministic Mode

Command-line option: -Xgc:deterministic

The deterministic mode ensures short garbage collection pause times and limits the total pause time within a prescribed window. The JRockit JVM achieves this by using a specially designed mostly concurrent garbage collector, which allows the Java application to continue running as much as possible during the garbage collection.

Use the deterministic mode for applications with strict demands on short and deterministic latencies (for example, transaction-based applications such as brokerage applications).

Example:

java -Xgc:deterministic MyApplication

This command starts the JVM with the garbage collection mode optimized for short and deterministic pauses.

For more information, see the Oracle JRockit Command-Line Reference.

4.2.1.3.1 Special Note for Oracle WebLogic Real Time Users

Deterministic garbage collection time can be affected by the JRockit Mission Control Client. While all JRockit Mission Control tools are fully supported when running Oracle WebLogic Real Time with the deterministic garbage collector, you should be aware of the following:

  • The -Xmanagement option does not prolong deterministic garbage collection pauses by itself but it does introduce a slightly increased amount of Java code executed by the JVM. This can affect response times and performance compared to not using the -Xmanagement option.

  • When making a recording by using the JRockit Flight Recorder, if you run the application in a latency-sensitive situation where you cannot accept the pause for the benefit of the information, disable heap statistics (heapstat). Heapstat provides additional bookkeeping of the content of the heap. These statistics are collected at the beginning and at the end of a Flight Recorder recording, inside a pause. You can disable heapstat by using specific arguments when requesting the recording.

  • JRockit Flight Recorder recordings, even with heapstats disabled, might cause deterministic garbage collection pauses to last slightly longer.

  • Memory leak trend analysis can cause longer garbage collection pauses, similar to JRockit Flight Recorder recordings.

  • For requests for more information when the Memory Leak Detector is using its graphical user interface or the diagnostic command, a longer pause can be introduced (for example, to retrieve the number of instances of a type of object or to retrieve the list of references to an instance).

For more information about JRockit Mission Control, see the Oracle JRockit Mission Control Online Help.

4.2.1.3.2 Setting a Pause Target for Deterministic Mode

Command-line option: -XpauseTarget:time

The deterministic mode uses a pause target for optimizing the pause times. The pause target affects the application throughput. A lower pause target inflicts more overhead on the memory management system.

Set the pause target as high as your application can tolerate.

The garbage collector aims to keep the garbage collection pauses below the given pause target. Success of this strategy depends on the application and the hardware. For example, a pause target of 30 milliseconds has been verified on an application with 1 GB heap and an average of 30 percent of the live data or less at collection time, running on the following hardware:

  • 2 x Intel Xeon 3.6 GHz, 2 MB level 2 cache, 4 GB RAM

  • 4 x Intel Xeon 2.0 GHz, 0.5 MB level 2 cache, 8 GB RAM

Running on slower hardware, with a different heap size and with more live data can break the deterministic behavior or cause performance degradation over time, while faster hardware or less live data might allow you to set a lower pause target.

Example:

java -Xgc:deterministic -XpauseTarget:40ms MyApplication

This command starts the JVM with the garbage collection optimized for short and deterministic pauses and a pause target of 40 milliseconds.

The default pause target for the deterministic mode is 30 milliseconds.

For more information, see the Oracle JRockit Command-Line Reference.

4.2.1.4 Garbage Collector Mode Selection Workflow

To select the best garbage collection strategy for your application you can follow this workflow:

  1. Is your application sensitive to long garbage collection pauses (500 milliseconds or more)?

    • Yes: Select a mostly concurrent garbage collection mode, gencon or singlecon.

    • No: Select a parallel garbage collection mode, genpar or singlepar.

  2. Does your application allocate a lot of temporary objects?

    • Yes: Select a two-generational garbage collection mode, gencon or genpar.

    • No: Select a single-generational garbage collection mode, singlecon or singlepar.

For example, the Oracle WebLogic SIP Server is a transaction-based system that allocates new objects for each transaction and has short timeouts for transactions. Long garbage collection pauses would cause transactions to time out, so use a mostly concurrent garbage collection: gencon or singlecon. Because the transactions generate many temporary or short lived objects, a two-generational garbage collection mode (gencon) would be appropriate.

You can set a static garbage collection strategy with the -Xgc:mode option.

Example:

java -Xgc:gencon MyApplication

This command starts the JVM with the generational concurrent garbage collector.

4.2.1.5 Changing Garbage Collection Mode at Run Time

You can change the garbage collector mode at run time from the Memory tab of the JRockit Management Console (in JRockit Mission Control), except when using the deterministic and singlepar garbage collection modes.

For more information, see the Oracle JRockit Mission Control Online Help.

4.2.2 Tuning the Concurrent Garbage Collection Trigger

Command-line option: -XXgcTrigger:value

When you are using a concurrent strategy for garbage collection (in either the mark or the sweep phase, or both), the JRockit JVM dynamically adjusts when to start an old generation garbage collection in order to avoid running out of free heap space during the concurrent phases of the garbage collection. The triggering is based on such characteristics as how much space is available on the heap after previous collections. The JVM dynamically tries to optimize this space and, in doing so, it occasionally runs out of free heap during the concurrent garbage collection. When this happens, the following verbose output is displayed if the -Xverbose:memdbg option is used.

[DEBUG][memory ] [OC#55] Starting parallel sweeping phase.
[DEBUG][memory ] [OC#55] Will run parallel sweep due to: Alloc Queue Not Empty.
or
[DEBUG][memory ] [OC#55] Will run parallel sweep due to: Promotion Failed.

This message means that a concurrent sweep could not finish in time and the JVM is using all currently available resources to make up for it. In this case, a parallel sweep is made. If the JVM fails to adapt and the above output continues to appear, performance is affected.

To avoid this, set the -XXgcTrigger option to trigger a garbage collection when a specified percentage of the heap remains.

Example:

java -XXgcTrigger:20 MyApplication

This command triggers an old generation garbage collection when less than 20 percent of the free heap size is left unused.

If you are using a parallel garbage collection strategy (in both the mark and the sweep phase), old generation garbage collections are performed whenever the heap is completely full.

For more information, see the Oracle JRockit Command-Line Reference.

4.3 Tuning Compaction

Compaction is the process of moving chunks of allocated space towards the lower end of the heap, helping create contiguous free memory at the upper end. The JRockit JVM does partial compaction of the heap at each old collection. The size and position of the compaction area as well as the compaction method is selected by advanced heuristics, depending on the garbage collection mode used.

4.3.1 Fragmentation vs. Garbage Collection Pauses

Compaction of a large area with many objects increases the garbage collection pause times. On the other hand, insufficient compaction leads to fragmentation of the heap, which leads to lower performance. If the fragmentation increases over time, the JRockit JVM is eventually forced to either do a full compaction of the heap, causing a long garbage collection pause or throw an OutOfMemoryError.

If your application shows intermittent performance degradation over time, such that the performance degrades until it suddenly returns to normal, and then starts degrading again, you are most likely experiencing fragmentation problems. The heap becomes more and more fragmented for each old collection until, finally, object allocation becomes impossible and the JVM is forced to do a full compaction of the heap. The full compaction eliminates the fragmentation, but only until the next garbage collection. You can verify this by looking at the -Xverbose:memory outputs, monitoring the JVM through the Management Console in JRockit Mission Control, or by creating a JRockit Flight Recorder recording and examining the garbage collection data. If you see that the amount of used heap after each old collection keeps increasing over time until it peaks, and then decreases again at the next old collection, you are experiencing a fragmentation problem.

You can consider compaction to be optimally tuned when the fragmentation remains at a low level consistently.

4.3.2 Adjusting Compaction

Though the compaction heuristics in the JRockit JVM are designed to keep the garbage collection pauses low and consistent, you might sometimes want to limit the compaction ratio further to reduce the garbage collection pauses. In other cases, you might want to increase the compaction ratio to keep heap fragmentation in control.

You can adjust the compaction by using one of the following techniques:

4.3.2.1 Setting the Compaction Ratio

Command-line option:-XXcompaction:percentage=value

Setting a static compaction ratio forces the JVM to compact a specified percentage of the heap at each old collection. This disables the heuristics for selecting a dynamic compaction ratio that depends on the heap layout. The compact ratio can be defined to a static percentage of the heap using the -XXcompaction:percentage option.

Example:

java -XXcompaction:percentage=5 MyApplication

This command starts the JVM with a static compact ratio of about 5 percent of the heap.

For more information, see the Oracle JRockit Command-Line Reference.

Use this option if you need to force the JVM to use a smaller or larger compaction ratio than it would select by default. You can monitor the compaction ratio in -Xverbose:compaction outputs and JRockit Flight Recorder recordings. A high compaction ratio keeps down the fragmentation on the heap but increases the compaction pause times.

4.3.2.2 Setting Maximum References

Command-line option: -XXcompaction:maxReferences=value

When compaction has moved objects, the references to these objects must be updated. The garbage collector does this before the Java threads are allowed to run again, which increases the garbage collection pause proportionally to the number of references that have been updated.

The maximum number of references that can exist from objects outside the compaction area to objects within the compaction area depends on the garbage collection mode used and, for some modes, is adjusted dynamically at run time.

To limit the pause caused by compaction, you can specify a static maximum number of references, by using the -XXcompaction:maxReferences option. If, during a garbage collection, the number of references to the chosen compaction area exceeds the specified maxReferences value, the compaction is cancelled.

Example:

java -XXcompaction:maxReferences=20000 MyApplication

This command starts the JVM with a maximum of 20000 references.

You can monitor the compaction behavior and compaction pause times in the -Xverbose:compaction output and Flight Recorder recordings. If you observe that many compactions are cancelled (skipped), increase the value of maxReferences; if the compaction pause times are too long, decrease the value of maxReferences.

For more information about maximum references, see the Oracle JRockit Command-Line Reference.

4.3.2.3 Turning Off Compaction

Command-line option: -XXcompaction:enable=false

Very few applications survive in the long run without any compaction at all but for those that do you can turn off the compaction entirely.

Example:

java -XXcompaction:enable=false MyApplication

For more information, see the Oracle JRockit Command-Line Reference.

4.3.2.4 Using Full Compaction

Command-line option: -XXcompaction:full

Some applications are not sensitive to garbage collection pauses or perform old collections very infrequently. For these applications, you might want to try running full compaction, as this maximizes the object allocation performance between the garbage collections. However, a full compaction of a large heap with a lot of objects can take several seconds to perform.

Example:

java -XXcompaction:full MyApplication

For more information, see the Oracle JRockit Command-Line Reference.

4.4 Optimizing Memory Allocation Performance

Apart from optimizing the garbage collection to clear space for object allocation, you can tune the object allocation itself to maximize the application throughput.

4.4.1 Setting the Thread Local Area Size

Command-line options: -XXtlaSize:min=size,preferred=size,wasteLimit=size

The thread local area size influences the allocation speed, but can also have an impact on garbage collection frequency. A large TLA size allows each thread to allocate a lot of objects before requesting a new TLA, and in JRockit JVM it also allows the thread to allocate larger objects in the thread local area. On the other hand, a large TLA size prevents small chunks of free memory from being used for object allocation, which increases the impact of fragmentation. The TLA size is dynamic depending on the size of the available chunks of free space, and varies between a minimum and a preferred size.

Increasing the preferred TLA size is beneficial for applications where each thread allocates a lot of objects. When a two-generational garbage collection strategy is used, a large minimum and preferred TLA size also allow larger objects to be allocated in the nursery. Note however that the preferred TLA size should always be less than about 5 percent of the nursery size.

Increasing the minimum TLA size can improve garbage collection times slightly, as the garbage collector can ignore any free chunks that are smaller than the minimum TLA size.

Decreasing the preferred TLA size is beneficial for applications where each thread allocates only a few objects before it is terminated, so that a larger TLA would not ever become full. A small preferred TLA size is also beneficial for applications with very many threads, where the threads do not have time to fill their TLAs before a garbage collection is performed.

Decreasing the minimum TLA size lessens the impact of fragmentation.

A thread requests a new TLA earliest when the current TLA has memory less than the value specified by the -XXtlaSize:wasteLimit option. Decreasing the value of -XXtlaSize:wasteLimit makes better use of the available memory, but causes more 'slow-case' allocations in the memory other than TLA. A large value for the -XXtlaSize:wasteLimit option reduces slow allocations, but it can make garbage collections more frequent. Oracle recommends that you set -XXtlaSize:wasteLimit to the same value as -XXtlaSize:min.

A common setting for the TLA size is a minimum TLA size of 2 KB to 4 KB and a preferred TLA size of 16 KB to 256 KB.

Example:

java -XXtlaSize:min=1k,preferred=512k MyApplication

This command starts the JVM with a minimum TLA size of 1 KB and a preferred TLA size of 512 KB.

For more information, see the Oracle JRockit Command-Line Reference.