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

分析堆转储我寻找 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                   

                              

  1. 为什么 FileInputStream 和 FileOutputStream 总是在 ReferenceQueue 的尾部?

  2. 它们不是被垃圾收集器收集的,因为我只观察到分配失败 GC 而不是完全 GC 发生吗?

  3. 为什么描述符对他们来说总是 0 和 1?


幕布斯7119047
浏览 170回答 1
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:  0stdout: 1stderr: 2Ideone,请注意,对于 JDK 8,这仅适用于类 Unix 系统换句话说,您正在查看由System.inand封装的文件流System.out,当然,它们永远不会被垃圾收集,通常您也不会调用close()它们。终结不支持任何类型的选择退出,因此任何具有“非平凡finalize()方法”的类实例都将在构造时获得终结器引用,即使创建者知道对象永远不会被终结。最新的 JDK 版本Cleaner为此使用 a ,这允许在使用现有的FileInputStream或构造时不注册清洁器,标准输入和标准输出就是这种情况。它还允许立即清理并因此在该方法中注销,不需要对表现良好的程序进行任何事后清理。FileOutputStreamFileDescriptorclose()因此,对于最新的 Java 版本,您应该只看到堆转储中实际使用的流的清理程序。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java