Spring Boot项目启动时输出PID、CPU和内存信息的4种方法

文章目录

在Spring Boot项目中,可以通过以下几种方式输出项目的PID、CPU和内存占用信息:

方法1:使用Spring Boot Actuator(推荐)

  1. 首先添加依赖:

    xml 复制代码
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  2. application.yml中启用相关端点:

    yaml 复制代码
    # 启用所有端点(或只启用需要的)
    management:
      endpoints:
        web:
          exposure:
            include: '*'  # 暴露所有端点(生产环境慎用)
        health:
          show-details: always  # 显示健康检查详情
        metrics:
          enabled: true
  3. 访问以下端点获取信息:

    • /actuator/info - 基本信息
    • /actuator/health - 健康状态
    • /actuator/metrics - 各种指标
    • /actuator/metrics/system.cpu.usage - CPU使用率
    • /actuator/metrics/jvm.memory.used - JVM内存使用

方法2:自定义启动时输出(简单直接)

在Spring Boot主类或配置类中添加:

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.PostConstruct;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;

@SpringBootApplication
public class YourApplication {
	public static void main(String[] args) {
		SpringApplication.run(YourApplication.class, args);
	}
	
	@PostConstruct
	public void printSystemInfo() {
		// 获取PID
		RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
		String jvmName = runtimeBean.getName();
		long pid = Long.parseLong(jvmName.split("@")[0]);
		
		// 内存信息
		Runtime runtime = Runtime.getRuntime();
		long maxMemory = runtime.maxMemory();
		long allocatedMemory = runtime.totalMemory();
		long freeMemory = runtime.freeMemory();
		
		// 输出信息
		System.out.println("\n==========================================");
		System.out.println(" Application System Information");
		System.out.println("==========================================");
		System.out.println(" PID: " + pid);
		System.out.println(" CPU Cores: " + runtime.availableProcessors());
		System.out.println(" Memory Info:");
		System.out.println(" - Max: " + maxMemory / 1024 / 1024 + " MB");
		System.out.println(" - Allocated: " + allocatedMemory / 1024 / 1024 + " MB");
		System.out.println(" - Free: " + freeMemory / 1024 / 1024 + " MB");
		System.out.println(" - Used: " + (allocatedMemory - freeMemory) / 1024 / 1024 + " MB");
		System.out.println("==========================================\n");
	}
}

方法3:使用Logback/SLF4J输出(生产环境推荐)

logback-spring.xml配置中添加:

xml 复制代码
<configuration>
    <!-- 1. 从Spring环境变量中获取应用名称 -->
    <springProperty name="app.name" source="spring.application.name" defaultValue="spring-boot-app"/>
    
    <!-- 2. 定义控制台日志输出格式 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志格式:时间 + 线程 + 日志级别 + 类名 + 消息 -->
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 3. 设置根日志级别和输出目标 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>

    <!-- 4. 定义SystemInfoLogger Bean并指定初始化方法 -->
    <bean id="systemInfoLogger" class="com.your.package.SystemInfoLogger" init-method="logSystemInfo"/>
</configuration>

如果使用的是 logback.xml(而非 logback-spring.xml)并遇到 <bean> 标签启动报错,这是因为 Logback 原生配置不支持直接定义 Spring Bean。问题根源在于配置文件的解析机制不同:

  • logback.xml:由 Logback 原生解析,不支持 Spring 的 <bean> 标签
  • logback-spring.xml:由 Spring Boot 解析,扩展了 Spring 的 XML 命名空间,支持 <springProperty><bean> 等标签

如果将 logback.xml 重命名为 logback-spring.xml,Spring Boot 仍然没有正确解析 <bean> 标签,可以使用下面的替代方案。

然后创建对应的Java类:

java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;

@Component
public class SystemInfoLogger {
    // 1. 通过SLF4J获取Logger实例
    private static final Logger logger = LoggerFactory.getLogger(SystemInfoLogger.class);

    // 2. Bean初始化后调用的方法
    public void logSystemInfo() {
        // 3. 获取PID(通过RuntimeMXBean)
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
        long pid = Long.parseLong(runtimeBean.getName().split("@")[0]);
        
        // 4. 获取内存信息(通过Runtime)
        Runtime runtime = Runtime.getRuntime();
        long usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;

        // 5. 格式化输出到日志(使用占位符{}避免字符串拼接)
        logger.info("\n" +
                        "=========================================\n" +
                        " Application: {}\n" +
                        " PID: {}\n" +
                        " CPU Cores: {}\n" +
                        " Memory:\n" +
                        "  - Max: {} MB\n" +
                        "  - Total: {} MB\n" +
                        "  - Free: {} MB\n" +
                        "  - Used: {} MB\n" +
                        "=========================================",
                System.getProperty("spring.application.name", "Spring Boot Application"),
                pid,
                runtime.availableProcessors(),
                runtime.maxMemory() / 1024 / 1024,
                runtime.totalMemory() / 1024 / 1024,
                runtime.freeMemory() / 1024 / 1024,
                usedMemory
        );
    }
}
替代方案
java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;

