【问题标题】:Memory leak of java.util.ref.Finalizer while Finalizer thread is waiting终结器线程等待时 java.util.ref.Finalizer 的内存泄漏
【发布时间】:2019-07-16 07:07:46
【问题描述】:

分析堆转储我寻找 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 
  1. 为什么 FileInputStream 和 FileOutputStream 总是在 ReferenceQueue 的尾部?
  2. 它们不是被垃圾收集器收集的吗,因为我观察到的只是分配失败 GC 而不是完全 GC?
  3. 为什么描述符对它们来说总是 0 和 1?

【问题讨论】:

  • 您正在泄漏文件输入和输出流。解决方法:不要。
  • @user207421,很遗憾,您的回复看起来不像是对这 3 个问题的回答。

标签: java memory garbage-collection jvm finalize


【解决方案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.inSystem.out 封装的文件流,当然,它们永远不会被垃圾收集,通常您也不会在它们上调用close()

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

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

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

【讨论】:

    猜你喜欢
    • 2018-09-11
    • 2014-10-29
    • 2013-12-18
    • 1970-01-01
    • 2017-02-04
    • 2016-12-09
    • 2020-08-08
    • 2012-02-17
    • 2014-05-16
    相关资源
    最近更新 更多