资源泄漏危机:忘记关闭的IO流与数据库连接

前言

请添加图片描述

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:https://www.captainbed.cn/z
在这里插入图片描述
请添加图片描述

1. 错误场景复现

场景1:未关闭的IO流导致文件句柄耗尽

// 读取大文件时忘记关闭流
public void readFile(String path) {
    try {
        FileInputStream fis = new FileInputStream(path);
        BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
        String line;
        while ((line = reader.readLine()) != null) {
            process(line);
        }
        // 忘记调用 fis.close() 和 reader.close()
    } catch (IOException e) {
        e.printStackTrace();
    }
}

后果:连续调用后触发Too many open files系统错误,导致进程崩溃。


场景2:数据库连接泄漏

// 未正确关闭数据库连接
public List<User> queryUsers() {
    Connection conn = dataSource.getConnection();
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT * FROM users");
    
    List<User> users = new ArrayList<>();
    while (rs.next()) {
        users.add(mapRow(rs));
    }
    // 忘记关闭 rs、stmt、conn
    return users; 
}

后果:数据库连接池被占满,新请求因获取不到连接而超时。


场景3:异常分支导致关闭遗漏

// 异常导致close()未执行
public void copyFile(String src, String dest) {
    FileInputStream in = null;
    FileOutputStream out = null;
    try {
        in = new FileInputStream(src);
        out = new FileOutputStream(dest);
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
    } catch (IOException e) {
        log.error("复制失败"); 
    } finally {
        if (in != null) in.close();  // 若out.close()抛异常,in.close()不会执行!
        if (out != null) out.close();
    }
}

隐患:若out.close()抛出异常,in.close()将被跳过。


2. 原理解析

资源泄漏的底层危害

  1. 文件描述符泄漏:

    • 操作系统对进程打开文件数有限制(默认1024)
    • 泄漏导致后续文件操作失败(java.io.IOException: Too many open files)
  2. 数据库连接池耗尽:

    • 连接池中的连接未被归还,新请求排队等待
    • 典型症状:ConnectionTimeoutException
  3. 内存泄漏:

    • 未关闭的资源对象持有底层系统资源,无法被GC回收

为什么GC无法拯救资源泄漏?

  • GC只管理堆内存:资源句柄(如文件描述符、Socket端口)由操作系统管理
  • Finalizer不可靠:finalize()方法执行时机不确定,不能作为关闭资源的保障

3. 正确解决方案

方案1:try-with-resources(Java 7+)

// 自动关闭所有实现了AutoCloseable的资源
public void safeReadFile(String path) {
    try (FileInputStream fis = new FileInputStream(path);
         BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
        
        String line;
        while ((line = reader.readLine()) != null) {
            process(line);
        }
    } catch (IOException e) { 
        // 异常处理
    } 
    // 无需手动调用close()
}
执行顺序:
  1. 资源按照声明逆序关闭(先reader后fis)
  2. 关闭时的异常会被压制,可通过getSuppressed()获取

方案2:连接池的正确使用姿势

// 使用标准数据源管理连接
public List<User> queryUsers() {
    try (Connection conn = dataSource.getConnection(); // 连接池托管
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
        
        List<User> users = new ArrayList<>();
        while (rs.next()) {
            users.add(mapRow(rs));
        }
        return users;
    } // 自动关闭顺序:ResultSet → Statement → Connection(归还连接池)
}
连接池配置关键参数:
spring.datasource:
  hikari:
    maximum-pool-size: 10      # 最大连接数
    idle-timeout: 30000        # 空闲连接超时时间(毫秒)
    connection-timeout: 2000   # 获取连接超时时间
    leak-detection-threshold: 5000 # 泄漏检测阈值

方案3:防御性关闭工具类

// Apache Commons IO提供的安全关闭方法
public static void closeQuietly(Closeable closeable) {
    try {
        if (closeable != null) {
            closeable.close();
        }
    } catch (IOException e) {
        // 静默处理(通常用于finally块)
    }
}

// 改造旧代码
finally {
    IOUtils.closeQuietly(in);  // 处理可能出现的异常
    IOUtils.closeQuietly(out);
}
注意:Java 7+项目应优先使用try-with-resources,此方案适用于兼容旧代码。

4. 工具与最佳实践

资源泄漏检测工具

  1. JDK内置监控:

    # 查看进程文件描述符使用量
    lsof -p <pid> | wc -l
    
  2. 连接池监控:

    • HikariCP的HikariPoolMXBean
    • Druid的监控页面
  3. VisualVM插件:

    • 安装VisualGC插件,观察堆外内存使用情况

