为了账号安全,请及时绑定邮箱和手机立即绑定

终结器线程等待时 java.util.ref.Finalizer 的内存泄漏

终结器线程等待时 java.util.ref.Finalizer 的内存泄漏

幕布斯7119047 2022-06-30 18:03:21
分析堆转储我寻找 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                                                 为什么 FileInputStream 和 FileOutputStream 总是在 ReferenceQueue 的尾部?它们不是被垃圾收集器收集的,因为我只观察到分配失败 GC 而不是完全 GC 发生吗?为什么描述符对他们来说总是 0 和 1?
查看完整描述

1 回答

?
倚天杖

TA贡献1828条经验 获得超3个赞

也许下面的测试程序会对此有所了解:


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.inand封装的文件流System.out,当然,它们永远不会被垃圾收集,通常您也不会调用close()它们。


终结不支持任何类型的选择退出,因此任何具有“非平凡finalize()方法”的类实例都将在构造时获得终结器引用,即使创建者知道对象永远不会被终结。


最新的 JDK 版本Cleaner为此使用 a ,这允许在使用现有的FileInputStream或构造时不注册清洁器,标准输入和标准输出就是这种情况。它还允许立即清理并因此在该方法中注销,不需要对表现良好的程序进行任何事后清理。FileOutputStreamFileDescriptorclose()


因此,对于最新的 Java 版本,您应该只看到堆转储中实际使用的流的清理程序。


查看完整回答
反对 回复 2022-06-30
  • 1 回答
  • 0 关注
  • 168 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信