You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Sigar库内存占用过高致程序无响应问题求助

分析与解决方案

你的监控程序出现内存持续升高最终无响应的问题,核心原因是JDBC资源未正确关闭导致的内存泄漏,同时缺乏异常处理机制也可能引发程序静默挂起。下面是具体的修复步骤:

1. 修复JDBC资源泄漏(最关键)

在ConnectionUtils的sendQuery方法中,每次调用createStatement()都会创建新的Statement对象,但你没有关闭这些对象,也没有处理ResultSet的关闭。这些未关闭的JDBC资源会持续占用内存,最终导致内存溢出或程序无响应。

优化后的ConnectionUtils代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ConnectionUtils {
    private Connection conn;
    private String host = "localhost";
    private String port = "3306";
    private String database = "xgen-dashboard";
    private String user = "root";
    private String pass = "asdzxcasdzxc123";

    public ConnectionUtils() {}

    public synchronized void openConnection() {
        if (!isConnected()) {
            try {
                conn = DriverManager.getConnection(
                    "jdbc:mysql://" + host + ":" + port + "/" + database + 
                    "?user=" + user + "&password=" + pass + "&useSSL=false"
                );
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void closeConnection() {
        if (isConnected()) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isConnected() {
        try {
            return conn != null && !conn.isClosed();
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }

    // 重构为专门处理更新操作的方法,用try-with-resources自动关闭Statement
    public synchronized void sendUpdateQuery(String query) {
        if (!isConnected()) {
            openConnection();
        }
        try (Statement stmt = conn.createStatement()) {
            stmt.executeUpdate(query);
        } catch (SQLException e) {
            e.printStackTrace();
            // 连接异常时尝试重建连接并重试
            closeConnection();
            openConnection();
            try (Statement stmt = conn.createStatement()) {
                stmt.executeUpdate(query);
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    }

    // 如果需要执行查询操作,单独实现(调用方需负责关闭ResultSet和Statement)
    public synchronized ResultSet sendSelectQuery(String query) {
        if (!isConnected()) {
            openConnection();
        }
        try {
            Statement stmt = conn.createStatement();
            return stmt.executeQuery(query);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
}

说明:

  • 拆分了查询方法,sendUpdateQuery专门处理UPDATE/INSERT/DELETE操作,利用try-with-resources自动关闭Statement,无需手动清理。
  • 增加了连接异常后的重试逻辑,避免临时数据库连接中断导致程序停止。
  • 简化了原方法中冗余的执行逻辑,代码更清晰易维护。

2. 优化主程序的循环逻辑

主程序中存在两个隐患:一是没有处理Sigar调用可能抛出的异常,二是缺乏资源清理的兜底机制。

优化后的主程序代码:

import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.FileSystemUsage;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;

public class ServerMonitor {
    public static void main(String[] args) {
        ConnectionUtils connection = new ConnectionUtils();
        Sigar sigar = null;
        try {
            sigar = new Sigar();
            connection.openConnection();
            
            while (true) {
                try {
                    // 获取系统资源数据
                    Mem mem = sigar.getMem();
                    CpuPerc cpuperc = sigar.getCpuPerc();
                    FileSystemUsage filesystemusage = sigar.getFileSystemUsage("/");

                    long RAM = Math.round(mem.getUsedPercent());
                    long CPU = Math.round(cpuperc.getCombined() * 100);
                    long HDD = Math.round(filesystemusage.getUsePercent() * 100);

                    // 用String.format替代字符串拼接,更安全易读
                    String query = String.format(
                        "UPDATE ServerInfo SET RAM=%d,CPU=%d,HDD=%d WHERE ServerName='XgenVPS'",
                        RAM, CPU, HDD
                    );
                    connection.sendUpdateQuery(query);
                    System.out.println("Informacje zostały pomyślnie wysłane do bazy danych");
                    
                } catch (SigarException e) {
                    // 捕获Sigar调用异常,避免单次失败终止循环
                    e.printStackTrace();
                    System.err.println("Błąd podczas pobierania informacji o systemie");
                } catch (Exception e) {
                    // 捕获其他所有异常,防止程序静默挂起
                    e.printStackTrace();
                }
                
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 处理线程中断信号,允许程序优雅退出
                    e.printStackTrace();
                    break;
                }
            }
        } catch (SigarException e) {
            e.printStackTrace();
            System.err.println("Nie można zainicjalizować biblioteki Sigar");
        } finally {
            // 程序退出时强制关闭所有资源
            if (sigar != null) {
                sigar.close();
            }
            connection.closeConnection();
        }
    }
}

说明:

  • 增加了Sigar异常捕获,避免单次获取系统资源失败导致整个循环终止。
  • 使用String.format替代字符串拼接,减少语法错误风险。
  • 在finally块中关闭Sigar和数据库连接,确保程序退出时资源被彻底释放。
  • 处理Thread.sleep的中断异常,允许程序收到终止信号时优雅退出。

3. 额外的稳定性建议

  • 使用数据库连接池:替代直接用DriverManager获取连接,比如HikariCP,连接池能更高效地管理连接资源,避免连接泄漏。
  • 引入日志框架:用SLF4J+Logback替代System.out.println和printStackTrace,方便排查问题和监控程序运行状态。
  • 定期验证连接有效性:在循环中定期检查数据库连接是否存活,若失效则自动重建。
  • JVM参数调优:添加-Xmx512m等参数限制最大堆内存,避免内存无限增长。

内容的提问来源于stack exchange,提问作者Mariusz Sternak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 03:59:56