代码规范建议

  • 禁止直接调用资源类:统一通过连接池/工具类获取资源
  • 静态代码扫描规则:
    <!-- SpotBugs规则 -->
    <Match>
      <Bug category="BAD_PRACTICE" pattern="OS_OPEN_STREAM" />
    </Match>
    
  • 日志增强:记录资源打开/关闭的日志(调试阶段启用)

5. Code Review检查清单

检查项正确做法
是否使用try-with-resources?Java 7+项目必须使用
连接池配置是否合理?检查最大连接数、超时时间、泄漏检测阈值
finally块是否安全关闭资源?每个close()调用单独try-catch,或使用工具类
是否处理关闭时的异常?至少记录日志,避免掩盖原始错误

6. 真实案例

某票务系统在高峰期出现服务不可用:

  • 现象:数据库连接池达到最大值,所有请求超时
  • 分析:发现历史代码中某处查询未关闭ResultSet,连接未被归还
  • 修复:
    1. 使用try-with-resources重构所有数据库操作
    2. 配置Druid连接池的removeAbandonedTimeout参数自动回收泄漏连接
    3. 增加连接池监控大盘和报警规则

结果:系统连续平稳运行30天无连接池耗尽事件。


总结

  • 资源关闭是义务:必须使用try-with-resructures或finally块
  • 连接池不是银弹:错误使用仍会导致泄漏,需配合监控
  • 工具决定效率:静态扫描 + 运行时监控 = 双重防护
  • 防御性编程:总是假设资源可能泄漏,提前建立防线

下期预告:《异常处理三宗罪:吞异常、过度捕获与日志滥用》——从生产环境血泪案例解析异常处理的最佳实践。

联系作者

职场经验分享,Java面试,简历修改,求职辅导尽在科技泡泡
思维导图面试视频合集
在这里插入图片描述
在这里插入图片描述

MongoDB性能监控实战:从mongostat到Prometheus全链路诊断 MongoDB性能监控是保障数据库稳定运行的基础能力,其核心在于理解WiredTiger存储引擎的内存、锁、IO等底层行为。通过mongostat可实时观测脏页率、缓存使用率与锁队列长度;mongotop则精准定位高IO集合,辅助索引优化与写入模式调优。结合profile+explain可深度剖析慢查询执行路径,而Prometheus+Grafana实现指标聚合与告警闭环。本文覆盖Windows本地启动失败排查、生产环境零侵入采集、连接泄漏识别等高频场景,特别强调‘netIn/netOut’网络吞吐与‘lo 阅读详情

相关推荐

Tomcat假死排查全攻略:从CLOSE_WAIT到线程死锁的深度诊断

在分布式系统与微服务架构中,应用服务器的稳定性直接影响服务可用性。Tomcat作为广泛使用的Java Web容器,其假死问题往往源于资源耗尽、线程阻塞或连接泄漏等深层矛盾。理解TCP连接状态机原理,特别是CLOSE_WAIT状态的产生机制,是诊断网络层问题的关键;而掌握JVM线程模型与锁竞争原理,则能有效定位应用层死锁与性能瓶颈。这些技术对于保障高并发场景下的服务韧性具有重要价值,尤其在电商、金融等对响应延迟敏感的应用场景中。本文通过系统化的排查框架,结合线程转储分析与网络连接监控,帮助开发者快速定位Tom

weixin_30780649的博客 429

Java项目内存泄漏别慌!手把手教你用MAT分析.hprof文件定位问题(附JDK版本选择避坑)

本文详细介绍了如何使用Eclipse Memory Analyzer Tool(MAT)分析Java项目中的内存泄漏问题,通过.hprof文件精准定位问题源头。文章还提供了JDK版本选择的避坑指南,帮助开发者在处理内存溢出(OOM)时高效应对,提升系统稳定性。

weixin_42529328的博客 397

Java异步编程难题拆解:驾驭CompletableFuture的深水区

尽管提供了强大的异步编程能力,但在复杂流程编排中仍显不足。背压需求:响应式编程(Reactor/RxJava)分布式事务:Saga模式 + 消息队列亿级流量:协程(Project Loom的Virtual Thread)异步编程的本质是对不确定性的管理。优秀的异步设计 = 正确的工具选择 + 清晰的错误处理契约 + 精准的资源控制。通过本文的深度解析,希望您能驾驭Java异步编程的复杂场景,将CompletableFuture从工具升华为工程艺术的核心构件。注。

caishuangxi111的博客 975

SQL Server数据库管理实战:从SSMS工具链到健康度监控

数据库管理不是编写SQL语句的技能,而是对数据生命周期、访问行为与系统性能的全局掌控。其核心原理在于理解SQL Server三层架构(物理文件/逻辑对象/会话配置),依托SSMS等专业工具构建可量化的运维边界与健康指标体系。技术价值体现在降低生产事故率、提升故障响应速度、保障数据安全与合规性。典型应用场景包括多租户环境治理、高并发订单系统优化、医保/金融类关键业务稳定性保障。本文聚焦Microsoft SQL Server Management Studio(SSMS)深度用法、数据库健康阈值设定及真实慢查

weixin_34279184的博客 376

【StoryBank】实战复盘:一次 Spark on K8s 的 OOM 杀 Pod危机,排查,止血,定位根因的全流程复盘。

问题解决思路OOMKiller 杀掉 Pod?原因是容器总内存超限(堆 + 堆外 + native)JVM 抛 Direct buffer OOM会释放内存吗?❌ 不会立即释放,Cleaner 异步清理,风险依旧如何监控 native memory?开启 -XX:NativeMemoryTracking=summary,使用 jcmdOffHeap 属于 Spark 内部管理;Overhead 是 container 层保底兜底内存MaxDirectMemorySize 超限时行为?

petrel2015的博客 916

基于Matlab与Bladed联合仿真的交互软件设计

基于Matlab与Bladed的交互软件设计

weixin_45014222的博客 817

避坑指南:ros2_control在Gazebo中的常见配置错误及解决方案

本文系统梳理了在Gazebo仿真环境中配置ros2_control框架时最常见的错误,并提供了详细的解决方案。重点解析了YAML配置文件的结构陷阱、URDF/XACRO中关键标签的正确写法、Launch文件的启动顺序管理,并分享了实用的调试命令与故障排查思路,帮助开发者高效避坑,实现流畅的关节运动控制。

q5r6s7的博客 975

oscp备考,oscp系列——Djinn3靶场:ssti漏洞,pkexec提权

oscp备考,oscp系列——Djinn3靶场:ssti漏洞,pkexec提权难度简单偏上对于低权限shell获取涉及:ssti漏洞对于提权:pkexec提权。

2202_75361164的博客 635

基于SSD2828和STM32的MIPI显示屏驱动系统设计与实现

在硬件层面,需要将STM32、SSD2828和MIPI显示屏连接起来。STM32与SSD2828的连接:通过SPI接口连接STM32和SSD2828,用于配置和控制SSD2828。SSD2828与MIPI显示屏的连接:通过MIPI DSI接口连接SSD2828和MIPI显示屏,用于传输显示数据。

qq_38334677的博客 7151

Open3D 基于法向量夹角的特征点提取

基于法向量夹角的特征点提取是一种在点云处理中用于识别表面变化显著区域的技术。通过计算点云中每个点的法向量与其邻域点法向量之间的夹角,可以判断点云表面的局部变化程度,并提取那些法向量变化较大的点作为特征点。这种方法特别适用于提取边缘、角点等关键几何特征。

纵马踏花向自由 1554

YOLOv8改进策略【卷积层】| CVPR-2023 部分卷积 PConv 轻量化卷积,降低内存占用

本文记录的是利用优化的目标检测方法研究。深度可分离卷积可以减少FLOPs,但会导致更高的内存访问,引起延迟并减慢整体计算。部分卷积利用逐点卷积处理通道冗余,减少模型计算量和内存访问量。Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks及其改进的实现代码如下: 四、创新模块 4.1 改进点⭐ 模块改进方法:直接加入(第五节讲解添加步骤)。模块加入如下:模块改进方法:基于的(第五节讲解添加步骤)。第二种改进方法是对中的进行改进,并将

Limiiiing的博客 1016

RK3566/RK3568 Android 11 无操作自动隐藏导航栏、底部上拉显示导航栏

RK3566/RK3568 Android 11 定制大全在系统服务中增加无操作自动隐藏导航栏方法,在上层app动态调用无操作自动隐藏导航栏方法,系统会在5秒无操作后自动隐藏导航栏,隐藏导航栏后从底部上拉可显示导航栏,设备关机和重启后也能继续生效。

qq_23921815的博客 1260

OSG第三方库编译之二十一:minizip编译(Windows、Linux、Macos环境下编译)

OSG编译;第三方库;跨平台编译;minizip编译(Windows、Linux、Macos环境下编译)

LuckyHanMo的博客 2624

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: 单元测试的误区:为何你的@Test注解不生效?
下一篇: == 与 equals 的陷阱:对象比较为何总出错?
雪碧有白泡泡
雪碧有白泡泡 优质创作者: 编程框架技术领域 优质创作者: 编程框架技术领域
博客等级 码龄5年 1万+粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪碧有白泡泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值