We have played with the SA tool in front of Jstack and Classdump, today to see a fun, FinalizerInfo
.
Object#finalize
Most of the names can be guessed, finalizerinfo is related to the implementation of the Finalize method of object,
Called by the garbage collector on a object when garbage collection determines that there is no more references to the O Bject.
And then I went through the official documents for a long time to find this section of the Troubleshooting guide for HotSpot VM has a description of the finalize-related implementation,
One other potential, source of outofmemoryerror arises with applications, make excessive use of finalizers. If A class has a Finalize method, then objects of the that type does not had their space reclaimed at garbage collection time. Instead, after garbage collection The objects is queued for finalization, which occurs at a later time. In the Sun implementation, finalizers is executed by a daemon thread that services the finalization queue. If The finalizer thread cannot keep up with the finalization queue, then the Java heap could fill up and outofmemoryerror would be thrown.
That is, the Finalize method needs to be executed before the object is recycled, and the execution of the Finalize method needs to be consumed by one thread at a queue. Let's verify this by blocking the Finalize method,
Private Static class Foo { @Override protected void Finalize()throwsThrowable {System.out.println ("finalize#"+ This);Super. Finalize (); System.in.read ();//This finalize method will get stuck} }Private Static class Bar { @Override protected void Finalize()throwsThrowable {System.out.println ("finalize#"+ This);Super. Finalize (); System.in.read ();//This finalize method will also get stuck} } Public Static void Main(string[] args)throwsexception{foo (); Bar (); System.GC (); Thread.Sleep ( -); System.in.read (); }Private StaticFooFoo() {return NewFoo (); }Private StaticBarBar() {return NewBar (); }
If the above is true, then Foo and bar as long as one of the Finalize method is executed, the other must not be executed, because a single queue, one is stuck so its subsequent inevitable will not be consumed.
It is true that the output is only one line finalize#[email protected]
, so foo must be waiting to be finalize. This is where Finalizerinfo comes in handy, with which we can see which objects in the VM are waiting to be finalize,
# java7 Sun.jvm.hotspot.tools.FinalizerInfo 5960Attaching to process ID5960, please wait ... Debugger attached successfully. Server compiler detected. JVM version is24.65-b04number of objects pending for finalization: -Count Class Description------------------------------------------------------- -Java. Util. zip. ZipFile$ZipFileInputStream OneJava. Util. zip. ZipFile$ZipFileInflaterInputStream4Java. IO. FileInputStream1Me. Kisimple. Just4fun. Main$Foo
Duly we see 1 me.kisimple.just4fun.Main$Foo
that the description in the document is validated.
Finalizerthread
Further, we can rushed into the finalizerinfo source to see,
/* * The implementation here has a dependency on the implementation of * java.lang.ref.Finalizer. If the Finalizer implementation changes it‘s * possible this method will require changes too. We looked into using * ObjectReader to deserialize the objects from the target VM but as * there aren‘t any public methods to traverse the queue it means using * reflection which will also tie us to the implementation. * * The assumption here is that Finalizer.queue is the ReferenceQueue * with the objects awaiting finalization. The ReferenceQueue queueLength * is the number of objects in the queue, and ‘head‘ is the head of the * queue. */
The note has already told us that the queue that holds the object waiting for Finalize is in Java.lang.ref.Finalizer.queue. Then go to see Finalizer source code, you can see the consumption of this queue thread, that is, Finalizer.finalizerthread thread,
Public void Run() {if(running)return;//Finalizer thread starts before System.initializesystemclass //is called. Wait until javalangaccess is available while(! Vm.isbooted ()) {//Delay until VM completes initialization Try{vm.awaitbooted (); }Catch(Interruptedexception x) {//Ignore and Continue} }FinalJavalangaccess JLA = Sharedsecrets.getjavalangaccess (); running =true; for(;;) {Try{Finalizer F = (Finalizer) queue.remove (); F.runfinalizer (JLA); }Catch(Interruptedexception x) {//Ignore and Continue} } }
Private void Runfinalizer(Javalangaccess JLA) {synchronized( This) {if(Hasbeenfinalized ())return; Remove (); }Try{Object Finalizee = This. get ();if(Finalizee! =NULL&&! (FinalizeeinstanceofJava.lang.Enum)) {/////////////////////////////////////////////// //finally by Javalangaccess to really execute object#finalize.Jla.invokefinalize (Finalizee);/ * Clear stack slot containing this variable, to decrease the chances of false retention with a cons Ervative GC * /Finalizee =NULL; } }Catch(Throwable x) { }Super. Clear (); }
There is another way that we can debug at the Finalizerthread break point, so that we can verify our ideas. Alright, let's get here today, ^_^.
Resources
- http://openjdk.java.net/jeps/132
- Http://docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/memleaks.html#gbyvh
HotSpot SA #3: Finalizerinfo