Skip to content
This repository has been archived by the owner on Sep 1, 2023. It is now read-only.

A Java agent to automatically create heap dumps based on configurable thresholds and memory consumption patterns.

License

Notifications You must be signed in to change notification settings

SAP-archive/java-memory-assistant

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

54 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Important Notice

This public repository is read-only and no longer maintained.

REUSE status

Java Memory Assistant

A Java agent (as in -javaagent) that automates the generation of heap dumps when thresholds in terms of memory usage of the entire heap or single memory pools (e.g., eden, old gen, etc.) are met.

Unlike other Java agents, the Java Memory Assistant does not do bytecode manipulation via the Instrumentation API. It is shipped as a Java agent only for convenience, so that its functionality can be added to any Java application without having to modify the latter's content or logic.

Usage

The Java Memory Assistant jar file must be passed to the startup parameters of the JVM using the -javaagent option, e.g.:

java -javaagent:jma.jar -jar yourApp.jar

By default, the Java Memory Assistant does nothing. To activate it, one needs to pass at least two System properties as options:

  • jma.check_interval: every how often will the agent poll the JVM runtime to check the usage state of the memory pools
  • jma.thresholds.[memory pool or 'heap']: a usage threshold for the specified memory pool or for the entire heap

Thresholds of multiple memory pools are supported. The heap dump is then triggered when a check is executed and the usage of at least one memory pool is higher or equal to the threshold specified. For example:

java -javaagent:jma.jar -Djma.check_interval=5000ms -Djma.thresholds.heap=60 -jar yourApp.jar

These startup parameters would make the Java Memory Assistant check every 5 seconds how much of the heap is in use and, if 60% or more of the heap is allocated, it will trigger a heap dump using the HotSpotDiagnosticMXBean#dumpHeap(String, boolean) facility.

Supported configurations

System Property Supported values Description Default value
jma.enabled true, false Whether the agent has to activate itself or not, irrespective from any thresholds or other settings specified true
jma.check_interval (0, 2147483647]ms|s|m|h How often will the agent check for the memory usage conditions. The numeric value must be and int between 1 and 2147483647 (extremes included). The time unit is one of ms (milliseconds), s (seconds), m (minutes) or h (hours). -1 (disabled)
jma.max_frequency (1, 2147483647]/(1, 2147483647]ms|s|m|h How often can agent create heap dumps in a given time-span. Both numeric values must be int between 1 and 2147483647 (extremes included). The time unit is one of ms (milliseconds), s (seconds), m (minutes) or h (hours). no maximum frequency specified
jma.log_level DEBUG, WARN, INFO, ERROR, OFF The minimum severity of the logs to be printed; all logs from severity INFO and lower are printed to System.out; ERROR is printed to System.err; if OFF is specified, no logs are written anywhere ERROR
jma.heap_dump_folder Any valid local of absolute filesystem path in which the JVM process can create files Filesystem location where heap dumps will be created System.getProperty("user.dir")
jma.heap_dump_name The pattern to use to generate file names for heap dumps, e.g., heapdump_%host_name%_%ts%.hprof Name of the file under the jma.heap_dump_folder where the heap dump will be stored heapdump_%host_name%_%ts:yyyyMMddHHmmss%.hprof
jma.thresholds.heap The thresholds can be specified as one of the following:
  • absolute percentage threshold, e.g., more than 200 MB (`>200MB`) or less than 40KB (`<40k`); the first token is a comparison operator out of `<` (strictly lesser than), `<=` (strictly lesser than or equal to), `==` (equal to, exact to the byte), `=>` (equal to or greater than) and `>` (strictly greater than); supported memory units are `GB`, `MB` and `KB`
  • usage percentage threshold, i.e., any number between 0 and 99.99 followed by the '%' sign (precise to the second decimal digit, e.g., 42.42)
  • percentage-based increase-over-time-frame specification, e.g., +5%/4s (5% increase over 4 seconds); time unit is one of ms (milliseconds), s (seconds), m (minutes) or h (hours)
The usage threshold of the overall heap that, when reached or surpassed, triggers a heap dump null (disabled)
jma.thresholds.[memory_pool_name] Either a usage percentage threshold, i.e., any number between 0 and 99.99 followed by the '%' sign (precise to the second decimal digit, e.g., 42.42), or a percentage-based increase-over-time-frame specification, e.g., +5%/4s (5% increase over 4 seconds); time unit is one of ms (milliseconds), s (seconds), m (minutes) or h (hours). Supported memory pools depend on the JVM The usage threshold of the particular memory pool that, when reached or surpassed, triggers a heap dump null (disabled)
jma.command.interpreter Any string A OS-specific interpreter (e.g., shell, cmd) that will be executed via the JDK java.lang.ProcessBuilder API. If set to "", the command interpreter is ignored. cmd on Windows, sh otherwise
jma.execute.before Any string A script that will be executed by the command interpreter before a heap dump is created; it receives as first (and only) input the filename of the heap dump that will be created API null (disabled)
jma.execute.after Any string A script that will be executed by the command interpreter after a heap dump has been created; it receives as first (and only) input the filename of the heap dump that has been created API null (disabled)
jma.execute.on_shutdown Any string A script that will be executed by the command interpreter as a shutdown hook (as in java.lang.Runtime.addShutdownHook(java.lang.Thread)) upon a soft shutdown of the JVM, e.g., when java.lang.System.exit(int) is invoked; the command invoked receives no input. null (disabled)

File names

