问题
Analysing a heap dump I look for instances of java.lang.ref.Finalizer class. java.lang.ref.Finalizer has 'next' and 'prev' member fields for maintaining linked list. I always get FileInputStream as a tail of the list and FileOutputStream as previous entry to it (analysed several heap dumps). File descriptors for FileInputStream and FileOutputStream are always 0 and 1 respectively:
+---[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
- Why always FileInputStream and FileOutputStream are at the tail of the ReferenceQueue?
- Aren't they collected by garbage collector because I observe only Allocation Failure GC not Full GC occuring?
- Why descriptors are always 0 and 1 for them?
回答1:
Maybe the following test program will shed some light on it:
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, Note that for JDK 8, this applies to Unix like systems only
In other words, you are looking at the file streams encapsulated by System.in
and System.out
and, of course, these will never get garbage-collected and usually, you also don’t call close()
on them.
The finalization does not support any kind of opt-out, so any instance of a class with a “non trivial finalize()
method” will get a finalizer reference upon construction, even when the creator knows that the object will never get finalized.
The most recent JDK versions use a Cleaner for this purpose, which allows not to register a cleaner when a FileInputStream
or FileOutputStream
is constructed using an existing FileDescriptor
, which is the case for stdin and stdout. It also allows immediate cleaning and hence deregistration in the close()
method, not needing any post-mortem cleanup for well behaved programs.
So with the newest Java versions, you should see only cleaners for streams actually in use in the heap dump.
来源:https://stackoverflow.com/questions/54822835/memory-leak-of-java-util-ref-finalizer-while-finalizer-thread-is-waiting