Skip to content

Commit 3d106cb

Browse files
committedMar 5, 2024
8325139: JFR SwapSpace event - add free swap space information on Linux when running in a container environment
Reviewed-by: lucy, sgehwolf
1 parent c00c939 commit 3d106cb

9 files changed

+101
-10
lines changed
 

‎src/hotspot/os/linux/cgroupSubsystem_linux.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ class CgroupSubsystem: public CHeapObj<mtInternal> {
264264
virtual jlong pids_current() = 0;
265265
virtual jlong memory_usage_in_bytes() = 0;
266266
virtual jlong memory_and_swap_limit_in_bytes() = 0;
267+
virtual jlong memory_and_swap_usage_in_bytes() = 0;
267268
virtual jlong memory_soft_limit_in_bytes() = 0;
268269
virtual jlong memory_max_usage_in_bytes() = 0;
269270
virtual jlong rss_usage_in_bytes() = 0;

‎src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -168,6 +168,20 @@ jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() {
168168
return memory_swap;
169169
}
170170

171+
jlong CgroupV1Subsystem::memory_and_swap_usage_in_bytes() {
172+
jlong memory_sw_limit = memory_and_swap_limit_in_bytes();
173+
jlong memory_limit = CgroupSubsystem::memory_limit_in_bytes();
174+
if (memory_sw_limit > 0 && memory_limit > 0) {
175+
jlong delta_swap = memory_sw_limit - memory_limit;
176+
if (delta_swap > 0) {
177+
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.memsw.usage_in_bytes",
178+
"mem swap usage is: ", JULONG_FORMAT, JULONG_FORMAT, memory_swap_usage);
179+
return (jlong)memory_swap_usage;
180+
}
181+
}
182+
return memory_usage_in_bytes();
183+
}
184+
171185
jlong CgroupV1Subsystem::read_mem_swappiness() {
172186
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.swappiness",
173187
"Swappiness is: ", JULONG_FORMAT, JULONG_FORMAT, swappiness);

‎src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class CgroupV1Subsystem: public CgroupSubsystem {
7676
public:
7777
jlong read_memory_limit_in_bytes();
7878
jlong memory_and_swap_limit_in_bytes();
79+
jlong memory_and_swap_usage_in_bytes();
7980
jlong memory_soft_limit_in_bytes();
8081
jlong memory_usage_in_bytes();
8182
jlong memory_max_usage_in_bytes();

‎src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,16 @@ jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() {
180180
return swap_limit;
181181
}
182182

183+
jlong CgroupV2Subsystem::memory_and_swap_usage_in_bytes() {
184+
jlong memory_usage = memory_usage_in_bytes();
185+
if (memory_usage >= 0) {
186+
char* mem_swp_current_str = mem_swp_current_val();
187+
jlong swap_current = limit_from_str(mem_swp_current_str);
188+
return memory_usage + (swap_current >= 0 ? swap_current : 0);
189+
}
190+
return memory_usage; // not supported or unlimited case
191+
}
192+
183193
char* CgroupV2Subsystem::mem_swp_limit_val() {
184194
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.max",
185195
"Memory and Swap Limit is: %s", "%1023s", mem_swp_limit_str, 1024);

‎src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class CgroupV2Subsystem: public CgroupSubsystem {
7575
int cpu_period();
7676
int cpu_shares();
7777
jlong memory_and_swap_limit_in_bytes();
78+
jlong memory_and_swap_usage_in_bytes();
7879
jlong memory_soft_limit_in_bytes();
7980
jlong memory_usage_in_bytes();
8081
jlong memory_max_usage_in_bytes();

‎src/hotspot/os/linux/osContainer_linux.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ jlong OSContainer::memory_and_swap_limit_in_bytes() {
7777
return cgroup_subsystem->memory_and_swap_limit_in_bytes();
7878
}
7979

80+
jlong OSContainer::memory_and_swap_usage_in_bytes() {
81+
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
82+
return cgroup_subsystem->memory_and_swap_usage_in_bytes();
83+
}
84+
8085
jlong OSContainer::memory_soft_limit_in_bytes() {
8186
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
8287
return cgroup_subsystem->memory_soft_limit_in_bytes();

‎src/hotspot/os/linux/osContainer_linux.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class OSContainer: AllStatic {
5252

5353
static jlong memory_limit_in_bytes();
5454
static jlong memory_and_swap_limit_in_bytes();
55+
static jlong memory_and_swap_usage_in_bytes();
5556
static jlong memory_soft_limit_in_bytes();
5657
static jlong memory_usage_in_bytes();
5758
static jlong memory_max_usage_in_bytes();

‎src/hotspot/os/linux/os_linux.cpp

+31-8
Original file line numberDiff line numberDiff line change
@@ -304,18 +304,41 @@ jlong os::total_swap_space() {
304304
return (jlong)(si.totalswap * si.mem_unit);
305305
}
306306

307+
static jlong host_free_swap() {
308+
struct sysinfo si;
309+
int ret = sysinfo(&si);
310+
if (ret != 0) {
311+
return -1;
312+
}
313+
return (jlong)(si.freeswap * si.mem_unit);
314+
}
315+
307316
jlong os::free_swap_space() {
317+
jlong host_free_swap_val = host_free_swap();
308318
if (OSContainer::is_containerized()) {
309-
// TODO add a good implementation
310-
return -1;
311-
} else {
312-
struct sysinfo si;
313-
int ret = sysinfo(&si);
314-
if (ret != 0) {
315-
return -1;
319+
jlong mem_swap_limit = OSContainer::memory_and_swap_limit_in_bytes();
320+
jlong mem_limit = OSContainer::memory_limit_in_bytes();
321+
if (mem_swap_limit >= 0 && mem_limit >= 0) {
322+
jlong delta_limit = mem_swap_limit - mem_limit;
323+
if (delta_limit <= 0) {
324+
return 0;
325+
}
326+
jlong mem_swap_usage = OSContainer::memory_and_swap_usage_in_bytes();
327+
jlong mem_usage = OSContainer::memory_usage_in_bytes();
328+
if (mem_swap_usage > 0 && mem_usage > 0) {
329+
jlong delta_usage = mem_swap_usage - mem_usage;
330+
if (delta_usage >= 0) {
331+
jlong free_swap = delta_limit - delta_usage;
332+
return free_swap >= 0 ? free_swap : 0;
333+
}
334+
}
316335
}
317-
return (jlong)(si.freeswap * si.mem_unit);
336+
// unlimited or not supported. Fall through to return host value
337+
log_trace(os,container)("os::free_swap_space: container_swap_limit=" JLONG_FORMAT
338+
" container_mem_limit=" JLONG_FORMAT " returning host value: " JLONG_FORMAT,
339+
mem_swap_limit, mem_limit, host_free_swap_val);
318340
}
341+
return host_free_swap_val;
319342
}
320343

321344
julong os::physical_memory() {

‎test/hotspot/jtreg/containers/docker/TestJFREvents.java

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -68,6 +68,10 @@ public static void main(String[] args) throws Exception {
6868
testMemory("500m", "" + 500*MB);
6969
testMemory("1g", "" + 1024*MB);
7070

71+
// see https://docs.docker.com/config/containers/resource_constraints/
72+
testSwapMemory("200m", "200m", "" + 0*MB, "" + 0*MB);
73+
testSwapMemory("200m", "300m", "" + 100*MB, "" + 100*MB);
74+
7175
testProcessInfo();
7276

7377
testEnvironmentVariables();
@@ -211,6 +215,37 @@ private static void testMemory(String valueToSet, String expectedValue) throws E
211215
}
212216

213217

218+
private static void testSwapMemory(String memValueToSet, String swapValueToSet, String expectedTotalValue, String expectedFreeValue) throws Exception {
219+
Common.logNewTestCase("Memory: --memory = " + memValueToSet + " --memory-swap = " + swapValueToSet);
220+
OutputAnalyzer out = DockerTestUtils.dockerRunJava(
221+
commonDockerOpts()
222+
.addDockerOpts("--memory=" + memValueToSet)
223+
.addDockerOpts("--memory-swap=" + swapValueToSet)
224+
.addClassOptions("jdk.SwapSpace"));
225+
out.shouldHaveExitValue(0)
226+
.shouldContain("totalSize = " + expectedTotalValue)
227+
.shouldContain("freeSize = ");
228+
List<String> ls = out.asLinesWithoutVMWarnings();
229+
for (String cur : ls) {
230+
int idx = cur.indexOf("freeSize = ");
231+
if (idx != -1) {
232+
int startNbr = idx+11;
233+
int endNbr = cur.indexOf(' ', startNbr);
234+
if (endNbr == -1) endNbr = cur.length();
235+
String freeSizeStr = cur.substring(startNbr, endNbr);
236+
long freeval = Long.parseLong(freeSizeStr);
237+
long totalval = Long.parseLong(expectedTotalValue);
238+
if (0 <= freeval && freeval <= totalval) {
239+
System.out.println("Found freeSize value " + freeval + " is fine");
240+
} else {
241+
System.out.println("Found freeSize value " + freeval + " is bad");
242+
throw new Exception("Found free size value is bad");
243+
}
244+
}
245+
}
246+
}
247+
248+
214249
private static void testProcessInfo() throws Exception {
215250
Common.logNewTestCase("ProcessInfo");
216251
DockerTestUtils.dockerRunJava(

0 commit comments

Comments
 (0)
Please sign in to comment.