File names for heap dumps can be generated using a combination of fixed characters and tokens that can replaced with values. Tokens are delimited by the % character. For example, my_heapdump_%host_name%_%uuid%.hprof is a pattern with two tokens: %host_name% and %uuid%. When generating a heap dump, the name of the file where it will be stored is calculated by replacing %host_name% with the hostname of the machine running the Java process (e.g., localhost) and a random UUID. That is, the heap dump name may look something like my_heapdump_localhost_19866c22-ce15-41de-807b-4805d0387d76.hprof.

The Java Memory Assistant supports the following tokens:

Token Supports configuration? Description
%host_name% forbidden The network name of the host of the Java Virtual Machine, calculated as: java.net.InetAddress.getLocalHost().getHostName().
%uuid% forbidden A random UUID of 128 bits, calculated as: java.util.UUID.randomUUID().toString().
%ts% or %ts:[date format]% optionally: a valid java.text.SimpleDateFormat pattern The number of millis since the Unix epoch, calculated as java.lang.System.currentTimeMillis(), or its value formatted with the pattern provided as configuration; the formatter uses the java.util.TimeZone.getDefault() time zone.
%env:['['i, j']']% required: the name of an environment property The name of an environment property, the value of which replaces the token. The value is looked up via the java.lang.System.getenv([property name]) call. If the environment property is not present, the token will generate no characters. The value of the environment property can be truncated using the `[a, b]` notation appended to the environment variable name to select a substring of the value.

The timestamp %ts% token supports configuration that specifies the pattern to use to format the timestamp. Configuration is passed after the : character that follows the token name, e.g., %ts:[config]%. Configuration is optional. If no configuration is passed to the %ts% token, the number of millis since the epoch is printed out. For example, assume we were to generate a heap dump exactly at Unix epoch, we would have the following file names:

  • heap_dump_%ts%.hprof generates heap_dump_0.hprof
  • heap_dump_%ts:yyyyMMddmmssSS.hprof generates heap_dump_19700101000000.hprof

The environment %env:...% token allows you to insert in the heap dump names the value of an environment property. The environment property is evaluated once at agent's startup. If the environment property is not set, the token does not generate any character in the heap dump name.

The % character also serves as escape character for itself. Escaping needs to be performed both in token as well as in the fixed part of a pattern:

  • heap_%%_dump.hprof generates heap_%_dump.hprof
  • heap_dump_%ts:yyyyMMdd'%%'mmssSS.hprof generates heap_dump_19700101%000000.hprof

Integration tests

End-to-end integration tests are run automatically in the JVM running Gradle.

Additionally, the same tests are repeated automatically for each of the supported JVMs, provided that the binaries of the JVM are available in the right folder (more on this later).

This repository does not contain any JVM binaries, as that would count as distribution and violate most EULAs out there. (Plus, polluting GIT with tons of binaries is bad form.)

JVM for the integration tests have to be placed in folders under the test-e2e/src/test/resources/jdks directory. The java executable must be under the test-e2e/src/test/resources/jdks/[jvm_directory]/bin/java path. The name of the [jvm_directory] will given the name to the Gradle itest task that will run the integration tests with that JVM.

Supported JVMs

All JVMs support thresholds for the entire heap (specified via the jma.thresholds.heap system property). The specific memory pools, however, depend on the particular JVM. The Java Memory Assistant currently supports the following JVMs and settings thresholds for the specific memory areas. Trying to run the Java Memory Assistant on an unsupported JVM will lead to the agent disabling itself, but won't impact the rest of the JVM or the application running inside it.

AdoptOpenJDK HotSpot 8.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • compressed_class

AdoptOpenJDK HotSpot 11.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • code_heap.non_nmethods
  • code_heap.non_profiled_nmethods
  • code_heap.profiled_nmethods
  • compressed_class

OpenJDK 7.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • perm_gen
  • code_cache

OpenJDK 8.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • compressed_class

OpenJDK 11.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • compressed_class

Oracle JVM 7.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • perm_gen
  • code_cache

Oracle JVM 8.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • compressed_class

Oracle JVM 11.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • code_heap.non_nmethods
  • code_heap.non_profiled_nmethods
  • code_heap.profiled_nmethods
  • compressed_class

Pivotal JDK 8.x

Supported memory pools:

  • eden
  • survivor
  • tenured_gen
  • metaspace
  • code_cache
  • compressed_class

SAP JVM 7.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • compressed_class

SAP JVM 8.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • compressed_class

SAP Machine 11.x

Supported memory pools:

  • eden
  • survivor
  • old_gen
  • metaspace
  • code_cache
  • code_heap.non_nmethods
  • code_heap.non_profiled_nmethods
  • code_heap.profiled_nmethods
  • compressed_class

Build

This project should be built with a Java 1.7 JDK. Building it with a Java 1.8 JDK will also work, but there will be warnings reporting that the bootstrap classpath [is] not set in conjunction with -source 1.7.

To trigger a build, run the following command from the root of this repository:

./gradlew clean build

For building behind a proxy, consider setting the proxy-variables as follows:

./gradlew -Dhttps.proxyHost=[proxy_hostname] -Dhttps.proxyPort=[proxy_port] -Dhttp.proxyHost=[proxy_hostname] -Dhttp.proxyPort=[proxy_port] clean build

where, for example [proxy_hostname] is proxy.wdf.sap.corp and [proxy_port] is 8080.

When adding new code files to the project the build might fail with Missing header in: [...]. Run the following command to add a license header to all new files:

./gradlew LicenseFormat

Running the Java Memory Assistant on Java 11

Due to the restrictions introduced by the Java module system with Java 9, the following additional argument must be passed to the java command:

java ... --add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED ...

This will allow the Java Memory Assistant to access the com.sun.management.internal.HotSpotDiagnostic MBean it uses to trigger the creation of the heap dumps.

License

This project is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file.

About

A Java agent to automatically create heap dumps based on configurable thresholds and memory consumption patterns.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages