Skip to content

Commit 640c711

Browse files
authored
Merge pull request #1388 from kieranhejmadi01/java_tuning
Introduction to tuning Java Garbage Collection LP
2 parents 99972fc + 355f5ec commit 640c711

File tree

10 files changed

+482
-0
lines changed

10 files changed

+482
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
title: Example Application
3+
weight: 4
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Example Application.
10+
11+
Copy and paste the following Java snippet into a file called `HeapUsageExample.java`. This code example allocates 1 million string objects to fill up the heap. We can use this example to easily observe the effects of different GC tuning parameters.
12+
13+
```java
14+
public class HeapUsageExample {
15+
public static void main(String[] args) {
16+
System.out.println("Starting the application...");
17+
18+
try {
19+
// Create a large number of objects to quickly use up the heap
20+
for (int i = 0; i < 1000000; i++) {
21+
String[] array = new String[1000];
22+
for (int j = 0; j < array.length; j++) {
23+
array[j] = "Object " + j;
24+
}
25+
}
26+
} catch (OutOfMemoryError e) {
27+
System.err.println("OutOfMemoryError caught: " + e.getMessage());
28+
}
29+
30+
System.out.println("Application finished.");
31+
}
32+
}
33+
```
34+
35+
### Enable GC logging
36+
37+
To observe the what the GC is doing, one option is to enabling logging whilst the JVM is running. To enable this, we need to pass in some command-line arguments. The `gc` option logs the GC information we are interested. The `filecount` option creates a rolling log to prevent uncontrolled growth of logs with the drawback that historical logs may be rewritten and lost. Run the following command from the terminal to enable logging on JDK 11 onwards.
38+
39+
```bash
40+
java -Xms512m -Xmx1024m -XX:+UseSerialGC -Xlog:gc:file=gc.log:tags,uptime,time,level:filecount=10,filesize=16m HeapUsageExample.java
41+
```
42+
43+
Use the following command if you are using JDK8.
44+
45+
```bash
46+
java -Xms512m -Xmx1024m -XX:+UseSerialGC -Xloggc:gc.log -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation HeapUsageExample.java
47+
```
48+
49+
The `-Xms512m` and `-Xmx1024` options create a minimum and maximum heap size of 512 MiB and 1GiB respectively. This is simply so we do not have to wait for too long to see activity within the GC. Additionally, we force the JVM to use the serial garbage collector with the `-XX:+UseSerialGC` flag.
50+
51+
You will now see logs, named `gc.log.*` within the same directory. Viewing the contents you will see the following.
52+
53+
```output
54+
[2024-11-08T15:04:54.304+0000][0.713s][info][gc ] GC(2) Pause Young (Allocation Failure) 139M->3M(494M) 3.627ms
55+
...
56+
[2024-11-08T15:04:54.350+0000][0.759s][info][gc ] GC(3) Pause Young (Allocation Failure) 139M->3M(494M) 3.699ms
57+
```
58+
59+
These logs provide insights into the frequency, duration, and impact of Young garbage collection events. The results may vary depending on your system.
60+
61+
- Frequency: ~ every 46 ms
62+
- Pause duration: ~ 3.6 ms
63+
- Reduction size: ~ 139 MB (or 3M objects)
64+
65+
This logging method has the benefit of being verbose but at the tradeoff of clarity. Furthermore, this method clearly isn't suitable for a running process which makes debugging a live environment slightly more challenging.
66+
67+
### Use jstat to observe real-time GC statistics
68+
69+
The following java code snippet is a long-running example that prints out a random integer and double precision floating point number 4 times a second. Copy the example below and paste into a file called `WhileLoopExample.java`.
70+
71+
```java
72+
import java.util.Random;
73+
74+
public class GenerateRandom {
75+
76+
public static void main(String[] args) {
77+
Random rand = new Random();
78+
79+
while (true) {
80+
// Generate random integer in range 0 to 999
81+
int rand_int1 = rand.nextInt(1000);
82+
83+
// Print random integer
84+
System.out.println("Random Integers: " + rand_int1);
85+
86+
// Generate random double
87+
double rand_dub1 = rand.nextDouble();
88+
89+
// Print random double
90+
System.out.println("Random Doubles: " + rand_dub1);
91+
92+
// Sleep for 1 second (1000 milliseconds)
93+
try {
94+
Thread.sleep(250);
95+
} catch (InterruptedException e) {
96+
System.err.println("Thread interrupted: " + e.getMessage());
97+
}
98+
}
99+
}
100+
}
101+
```
102+
103+
Run the following command and open up a separate terminal session. Start the Java program with the command below. This will use the default parameters for the garbage collection.
104+
105+
```bash
106+
java WhileLoopExample.java
107+
```
108+
On the other terminal session, we use the `jstat` command to print out the JVM statistics specifically related to the GC using the `-gcutil` flag.
109+
110+
```bash
111+
jstat -gcutil $(pgrep java) 1000
112+
```
113+
114+
115+
You will obserse an output like the following until `ctl+c` is pressed.
116+
117+
```output
118+
S0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT
119+
0.00 100.00 6.11 1.81 71.05 73.21 1 0.010 0 0.000 0 0.000 0.010
120+
0.00 100.00 6.11 1.81 71.05 73.21 1 0.010 0 0.000 0 0.000 0.010
121+
0.00 100.00 6.11 1.81 71.05 73.21 1 0.010 0 0.000 0 0.000 0.010
122+
...
123+
0.00 100.00 6.11 1.81 71.05 73.21 1 0.010 0 0.000 0 0.000 0.010
124+
```
125+
126+
The columns of interest are:
127+
- **E (Eden Space Utilization)**: The percentage of the Eden space that is currently used. High utilization indicates frequent allocations and can trigger minor GCs.
128+
- **O (Old Generation Utilization)**: The percentage of the Old (Tenured) generation that is currently used. High utilization can lead to Full GCs, which are more expensive.
129+
- **YGCT (Young Generation GC Time)**: The total time (in seconds) spent in Young Generation (minor) GC events. High values indicate frequent minor GCs, which can impact performance.
130+
- **FGCT (Full GC Time)**: The total time (in seconds) spent in Full GC events. High values indicate frequent Full GCs, which can significantly impact performance.
131+
- **GCT (Total GC Time)**: The total time (in seconds) spent in all GC events (Young, Full, and Concurrent). This provides an overall view of the time spent in GC, helping to assess the impact on application performance.
132+
133+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: Basic GC Tuning
3+
weight: 5
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
### Update the JDK version used
10+
11+
A sensible first step is to use one of the latest long-term-support (LTS) releases of JDK. This is because the GC versions included with recent JDKs offer improvements. For example, the G1GC included with JDK11 offers improvements in the pause time compared to JDK 8. As mentioned earlier, the `java --version` command will show you the version currently in use.
12+
13+
```output
14+
$ java --version
15+
openjdk 21.0.4 2024-07-16 LTS
16+
OpenJDK Runtime Environment Corretto-21.0.4.7.1 (build 21.0.4+7-LTS)
17+
OpenJDK 64-Bit Server VM Corretto-21.0.4.7.1 (build 21.0.4+7-LTS, mixed mode, sharing)
18+
```
19+
20+
21+
### Use an alternative GC
22+
23+
In this section we will use the `HeapUsageExample.java` file we created earlier.
24+
25+
The G1 GC (Garbage-First Garbage Collector) is designed to handle large heaps and aims to provide low pause times by dividing the heap into regions and performing incremental garbage collection. This makes it suitable for applications with high allocation rates and large memory footprints.
26+
27+
We can run the following command to generate the GC logs using a different GC and compare. We have simply changed the GC from `Serial` to `G1GC` using the `-XX:+UseG1GC` option.
28+
29+
```bash
30+
java -Xms512m -Xmx1024m -XX:+UseG1GC -Xlog:gc:file=gc.log:tags,uptime,time,level:filecount=10,filesize=16m HeapUsageExample.java
31+
```
32+
From the created log file `gc.log.*`, we can observe that at a very similar time after start up (~0.75s), the Pause Young time has reduced from ~3.6ms to ~1.9ms. Further, the time between GC pauses has imprved from ~46ms to every ~98ms.
33+
34+
```output
35+
[2024-11-08T16:13:53.088+0000][0.790s][info][gc ] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 307M->3M(514M) 1.976ms
36+
...
37+
[2024-11-08T16:13:53.186+0000][0.888s][info][gc ] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 307M->3M(514M) 1.703ms
38+
```
39+
As discussed in the previous section, the performance improvement from moving to a G1GC will depend on the CPU overhead of your system. As such, the performance may vary depending on the cloud instance size and available CPU resources.
40+
41+
### Add GC Targets
42+
43+
You can manually provide targets for specific metrics and the GC will attempt to meet those requirements. For example, if you have a time-sensitive application such as a rest server, you may want to ensure that all customers receive a response within a specific time. You may find that if a client request is sent during GC you need to ensure that the GC pause time is minimised.
44+
45+
Running the command with the `-XX:MaxGCPauseMillis=<N>` sets a target max GC pause time.
46+
47+
```bash
48+
java -Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=1 -Xlog:gc:file=gc.log:tags,uptime,time,level:filecount=10,filesize=16m HeapUsageExample.java
49+
```
50+
51+
Looking at the output below, we can see that at the same initial state after ~0.7s the pause time has been reduced. However, we can also see the initial size of the Young space has gone from 307MiB above to 124MiB. The GC decided to reduce the size of he Young space to reduce the pause time at the expense of more frequent pauses.
52+
53+
```output
54+
[2024-11-08T16:27:37.061+0000][0.765s][info][gc] GC(18) Pause Young (Normal) (G1 Evacuation Pause) 124M->3M(514M) 0.489ms
55+
[2024-11-08T16:27:37.149+0000][0.853s][info][gc] GC(19) Pause Young (Normal) (G1 Evacuation Pause) 193M->3M(514M) 0.482ms
56+
```
57+
58+
Here are some additional target options you can consider to tune performance:
59+
60+
- -XX:InitiatingHeapOccupancyPercent:
61+
62+
Defines the old generation occupancy threshold to trigger a concurrent GC cycle. Adjusting this can be beneficial if your application experiences long GC pauses due to high old generation occupancy. For example, lowering this threshold can help start GC cycles earlier, reducing the likelihood of long pauses during peak memory usage.
63+
- -XX:ParallelGCThreads
64+
65+
Specifies the number of threads for parallel GC operations. Increasing this value can be beneficial for applications running on multi-core processors, as it allows GC tasks to be processed faster. For instance, a high-throughput server application might benefit from more parallel GC threads to minimize pause times and improve overall performance.
66+
- -XX:G1HeapRegionSize
67+
68+
Determines the size of G1 regions, which must be a power of 2 between 1 MB and 32 MB. Adjusting this can be useful for applications with specific memory usage patterns. For example, setting a larger region size can reduce the number of regions and associated overhead for applications with large heaps, while smaller regions might be better for applications with more granular memory allocation patterns.
69+
70+
We recommend reading [this technical article](https://www.oracle.com/technical-resources/articles/java/g1gc.html) for more information of G1GC tuning.
71+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Tune the Performance of the Java Garbage Collector
3+
4+
minutes_to_complete: 45
5+
6+
who_is_this_for: This learning path is designed for Java developers aiming to optimize application performance on Arm-based servers. It is especially valuable for those migrating applications from x86-based to Arm-based instances. Here, you'll learn about essential garbage collection (GC) tuning parameters and how to adjust them to meet your specific performance needs.
7+
8+
learning_objectives:
9+
- Understand the key differences among Java garbage collectors (GCs).
10+
- Monitor and interpret GC performance metrics.
11+
- Adjust core parameters to optimize performance for your specific workload
12+
13+
prerequisites:
14+
- Access to an Arm-based server
15+
- Basic understanding of Java
16+
- Java installed on your system
17+
18+
author_primary: Kieran Hejmadi
19+
20+
### Tags
21+
skilllevels: Introductory
22+
subjects: Java
23+
armips:
24+
- Neoverse
25+
tools_software_languages:
26+
- Java
27+
operatingsystems:
28+
- AWS Linux
29+
30+
31+
### FIXED, DO NOT MODIFY
32+
# ================================================================================
33+
weight: 1 # _index.md always has weight of 1 to order correctly
34+
layout: "learningpathall" # All files under learning paths have this same wrapper
35+
learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content.
36+
---
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
next_step_guidance: Run a Java Application on Google Axion instances.
3+
4+
recommended_path: /learning-paths/servers-and-cloud-computing/java-on-axion/
5+
6+
further_reading:
7+
- resource:
8+
title: OpenJDK Wiki
9+
link: https://wiki.openjdk.org/
10+
type: documentation
11+
- resource:
12+
title: G1GC Tuning
13+
link: https://www.oracle.com/technical-resources/articles/java/g1gc.html
14+
type: documentation
15+
16+
17+
# ================================================================================
18+
# FIXED, DO NOT MODIFY
19+
# ================================================================================
20+
weight: 21 # set to always be larger than the content in this path, and one more than 'review'
21+
title: "Next Steps" # Always the same
22+
layout: "learningpathall" # All files under learning paths have this same wrapper
23+
---
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
review:
3+
- questions:
4+
question: >
5+
What is the purpose of garbage collection?
6+
answers:
7+
- To manage memory by automatically reclaiming unused objects
8+
- To manually manage memory allocation
9+
correct_answer: 1
10+
explanation: >
11+
Garbage collection is used to manage memory by automatically reclaiming memory occupied by objects that are no longer in use, thus preventing memory leaks and optimizing memory usage.
12+
13+
- questions:
14+
question: >
15+
Which JVM flag can be used to enable detailed garbage collection logging?
16+
answers:
17+
- -XX:+UseG1GC
18+
- -XX:+PrintGCDetails
19+
correct_answer: 2
20+
explanation: >
21+
The flag -XX:+PrintGCDetails enables detailed logging of garbage collection events, which helps in monitoring and tuning the GC performance.
22+
23+
- questions:
24+
question: >
25+
Which garbage collector is best suited for applications requiring very low latency in a heavily multi-threaded application?
26+
answers:
27+
- Serial GC
28+
- ZGC
29+
correct_answer: 2
30+
explanation: >
31+
ZGC (Z Garbage Collector) is designed for applications requiring very low latency, as it aims to keep pause times below 10 milliseconds even for large heaps.
32+
33+
34+
35+
36+
# ================================================================================
37+
# FIXED, DO NOT MODIFY
38+
# ================================================================================
39+
title: "Review" # Always the same title
40+
weight: 20 # Set to always be larger than the content in this path
41+
layout: "learningpathall" # All files under learning paths have this same wrapper
42+
---

0 commit comments

Comments
 (0)