分析堆转储我查找java.lang.ref.Finalizer类的实例。 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
也许下面的测试程序会对它有所了解:
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()
方法”的类的任何实例将在构造时获得终结器引用,即使创建者知道该对象永远不会被最终确定。
最近的JDK版本使用Cleaner
用于此目的,当使用现有的FileInputStream
构造FileOutputStream
或FileDescriptor
时,不允许注册清理器,这是stdin和stdout的情况。它还允许立即清洁并因此在close()
方法中取消注册,不需要对表现良好的程序进行任何事后清理。
因此,对于最新的Java版本,您应该只看到堆转储中实际使用的流的清理程序。