Skip to content

SkyWalking JVM Observability Best Practices


Introduction

JVM is a specification for computing devices. It is a virtual computer, short for Java Virtual Machine. Java is a highly abstract language that provides automatic memory management and other features, hence the abstraction layer of JVM exists. JVM runs on top of the operating system to execute Java bytecode, enabling Java to achieve cross-platform capabilities.

The following briefly introduces the JVM memory structure and threads, then uses SkyWalking to collect JVM metrics data and observes through Guance.

JVM Memory Structure

The Java compiler only targets the JVM, generating byte code files understandable by the JVM. The class loader in the JVM loads these byte codes, and once loaded, it hands them over to the JVM execution engine for execution. During the entire process of class loading, the JVM uses a segment of space to store data and related information. This segment of space is commonly referred to as JVM memory.

According to the JVM specification, JVM memory is divided into:

  • Program Counter
    Used to record the execution address of the next JVM instruction.

  • Virtual Machine Stack
    Each stack consists of multiple stack frames (Frame), corresponding to the memory used during each method call. It stores local variable tables, operand stacks, constant pool references, etc. From method invocation to completion, this corresponds to a stack frame being pushed onto and popped from the Java virtual machine stack. You can set the size of the virtual machine stack via -Xss.

  • Native Method Stack
    Its functionality and characteristics are similar to those of the virtual machine stack, except that the native method stack serves native methods.

  • Method Area
    The method area is a JVM specification, with permanent generation and meta-space being one of its implementations. In JDK8 and later versions, the original permanent generation data was split into heap and meta-space. Meta-space stores class metadata, static variables, and string constant pools, which are placed in the heap.

  • Heap
    The heap is shared by all threads and primarily stores object instances and arrays. It can reside in physically non-contiguous spaces but must be logically contiguous.
    The heap memory is divided into Young Generation and Old Generation. The Young Generation is further divided into Eden and Survivor regions. The Survivor region consists of FromSpace and ToSpace. Eden occupies a larger capacity, while the two Survivor regions occupy smaller capacities, with a default ratio of 8:1:1.
    If there is insufficient memory in the Java heap to complete instance allocation and the heap cannot expand further, the Java virtual machine will throw an OutOfMemoryError exception.

  • Runtime Constant Pool
    The runtime constant pool is part of the method area, functioning as a table. The virtual machine instructions use this table to find the names of classes, methods, parameter types, etc., to be executed.

  • Direct Memory
    Direct memory is not part of the runtime data area of the virtual machine nor is it a memory region defined in the Java Virtual Machine Specification. It is not restricted by the size of the Java heap but is limited by the total physical memory size of the host machine. Direct memory can be specified using -XX:MaxDirectMemorySize. Allocating direct memory consumes higher performance, but IO read/write operations on direct memory perform better than regular heap memory. Exhausting the direct memory throws an OutOfMemoryError exception.

Threads

There are two types of threads in Java: User Threads and Daemon Threads.

  • User threads can be understood as the working threads of the system, completing the business operations required by the application.
  • Daemon threads are a special type of thread that silently completes some systematic services in the background, such as garbage collection threads.

Threads in the JVM have the following states:

  • New State (NEW), the state where the thread has not started executing.
  • Runnable State (RUNNABLE), the state where the thread is executing.
  • Blocked State (BLOCKED), the state where the thread is waiting due to a monitor lock.
  • Waiting State (WAITING), indefinitely waiting for another thread to perform a specific operation.
  • Timed Waiting State (TIMED_WAITING), waiting for a specific time limit for another thread to perform a specific operation.
  • Terminated State (TERMINATED), the state where the thread is terminated.

Performance Metrics

Metric Description Data Type Unit
class_loaded_count
Loaded class count. int count
class_total_loaded_count Total loaded class count. int count
class_total_unloaded_class_count Total unloaded class count. int count
cpu_usage_percent CPU usage percentile. float percent
gc_phrase_old/new_count GC old or new count. int count
heap/stack_committed Committed amount of memory for heap or stack. int count
heap/stack_init Initialized amount of memory for heap or stack. int count
heap/stack_max Maximum amount of memory for heap or stack. int count
heap/stack_used Used amount of memory for heap or stack. int count
pool_*_committed Committed amount of memory in various pools
(code_cache_usage,newgen_usage,oldgen_usage,
survivor_usage,permgen_usage,metaspace_usage).
int count
pool_*_init Initialized amount of memory in various pools
(code_cache_usage,newgen_usage,oldgen_usage,
survivor_usage,permgen_usage,metaspace_usage).
int count
pool_*_max Maximum amount of memory in various pools
(code_cache_usage,newgen_usage,oldgen_usage,
survivor_usage,permgen_usage,metaspace_usage).
int count
pool_*_used Used amount of memory in various pools
(code_cache_usage,newgen_usage,oldgen_usage,
survivor_usage,permgen_usage,metaspace_usage).
int count
thread_blocked_state_count Count of threads in blocked state. int count
thread_daemon_count Daemon thread count. int count
thread_live_count Live thread count. int count
thread_peak_count Peak thread count. int count
thread_runnable_state_count Count of threads in runnable state. int count
thread_time_waiting_state_count Count of threads in time-waiting state. int count
thread_waiting_state_count Count of threads in waiting state. int count

Prerequisites

  • Install DataKit 1.4.17+

Procedure Steps

1 Enable Collector

Enter the host where DataKit is installed and copy the sample file.

cd /usr/local/datakit/conf.d/skywalking
cp skywalking.conf.sample skywalking.conf

If the deployed DataKit is not on the same host as the target JVM's Jar, modify the address = "localhost:11800" in skywalking.conf by replacing localhost with the IP of the host where the Jar resides.

Since the Jar and DataKit used here are on the same host, reporting to the JVM metrics via localhost is sufficient, so no modification of the sample file is needed.

2 Deploy Application

Download the skywalking-demo project, open it with Idea, click 「package」 to generate the skywalking-user-service.jar file.
Download SkyWalking , extract it and copy the agent directory to the same directory on the host where skywalking-user-service.jar is stored.

Run the following command to start the application.

java  -javaagent:agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=skywalking-user  \
-Dskywalking.collector.backend_service=localhost:11800 \
-jar skywalking-user-service.jar

3 JVM Observability

Log in to 「Guance」 - 「Use Cases」, input "JVM", select 「JVM Skywalking Monitoring View」, and click 「Confirm」.

Then click on the newly created 「JVM Skywalking Monitoring View」 to begin observing.

image

image

image

Feedback

Is this page helpful? ×