当Finalizer线程正在等待时,java.util.ref.Finalizer的内存泄漏

问题描述 投票:0回答:1

分析堆转储我查找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?
java memory garbage-collection jvm finalize
1个回答
0
投票

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

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用于此目的,当使用现有的FileInputStream构造FileOutputStreamFileDescriptor时,不允许注册清理器,这是stdin和stdout的情况。它还允许立即清洁并因此在close()方法中取消注册,不需要对表现良好的程序进行任何事后清理。

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

© www.soinside.com 2019 - 2024. All rights reserved.