@Component
public class SystemInfoPrinter implements ApplicationListener<ContextRefreshedEvent> {
    // 通过SLF4J获取Logger实例,用于记录日志信息
    private static final Logger logger = LoggerFactory.getLogger(SystemInfoPrinter.class);

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
        // 获取PID(通过RuntimeMXBean)
        long pid = Long.parseLong(runtimeBean.getName().split("@")[0]);
        Runtime runtime = Runtime.getRuntime();

        // 格式化输出到日志(使用占位符{}避免字符串拼接)
        logger.info("\n" +
                        "=========================================\n" +
                        " Application: {}\n" +
                        " PID: {}\n" +
                        " CPU Cores: {}\n" +
                        " Memory:\n" +
                        "  - Max: {} MB\n" +
                        "  - Total: {} MB\n" +
                        "  - Free: {} MB\n" +
                        "  - Used: {} MB\n" +
                        "=========================================",
                System.getProperty("spring.application.name", "Spring Boot Application"),
                pid,
                runtime.availableProcessors(),
                runtime.maxMemory() / 1024 / 1024,
                runtime.totalMemory() / 1024 / 1024,
                runtime.freeMemory() / 1024 / 1024,
                (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
        );
    }
}
知识拓展:@PostConstruct与ApplicationListener差异

1. 核心异同对比表

特性 ApplicationListener @PostConstruct
触发时机 Spring上下文完全刷新后(所有Bean初始化完成) Bean自身初始化完成后(依赖注入之后)
执行顺序 较晚(确保所有Bean可用) 较早(当前Bean初始化后立即执行)
事件类型 响应ContextRefreshedEvent事件 基于JSR-250标准注解
适用场景 需要访问其他完全初始化的Bean时 只需当前Bean自身资源的场景
多次执行风险 可能触发多次(重复上下文刷新时) 仅执行一次
Spring版本兼容性 所有Spring版本 需Spring 2.5+(支持JSR-250)
日志输出可靠性 更高(确保日志系统已初始化) 可能因日志系统未完全初始化导致输出丢失(极少数情况)

2. 执行时机差异图解

plaintext 复制代码
Spring启动流程:
1. 创建Bean实例
	└─ 方案二@PostConstruct执行 ← 此时部分依赖可能未就绪
2. 依赖注入
3. 初始化回调
4. 发布ContextRefreshedEvent
	└─ 方案一ApplicationListener执行 ← 此时所有Bean已就绪

方法4:使用第三方库(如oshi)

添加依赖:

xml 复制代码
<dependency>
	<groupId>com.github.oshi</groupId>
	<artifactId>oshi-core</artifactId>
	<version>6.4.0</version>
</dependency>

然后使用:

java 复制代码
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;

public void printDetailedSystemInfo() {
	SystemInfo si = new SystemInfo();
	HardwareAbstractionLayer hal = si.getHardware();
	CentralProcessor processor = hal.getProcessor();
	
	System.out.println("CPU: " + processor.getProcessorIdentifier().getName());
	System.out.println("Physical CPUs: " + processor.getPhysicalProcessorCount());
	System.out.println("Logical CPUs: " + processor.getLogicalProcessorCount());
	System.out.println("CPU Load: " + hal.getProcessor().getSystemLoadAverage());
}

输出示例

无论使用哪种方法,启动后你将看到类似这样的输出:

复制代码
==========================================
Application System Information
==========================================
PID: 12345
CPU Cores: 8
Memory Info:
- Max: 4096 MB
- Allocated: 1024 MB
- Free: 756 MB
- Used: 268 MB
==========================================

这些方法可以根据你的需求选择使用,对于生产环境,推荐使用方法1(Actuator)或方法3(日志记录)。

相关推荐
MegaDataFlowers2 小时前
Maven
java·maven
朱一头zcy2 小时前
Java基础复习03:面向对象基础入门(类与对象的概念 构造器 this关键字)
java·笔记
却道天凉_好个秋2 小时前
音视频学习(九十):再谈srt协议
后端·音视频·srt
牧天白衣.2 小时前
02-基础语法
java
想你的液宝2 小时前
下单系统寄/到件省市区关联选择功能实现方案
后端
dawudayudaxue2 小时前
Eclipse安卓环境配置
android·java·eclipse
iPadiPhone2 小时前
性能优化的“双刃剑”:MySQL 查询缓存深度架构解析与面试复盘
java·后端·mysql·缓存·面试·性能优化
兆子龙2 小时前
ahooks useDebounce 与 useThrottle:防抖节流的最佳实践
java·javascript
WmKong2 小时前
告别 GORM 的“魔法字符串”和“事务满天飞”:我开源了一个强类型查询构建库
后端