diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java index 85f46e0266ce..b9ad4f64e3ab 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java @@ -24,6 +24,7 @@ * Information about the process of the application. * * @author Jonatan Ivanov + * @author Andrey Litvitski * @since 3.3.0 */ public class ProcessInfo { @@ -72,6 +73,29 @@ public MemoryInfo getMemory() { return new MemoryInfo(); } + /** + * Virtual threads information for the process. These values provide details about the + * current state of virtual threads, including the number of mounted threads, queued threads, + * the parallelism level, and the thread pool size. + * @return an instance of {@link VirtualThreadsInfo} containing information about virtual threads, + * or {@code null} if the VirtualThreadSchedulerMXBean is not available. + * @since 3.5.0 + */ + public VirtualThreadsInfo getVirtualThreads() { + try { + Class mxBeanClass = Class.forName("jdk.management.VirtualThreadSchedulerMXBean"); + Object bean = ManagementFactory.getPlatformMXBean(mxBeanClass); + return new VirtualThreadsInfo( + (Integer) mxBeanClass.getMethod("getMountedVirtualThreadCount").invoke(bean), + (Long) mxBeanClass.getMethod("getQueuedVirtualThreadCount").invoke(bean), + (Integer) mxBeanClass.getMethod("getParallelism").invoke(bean), + (Integer) mxBeanClass.getMethod("getPoolSize").invoke(bean) + ); + } catch (ReflectiveOperationException e) { + return null; + } + } + public long getPid() { return this.pid; } @@ -84,6 +108,46 @@ public String getOwner() { return this.owner; } + /** + * Virtual threads information. + * + * @since 3.5.0 + */ + public static class VirtualThreadsInfo { + + private final int mounted; + + private final long queued; + + private final int parallelism; + + private final int poolSize; + + public VirtualThreadsInfo(int mounted, long queued, int parallelism, int poolSize) { + this.mounted = mounted; + this.queued = queued; + this.parallelism = parallelism; + this.poolSize = poolSize; + } + + public long getMounted() { + return this.mounted; + } + + public long getQueued() { + return this.queued; + } + + public int getParallelism() { + return this.parallelism; + } + + public int getPoolSize() { + return this.poolSize; + } + + } + /** * Memory information. * diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java index 37ba3b0173c0..70507bccfc74 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java @@ -19,6 +19,8 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.info.ProcessInfo.MemoryInfo.MemoryUsageInfo; +import org.springframework.boot.info.ProcessInfo.VirtualThreadsInfo; +import org.springframework.util.ClassUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -26,6 +28,7 @@ * Tests for {@link ProcessInfo}. * * @author Jonatan Ivanov + * @author Andrey Litvitski */ class ProcessInfoTests { @@ -54,4 +57,23 @@ void memoryInfoIsAvailable() { assertThat(nonHeapUsageInfo.getMax()).isEqualTo(-1); } + @Test + void virtualThreadsInfoIsNullWhenMXBeanIsNotAccessible() { + if (ClassUtils.isPresent("jdk.management.VirtualThreadSchedulerMXBean", null)) { + ProcessInfo processInfo = new ProcessInfo(); + + VirtualThreadsInfo virtualThreadsInfo = processInfo.getVirtualThreads(); + + assertThat(virtualThreadsInfo).isNotNull(); + assertThat(virtualThreadsInfo.getMounted()).isGreaterThanOrEqualTo(0); + assertThat(virtualThreadsInfo.getQueued()).isGreaterThanOrEqualTo(0); + assertThat(virtualThreadsInfo.getParallelism()).isGreaterThan(0); + assertThat(virtualThreadsInfo.getPoolSize()).isGreaterThan(0); + } else { + ProcessInfo processInfo = new ProcessInfo(); + + assertThat(processInfo.getVirtualThreads()).isNull(); + } + } + }