11/*
2- * Copyright 2012-2024 the original author or authors.
2+ * Copyright 2012-2025 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2020import java .lang .management .MemoryMXBean ;
2121import java .lang .management .MemoryUsage ;
2222import java .lang .management .PlatformManagedObject ;
23+ import java .lang .reflect .Method ;
24+
25+ import org .springframework .util .ClassUtils ;
2326
2427/**
2528 * Information about the process of the application.
3033 */
3134public class ProcessInfo {
3235
36+ private static final String VIRTUAL_THREAD_SCHEDULER_CLASS = "jdk.management.VirtualThreadSchedulerMXBean" ;
37+
38+ private static final boolean VIRTUAL_THREAD_SCHEDULER_CLASS_PRESENT = ClassUtils
39+ .isPresent (VIRTUAL_THREAD_SCHEDULER_CLASS , null );
40+
3341 private static final Runtime runtime = Runtime .getRuntime ();
3442
3543 private final long pid ;
@@ -85,14 +93,18 @@ public MemoryInfo getMemory() {
8593 */
8694 @ SuppressWarnings ("unchecked" )
8795 public VirtualThreadsInfo getVirtualThreads () {
96+ if (!VIRTUAL_THREAD_SCHEDULER_CLASS_PRESENT ) {
97+ return null ;
98+ }
8899 try {
89- Class <PlatformManagedObject > mxBeanClass = (Class <PlatformManagedObject >) Class
90- .forName ("jdk.management.VirtualThreadSchedulerMXBean" );
91- Object bean = ManagementFactory .getPlatformMXBean (mxBeanClass );
92- return new VirtualThreadsInfo ((Integer ) mxBeanClass .getMethod ("getMountedVirtualThreadCount" ).invoke (bean ),
93- (Long ) mxBeanClass .getMethod ("getQueuedVirtualThreadCount" ).invoke (bean ),
94- (Integer ) mxBeanClass .getMethod ("getParallelism" ).invoke (bean ),
95- (Integer ) mxBeanClass .getMethod ("getPoolSize" ).invoke (bean ));
100+ Class <PlatformManagedObject > mxbeanClass = (Class <PlatformManagedObject >) ClassUtils
101+ .forName (VIRTUAL_THREAD_SCHEDULER_CLASS , null );
102+ PlatformManagedObject mxbean = ManagementFactory .getPlatformMXBean (mxbeanClass );
103+ int mountedVirtualThreadCount = invokeMethod (mxbeanClass , mxbean , "getMountedVirtualThreadCount" );
104+ long queuedVirtualThreadCount = invokeMethod (mxbeanClass , mxbean , "getQueuedVirtualThreadCount" );
105+ int parallelism = invokeMethod (mxbeanClass , mxbean , "getParallelism" );
106+ int poolSize = invokeMethod (mxbeanClass , mxbean , "getPoolSize" );
107+ return new VirtualThreadsInfo (mountedVirtualThreadCount , queuedVirtualThreadCount , parallelism , poolSize );
96108 }
97109 catch (ReflectiveOperationException ex ) {
98110 return null ;
@@ -111,6 +123,12 @@ public String getOwner() {
111123 return this .owner ;
112124 }
113125
126+ @ SuppressWarnings ("unchecked" )
127+ private <T > T invokeMethod (Class <?> mxbeanClass , Object mxbean , String name ) throws ReflectiveOperationException {
128+ Method method = mxbeanClass .getMethod (name );
129+ return (T ) method .invoke (mxbean );
130+ }
131+
114132 /**
115133 * Virtual threads information.
116134 *
@@ -126,7 +144,7 @@ public static class VirtualThreadsInfo {
126144
127145 private final int poolSize ;
128146
129- public VirtualThreadsInfo (int mounted , long queued , int parallelism , int poolSize ) {
147+ VirtualThreadsInfo (int mounted , long queued , int parallelism , int poolSize ) {
130148 this .mounted = mounted ;
131149 this .queued = queued ;
132150 this .parallelism = parallelism ;
0 commit comments