All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.boot.info.ProcessInfo Maven / Gradle / Ivy

/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.info;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.PlatformManagedObject;
import java.lang.reflect.Method;
import java.util.List;

import org.springframework.util.ClassUtils;

/**
 * Information about the process of the application.
 *
 * @author Jonatan Ivanov
 * @author Andrey Litvitski
 * @since 3.3.0
 */
public class ProcessInfo {

	private static final String VIRTUAL_THREAD_SCHEDULER_CLASS = "jdk.management.VirtualThreadSchedulerMXBean";

	private static final boolean VIRTUAL_THREAD_SCHEDULER_CLASS_PRESENT = ClassUtils
		.isPresent(VIRTUAL_THREAD_SCHEDULER_CLASS, null);

	private static final Runtime runtime = Runtime.getRuntime();

	private final long pid;

	private final long parentPid;

	private final String owner;

	public ProcessInfo() {
		ProcessHandle process = ProcessHandle.current();
		this.pid = process.pid();
		this.parentPid = process.parent().map(ProcessHandle::pid).orElse(-1L);
		this.owner = process.info().user().orElse(null);
	}

	/**
	 * Number of processors available to the process. This value may change between
	 * invocations especially in (containerized) environments where resource usage can be
	 * isolated (for example using control groups).
	 * @return result of {@link Runtime#availableProcessors()}
	 * @see Runtime#availableProcessors()
	 */
	public int getCpus() {
		return runtime.availableProcessors();
	}

	/**
	 * Memory information for the process. These values can provide details about the
	 * current memory usage and limits selected by the user or JVM ergonomics (init, max,
	 * committed, used for heap and non-heap). If limits not set explicitly, it might not
	 * be trivial to know what these values are runtime; especially in (containerized)
	 * environments where resource usage can be isolated (for example using control
	 * groups) or not necessarily trivial to discover. Other than that, these values can
	 * indicate if the JVM can resize the heap (stop-the-world).
	 * @return heap and non-heap memory information
	 * @since 3.4.0
	 * @see MemoryMXBean#getHeapMemoryUsage()
	 * @see MemoryMXBean#getNonHeapMemoryUsage()
	 * @see MemoryUsage
	 */
	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
	 */
	@SuppressWarnings("unchecked")
	public VirtualThreadsInfo getVirtualThreads() {
		if (!VIRTUAL_THREAD_SCHEDULER_CLASS_PRESENT) {
			return null;
		}
		try {
			Class mxbeanClass = (Class) ClassUtils
				.forName(VIRTUAL_THREAD_SCHEDULER_CLASS, null);
			PlatformManagedObject mxbean = ManagementFactory.getPlatformMXBean(mxbeanClass);
			int mountedVirtualThreadCount = invokeMethod(mxbeanClass, mxbean, "getMountedVirtualThreadCount");
			long queuedVirtualThreadCount = invokeMethod(mxbeanClass, mxbean, "getQueuedVirtualThreadCount");
			int parallelism = invokeMethod(mxbeanClass, mxbean, "getParallelism");
			int poolSize = invokeMethod(mxbeanClass, mxbean, "getPoolSize");
			return new VirtualThreadsInfo(mountedVirtualThreadCount, queuedVirtualThreadCount, parallelism, poolSize);
		}
		catch (ReflectiveOperationException ex) {
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	private  T invokeMethod(Class mxbeanClass, Object mxbean, String name) throws ReflectiveOperationException {
		Method method = mxbeanClass.getMethod(name);
		return (T) method.invoke(mxbean);
	}

	public long getPid() {
		return this.pid;
	}

	public long getParentPid() {
		return this.parentPid;
	}

	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;

		VirtualThreadsInfo(int mounted, long queued, int parallelism, int poolSize) {
			this.mounted = mounted;
			this.queued = queued;
			this.parallelism = parallelism;
			this.poolSize = poolSize;
		}

		public int getMounted() {
			return this.mounted;
		}

		public long getQueued() {
			return this.queued;
		}

		public int getParallelism() {
			return this.parallelism;
		}

		public int getPoolSize() {
			return this.poolSize;
		}

	}

	/**
	 * Memory information.
	 *
	 * @since 3.4.0
	 */
	public static class MemoryInfo {

		private static final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();

		private static final List garbageCollectorMXBeans = ManagementFactory
			.getGarbageCollectorMXBeans();

		private final MemoryUsageInfo heap;

		private final MemoryUsageInfo nonHeap;

		private final List garbageCollectors;

		MemoryInfo() {
			this.heap = new MemoryUsageInfo(memoryMXBean.getHeapMemoryUsage());
			this.nonHeap = new MemoryUsageInfo(memoryMXBean.getNonHeapMemoryUsage());
			this.garbageCollectors = garbageCollectorMXBeans.stream().map(GarbageCollectorInfo::new).toList();
		}

		public MemoryUsageInfo getHeap() {
			return this.heap;
		}

		public MemoryUsageInfo getNonHeap() {
			return this.nonHeap;
		}

		/**
		 * Garbage Collector information for the process. This list provides details about
		 * the currently used GC algorithms selected by the user or JVM ergonomics. It
		 * might not be trivial to know the used GC algorithms since that usually depends
		 * on the {@link Runtime#availableProcessors()} (see:
		 * {@link ProcessInfo#getCpus()}) and the available memory (see:
		 * {@link MemoryUsageInfo}).
		 * @return {@link List} of {@link GarbageCollectorInfo}.
		 * @since 3.5.0
		 */
		public List getGarbageCollectors() {
			return this.garbageCollectors;
		}

		public static class MemoryUsageInfo {

			private final MemoryUsage memoryUsage;

			MemoryUsageInfo(MemoryUsage memoryUsage) {
				this.memoryUsage = memoryUsage;
			}

			public long getInit() {
				return this.memoryUsage.getInit();
			}

			public long getUsed() {
				return this.memoryUsage.getUsed();
			}

			public long getCommitted() {
				return this.memoryUsage.getCommitted();
			}

			public long getMax() {
				return this.memoryUsage.getMax();
			}

		}

		/**
		 * Garbage collection information.
		 *
		 * @since 3.5.0
		 */
		public static class GarbageCollectorInfo {

			private final String name;

			private final long collectionCount;

			GarbageCollectorInfo(GarbageCollectorMXBean garbageCollectorMXBean) {
				this.name = garbageCollectorMXBean.getName();
				this.collectionCount = garbageCollectorMXBean.getCollectionCount();
			}

			public String getName() {
				return this.name;
			}

			public long getCollectionCount() {
				return this.collectionCount;
			}

		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy