终结器线程等待时java.util.ref.Finalizer内存泄漏
分析堆转储时,我会查找java.lang.ref.Finalizer类的实例。Finalizer有“next”和“prev”成员字段用于维护链表。我总是将FileInputStream作为列表的尾部,将FileOutputStream作为列表的前一个条目(分析了几个堆转储)。FileInputStream和FileOutputStream的文件描述符始终分别为0和1:终结器线程等待时java.util.ref.Finalizer内存泄漏,java,memory,garbage-collection,jvm,finalize,Java,Memory,Garbage Collection,Jvm,Finalize,分析堆转储时,我会查找java.lang.ref.Finalizer类的实例。Finalizer有“next”和“prev”成员字段用于维护链表。我总是将FileInputStream作为列表的尾部,将FileOutputStream作为列表的前一个条目(分析了几个堆转储)。FileInputStream和FileOutputStream的文件描述符始终分别为0和1: +---[Pending Finalization] java.lang.ref.Finalizer |
+---[Pending Finalization] java.lang.ref.Finalizer
| |
| +---queue java.lang.ref.ReferenceQueue [Stack Local]
| |
| +---referent java.io.FileInputStream
| | |
| | +---closed = boolean false
| | |
| | +---closeLock java.lang.Object
| | |
| | +---fd java.io.FileDescriptor
| | |
| | +---closed = boolean false
| | |
| | +---fd = int 0
| | |
| | +---parent java.io.FileInputStream
| |
| +---prev [Pending Finalization] java.lang.ref.Finalizer
| |
| +---queue java.lang.ref.ReferenceQueue [Stack Local]
| |
| +---next [Pending Finalization] java.lang.ref.Finalizer
| |
| +---referent java.io.FileOutputStream
| | |
| | +---append = boolean false
| | |
| | +---closed = boolean false
| | |
| | +---closeLock java.lang.Object
| | |
| | +---fd java.io.FileDescriptor
| | |
| | +---closed = boolean false
| | |
| | +---fd = int 1 0x00000001
| | |
| | +---parent java.io.FileOutputStream
| |
| +---prev [Pending Finalization] java.lang.ref.Finalizer
也许下面的测试程序会对此有所启发:
Field fd = FileDescriptor.class.getDeclaredField("fd");
fd.setAccessible(true);
System.out.println("stdin: "+fd.get(FileDescriptor.in));
System.out.println("stdout: "+fd.get(FileDescriptor.out));
System.out.println("stderr: "+fd.get(FileDescriptor.err));
stdin:0
标准:1
标准:2
,请注意,对于JDK 8,这仅适用于类Unix系统
换句话说,您正在查看由System.In
和System.out
封装的文件流,当然,这些文件流永远不会被垃圾收集,通常也不会对它们调用close()
终结不支持任何类型的选择退出,因此具有“非平凡的finalize()
方法”的类的任何实例在构造时都将获得终结器引用,即使创建者知道对象永远不会被终结
最新的JDK版本为此使用了,这允许在使用现有的文件描述符构建文件输入流
或文件输出流
时不注册清洁器,这是stdin和stdout的情况。它还允许立即清理,从而在close()
方法中取消注册,而不需要对行为良好的程序进行任何事后清理
因此,对于最新的Java版本,您应该只看到堆转储中实际使用的流的清理器。您正在泄漏文件输入和输出流。解决方案:不要。@user207421,不幸的是,你的回答看起来不像是对这三个问题的回答。