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
相关产品推荐
相关产品推荐

