文件泄漏

一、Windows中的文件锁定机制

在Java中操作文件流时,确保及时关闭文件流是非常重要的。在Windows系统上,由于文件锁定机制比较严格,如果一个文件流没有被关闭,可能会导致文件被锁定,从而阻止其它程序对文件的访问,例如进行删除操作。

package com.example.demo.serivice;

import java.io.File;

public interface IFileService {

    void handler(File file) throws Exception;
}
package com.example.demo.serivice.impl;

import cn.hutool.core.io.FileUtil;
import com.example.demo.serivice.IFileService;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;

@Service
public class FileServiceImpl implements IFileService {

    @Override
    public void handler(File file) throws Exception{
        FileInputStream fileInputStream = new FileInputStream(file);
        // 文件处理...
        FileUtil.del(file);
    }
}
package com.example.demo.controller;

import com.example.demo.serivice.IFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;

@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {

    @Autowired
    private IFileService fileService;

    @PostMapping("/handler")
    public void handler(){
        log.info("---------开始测试---------");
        File file =new File("D:\\test\\example.docx");
        try {
            fileService.handler(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("---------结束测试---------");
    }
}

项目服务启动后,访问/file/handler接口,控制台中会报错: 

cn.hutool.core.io.IORuntimeException: FileSystemException: D:\test\example.docx: 另一个程序正在使用此文件,进程无法访问。

在上述业务逻辑处理中,虽然fileInputStream对象和删除操作都是位于handler方法中,但是由于fileInputStream对象未调用close方法关闭文件流,指定的文件还是处于锁定状态,所以后面接着调用FileUtil工具类的del方法进行删除操作是会报错的。 

针对上述这种情况,我们只需要在FileUtil.del();这行代码的前面添加下述代码就可以:

fileInputStream.close();

除了合理的关闭文件流,还有另外一种处理方法,就是在业务逻辑处理的外层调用System.gc();进行垃圾回收。

import com.example.demo.serivice.IFileService;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;

@Service
public class FileServiceImpl implements IFileService {

    @Override
    public void handler(File file) throws Exception{
        FileInputStream fileInputStream = new FileInputStream(file);
        // 文件处理...
    }
}
import cn.hutool.core.io.FileUtil;
import com.example.demo.serivice.IFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;

@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {

    @Autowired
    private IFileService fileService;

    @PostMapping("/handler")
    public void handler(){
        log.info("---------开始测试---------");
        File file =new File("D:\\test\\example.docx");
        try {
            fileService.handler(file);
            System.gc();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FileUtil.del(file);
        }
        log.info("---------结束测试---------");
    }
}

不过并不是很建议采用使用垃圾回收这种处理机制来解决这个问题,因为垃圾回收的过程需要占用CPU时间片,而且在执行垃圾回收时,应用程序的运行会被暂停,频繁的垃圾回收会影响我们的用户体验(Web界面卡顿),这个要慎用,当然作为一个排查问题的调试思路还是可以的。

二、Mac和Linux中的文件锁定机制

在Java中,如果你使用的是FileOutputstream、Filewriter等文件输出流,这些流在打开文件时可能会获取文件锁,而当流关闭时,相应的文件锁会被释放。因此,如果你没有在使用完文件流后关闭它,可能会导致文件锁一直存在,阻止其它进程对文件的操作。

在Mac或Linux系统上,文件锁定机制和Windows系统不同,可能会出现这么一种情况,即使文件流没有关闭但仍然可以删除文件。在Java中,你可以使用close()方法确保在使用完文件流后关闭流,这样就可以避免文件被锁定,使得在Windows、Mac和Linux系统上都能够正常删除文件。

总之,为了确保跨平台的兼容性和良好的文件管理实践,最好在使用完文件流后及时关闭它,以释放文件锁并允许其它进程对文件执行操作。

三、文件泄漏

文件描述符:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

资源泄漏:通常指的是系统资源,比如socket、文件描述符等,因为这些在系统中都是有限制的,如果创建了而不归还,久而久之,就会耗尽资源,导致其它程序不可用。

文件泄漏(资源泄漏包含文件泄漏):是指在程序中未正确关闭文件描述符或文件句柄,导致文件资源无法被释放的情况。这可能导致系统资源被耗尽,使得系统无法正常工作。

在Linux系统中,文件泄漏是一个比较常见的问题,一般情况下,删除一个文件并不会导致文件泄漏。删除文件只是从文件系统中删除文件的目录项(directory entry),而文件系统会维护一个文件的引用计数。只有当文件的引用计数降为零时,文件系统才会释放文件的磁盘空间。 文件泄漏通常是由于在程序中未正确关闭文件描述符或文件句柄,导致文件资源无法被释放,而不是由于删除文件引起的。删除文件只是删除了文件系统中的目录项,并不直接影响已经打开的文件描述符。 

当一个文件被删除时,如果有进程持有该文件的打开句柄,该文件仍然存在于文件系统中(原始文件会一直占用磁盘空间),直到所有的文件描述符都被关闭。 

四、文件泄漏的常见原因及解决方法

以下是一些可能导致文件泄漏的常见原因以及如何避免它们:

4.1 未正确关闭文件描述符

在程序中,确保在使用完文件后调用相应的关闭函数,例如Java语言中的close()方法。

4.2 异常情况未处理

在程序中处理异常或错误时,确保也关闭相应的文件。使用try-catch或类似机制来捕获异常,然后在适当的位置关闭文件。

FileReader reader = null;
try{
	reader = new FileReader("example.txt")
	// 使用文件...
}catch(IOException e){
	e.printStackTrace();
}finally{
	try{
		if(reader!=null){
			reader.close()
		}
	}catch(IOException e){
		e.printStackTrace();
	}
}

如果你在删除文件后仍然遇到文件泄漏问题,你可能需要仔细检查程序中文件的打开和关闭逻辑,确保文件描述符在适当的时候被关闭。 

另外,在Linux系统中可以使用"lsof"命令来查看哪些文件虽然已经删除了,但是仍然被打开,可以帮助你找出潜在的文件泄漏问题。

4.3 使用自动关闭资源(try-with-resources)

如果你使用Java7或更高版本,可以使用try-with-resources语句,它可以自动关闭实现"AutoCloseable"接口的资源,包括文件流。 

import java.io.File;

public interface IFileService {

    void handler(File file);
}
import com.example.demo.serivice.IFileService;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;

@Service
public class FileServiceImpl implements IFileService {

    @Override
    public void handler(File file){
        try(FileInputStream fileInputStream = new FileInputStream(file)){
            // 文件处理...
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
import cn.hutool.core.io.FileUtil;
import com.example.demo.serivice.IFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;

@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {

    @Autowired
    private IFileService fileService;

    @PostMapping("/handler")
    public void handler(){
        log.info("---------开始测试---------");
        File file =new File("D:\\test\\example.docx");
        fileService.handler(file);
        FileUtil.del(file);
        log.info("---------结束测试---------");
    }
}

4.4 使用合适的资源管理工具

对于更复杂的应用程序,可能需要使用资源管理工具或框架,以确保在资源使用完成后进行适当的清理。例如在Java中可以使用Apache Commons IO库中的"IOUtils.closeQuitely()"方法。 

<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.11.0</version>
</dependency>
import java.io.File;

public interface IFileService {

    void handler(File file);
}
import com.example.demo.serivice.IFileService;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;

@Service
public class FileServiceImpl implements IFileService {

    @Override
    public void handler(File file){
        FileInputStream fileInputStream=null;
        try{
            fileInputStream = new FileInputStream(file);
            // 文件处理...
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            IOUtils.closeQuietly(fileInputStream); // 使用框架安全关闭文件流
        }
    }
}
import cn.hutool.core.io.FileUtil;
import com.example.demo.serivice.IFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;

@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {

    @Autowired
    private IFileService fileService;

    @PostMapping("/handler")
    public void handler(){
        log.info("---------开始测试---------");
        File file =new File("D:\\test\\example.docx");
        fileService.handler(file);
        FileUtil.del(file);
        log.info("---------结束测试---------");
    }
}

通过遵循良好的文件资源管理实践,你可以有效地避免文件泄漏问题。同时,在编写代码时要留意异常处理,以确保即使发生错误,也能够正确地关闭文件资源。 

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值