File 未释放文件权柄问题处理

2023-05-08,,

Unreleased Resource: Files

Abstract

程序可能无法释放某个文件句柄。

Explanation

程序可能无法成功释放某一个文件句柄。 资源泄露至少有两种常见的原因: - 错误状况及其他异常情况。
- 未明确程序的哪一部份负责释放资源。 大部分 Unreleased Resource 问题只会导致一般的软件可靠性问
题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻
击。
例 1:下面的方法绝不会关闭它所打开的文件句柄。 ZipFile 中的 finalize() 方法最终会调用
close(),但是不能确定何时会调用 finalize() 方法。 在繁忙的环境中,这会导致 JVM 用尽它所有的文
件句柄。

public void printZipContents(String fName) throws ZipException, IOException, SecurityException,
    IllegalStateException, NoSuchElementException{
  ZipFile zf = new ZipFile(fName);
  Enumeration<ZipEntry> e = zf.entries();
  while (e.hasMoreElements()) {
    printFileInfo(e.nextElement());
  }
}

例 2:正常情况下,以下修复代码会在输出所有 zip 文件条目之后正常关闭文件句柄。 如果迭代这些条目时
出现异常,则不会关闭 zip 文件句柄。 如果这种情况经常出现,JVM 就可能耗尽所有可用的文件句柄。

public void printZipContents(String fName) throws ZipException, IOException, SecurityException,
  IllegalStateException, NoSuchElementException{
    ZipFile zf = new ZipFile(fName);
    Enumeration<ZipEntry> e = zf.entries();
    while (e.hasMoreElements()) {
      printFileInfo(e.nextElement());
    }
}

Recommendation

1.绝不要依赖 finalize() 回收资源。 为了使对象的 Finalize() 方法能被调用,垃圾收集器必须确认对
象符合垃圾回收的条件。 但是垃圾收集器只有在 JVM 内存过小时才会使用。因此,无法保证何时能够调用该
对象的 finalize() 方法。 垃圾收集器最终运行时,可能出现这样的情况,即在短时间内回收大量的资源,
这种情况会导致“突发”性能,并降低总体系统通过量。 随着系统负载的增加,这种影响会越来越明显。 最
后,如果某一资源回收操作被挂起(例如该操作需要通过网络进行通信),那么执行 finalize() 方法的线
程也将被挂起。 2. 在 finally 代码段中释放资源。
例 2 中的代码可按以下方式改写:

public void printZipContents(String fName) throws ZipException, IOException, SecurityException,
  IllegalStateException, NoSuchElementException{
    ZipFile zf;
    try {
      zf = new ZipFile(fName);
      Enumeration<ZipEntry> e = zf.entries();
    }
    finally {
      if (zf != null) {
        safeClose(zf);
      }
    }
}

public static void safeClose(ZipFile zf) {
    if (zf != null) {
    try {
      zf.close();
    } catch (IOException e) {
      log(e);
    }
  }
}

以上方案使用了一个助手函数,用以记录在尝试关闭文件时可能产生的异常。 该助手函数大约会在需要关闭
文件时重新使用。 同样,printZipContents 方法不会将 zf 对象初始化为 null。 而是进行检查,以确保
调用 safeClose() 之前,zf 不是 null。 如果没有检查 null,Java 编译器会报告 zf 可能没有进行初始
化。 编译器做出这一判断源于 Java 可以检测未初始化的变量。 如果用一种更加复杂的方法将 zf 初始化为
null,那么 Java 编译器就无法检测 zf 是否已被初始化。

File 未释放文件权柄问题处理的相关教程结束。

《File 未释放文件权柄问题处理.doc》

下载本文的Word格式文档,以方便收藏与打印。