[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[备忘]FileInputStream和FileOutputStream对象,引起的大量Finalizer对象,内存溢出?

上一篇:[备忘]JVM堆外内存之-XX:NativeMemoryTracking不咋好使
下一篇:[备忘]java缓存id范围,并发获取只有一个执行。

添加日期:2021/9/6 14:39:29 快速返回   返回列表 阅读717次
内存总是不停的涨,查不到原因,莫名其妙。属于堆外内存,不好查。
做dump,发现有多个FileInputStream和FileOutputStream对象,
<1>查看out reference,发现是程序中处理的文件。不过都已经close了,不知为何还存在。
<2>查看in referenct,发现被java.lang.ref.Finalizer持有。
------------------------------
搜了半天,整理如下:
(1)FileInputStream和FileOutputStream重写了Object的finalize()方法。属于保底动作,关闭流的操作。
(2)创建一个FileInputStream对象(O1)时,同时会创建一个Finalizer对象(F1),F1会引用O1。
在发生GC时,即使O1已经关闭,没用了,仍然不会被直接回收,因为F1在引用它。
然后JVM发现此种情况,会将F1放入Finalizer的static的ReferenceQueue中。
(3)JVM中有一个名为“Finalizer”的线程,负责清理ReferenceQueue中的对象。清理的过程,就是执行O1的finalize方法,并同时从队列中移除。被清理之后,再没有引用指向实现了finalize方法对象了,那么在下一次GC的时候,此对象就可以被回收了 。 
(4)即使代码中已经显式close了,仍然要走这个两次GC的流程,非常浪费。特别是大量文件读写的系统,将增加GC的时间。如果两次GC间隔很久,比如老年代,那么这些对象就堆积在内存中,造成内存上涨。
(5)名为“Finalizer”的线程,优先级很低,可能不会及时执行清理,或者干脆不执行清理。
(6)Java9,已经废弃了Object的finalize()方法,因为它可能造成内存回收性能问题,可能造成内存泄漏等问题。
https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#finalize--
(7)jdk12的新特性中移除了FileInputStream、FileOutputStream、Java.util.ZipFile/Inflator/Deflator的finalize方法。
------------------------------
解决方法:
 Java 7引入了两种创建流的方法,
InputStream is = java.nio.file.Files.newInputStream(myfile.toPath()); 
OutputStream os = java.nio.file.Files.newOutputStream(myfile.toPath());

InputStream in = Files.newInputStream(Paths.get(filePath),StandardOpenOption.READ);

newOutputStream会自动加WRITE参数,所以就不用写了。


public OutputStream newOutputStream(Path path, OpenOption... options)
    throws IOException
{
    int len = options.length;
    Set<OpenOption> opts = new HashSet<OpenOption>(len + 3);
    if (len == 0) {
        opts.add(StandardOpenOption.CREATE);
        opts.add(StandardOpenOption.TRUNCATE_EXISTING);
    } else {
        for (OpenOption opt: options) {
            if (opt == StandardOpenOption.READ)
                throw new IllegalArgumentException("READ not allowed");
            opts.add(opt);
        }
    }
    opts.add(StandardOpenOption.WRITE);
    return Channels.newOutputStream(newByteChannel(path, opts));
}


当然,能升级到JDK12,自然就没问题了。
------------------------------
如何正确的关闭流:
InputStream,OutputStream,Reader,Write这些都实现了Closeable接口。
(1)传统的finally大法
org.apache.commons.io.IOUtils
try{
   .......
   ........
} finally {
    IOUtils.closeQuietly(is);
    IOUtils.closeQuietly(os);
}
(2)try-with-resources语法糖
Java7开始,可以使用 try-with-resources写法,是语法糖~~
把这些流对象,放到try的括号里,编译时会自动编译成上面那种final的形式。
try (FileInputStream fis = new FileInputStream("../input/fxrates.txt");
      FileOutputStream fos = new FileOutputStream("../output/fxrates.tx")) {

      // code for reading contents

      .....

 } catch (IOException ioex) {
   ioex.printStackTrace();
 }

反正这么改完,内存莫名其妙就不涨了,我也是百思不得其解啊~~
按理说,调用close了,native内存也就释放了啊。
堆内存里有一些Finalizer对象,也占不了多少内存吧。
莫名其妙……

https://blog.csdn.net/weixin_41771503/article/details/79789086
这个说,嵌套的写法,要每层都显式关闭。
如果不行,回头我也试试。
 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved