Finalizer 线程等待时 java.util.ref.Finalizer 的内存泄漏
Memory leak of java.util.ref.Finalizer while Finalizer thread is waiting
分析堆转储我寻找 java.lang.ref.Finalizer class 的实例。 java.lang.ref.Finalizer有'next'和'prev'成员字段,用于维护链表。我总是将 FileInputStream 作为列表的尾部,将 FileOutputStream 作为它的前一个条目(分析了几个堆转储)。 FileInputStream 和 FileOutputStream 的文件描述符分别始终为 0 和 1:
+---[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
- 为什么FileInputStream和FileOutputStream总是在ReferenceQueue的尾部?
- 它们不是由垃圾收集器收集的,因为我只观察到分配失败 GC 而不是 Full GC 发生吗?
- 为什么描述符总是 0 和 1?
也许下面的测试程序会对此有所启发:
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
stdout: 1
stderr: 2
Ideone,请注意,对于 JDK 8,这仅适用于类 Unix 系统
换句话说,您正在查看由 System.in
和 System.out
封装的文件流,当然,这些文件流永远不会被垃圾收集,通常您也不会调用close()
在他们身上。
终结器不支持任何类型的选择退出,因此任何具有“非平凡 finalize()
方法”的 class 实例都将在构造时获得终结器引用,即使创建者知道该对象永远不会最终确定。
最新的 JDK 版本为此目的使用了 Cleaner
,当使用现有的 FileInputStream
或 FileOutputStream
构造时,允许不注册清洁器FileDescriptor
,stdin 和 stdout 就是这种情况。它还允许立即清理并因此在 close()
方法中注销,不需要任何 post- 运行良好的程序的 mortem 清理。
因此,对于最新的 Java 版本,您应该只会看到堆转储中实际使用的流清理器。
分析堆转储我寻找 java.lang.ref.Finalizer class 的实例。 java.lang.ref.Finalizer有'next'和'prev'成员字段,用于维护链表。我总是将 FileInputStream 作为列表的尾部,将 FileOutputStream 作为它的前一个条目(分析了几个堆转储)。 FileInputStream 和 FileOutputStream 的文件描述符分别始终为 0 和 1:
+---[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
- 为什么FileInputStream和FileOutputStream总是在ReferenceQueue的尾部?
- 它们不是由垃圾收集器收集的,因为我只观察到分配失败 GC 而不是 Full GC 发生吗?
- 为什么描述符总是 0 和 1?
也许下面的测试程序会对此有所启发:
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
stdout: 1
stderr: 2
Ideone,请注意,对于 JDK 8,这仅适用于类 Unix 系统
换句话说,您正在查看由 System.in
和 System.out
封装的文件流,当然,这些文件流永远不会被垃圾收集,通常您也不会调用close()
在他们身上。
终结器不支持任何类型的选择退出,因此任何具有“非平凡 finalize()
方法”的 class 实例都将在构造时获得终结器引用,即使创建者知道该对象永远不会最终确定。
最新的 JDK 版本为此目的使用了 Cleaner
,当使用现有的 FileInputStream
或 FileOutputStream
构造时,允许不注册清洁器FileDescriptor
,stdin 和 stdout 就是这种情况。它还允许立即清理并因此在 close()
方法中注销,不需要任何 post- 运行良好的程序的 mortem 清理。
因此,对于最新的 Java 版本,您应该只会看到堆转储中实际使用的流清理器。