-
Bug
-
Resolution: Fixed
-
P1
-
1.1.2
-
b01
-
x86
-
windows_nt
-
Not verified
Here is a problem that we've been experiencing and we've finally tracked down the source of
it. Unfortunately it (again) requires a fairly complicated testcase and doesn't always
occur - you'll see why when we explain why it appears to be occurring.
If you need us to provide the testcase for this (after you've read the logical explanation)
then please feel free to contact us.
The short of it is that it looks like you'll be needing to modify WComponentPeer.java and
WMenuItemPeer.java so that their finalize methods don't deal with the Hashtable (since
they should have been disposed elsewhere) - we haven't tested this yet...
Short Problem Description:
--------------------------
The Windows AWT implementation, and potentially others based upon
SunToolkit.java, have a high potential to become deadlocked with
the Finalizer thread once it becomes active.
This occurs because:
(1) the AWT Peers implement finalize methods that make calls to
[potentially locked] syncronized [shared] objects
(2) the Finalizer maintains an internal Finalizer lock (_finalmeq_lock)
to stop certain portions of the finalizer running concurrently AND
(3) the potentially locked object makes allocation calls against the
heap while locked. (as Hashtable does) AND
(4) due to a memory allocation failure in (3) the finalizer is run
synchronously.
If the Finalizer attempts to finalize an AWT Peer object, whilst another
[AWT Peer] is trying to be allocated then a deadlock can occur.
Scope of problem:
-----------------
It's possible that any application that makes extensive use of the AWT
could run into this problem.
The likelyhood of hitting this problem increases as the application
becomes more dynamic in creating and destroying it's AWT Components.
In short, it affects the scalability of AWT Applications, since it only
starts to occur once the finalizer starts to get involved - which is
mostly during high Application stress periods.
This occurs more often that you'd think...
Problem Details:
----------------
The class sun.awt.SunToolkit maintains a single Hashtable (peerMap) of
currently active component/peer objects. Whenever a peer is created,
through one of the createXXXX methods, a Component/Peer mapping entry
is added to the Hashtable. When a peer is disposed, it must remove it's
mapping entry from the Hashtable.
Subclasses of sun.awt.SunToolkit, such as sun.awt.windows.WToolkit,
implement various createXXXX methods to create assorted peers, each
of which is added to the single Hashtable (peerMap).
This code typically looks like this:
public MenuItemPeer createMenuItem(MenuItem target)
{
MenuItemPeer = new WMenuItemPeer(target);
peerMap.put(target, peer);
return peer;
}
In turn, each of these peer objects must "report" that it is going away,
by calling the method targetDisposedPeer (made public in WToolkit) so that
it's mapping entry can be removed from the Hashtable.
protected void finalize() throws Throwable
{
dispose();
super.finalize();
}
public void dispose()
{
WToolkit.targetDisposedPeer(target, this);
_dispose();
}
This code is typically called when the peer object is dispose()'d. In the
case of the Windows Toolkit, this call is implemented in:
sun.awt.windows.WComponentPeer AND
sun.awt.windows.WMenuItemPeer
(In our case it's MenuItems that cause the problem we are seeing)
When any Object is being finalized, by the Finalizer thread, it's
finalize method is invoked. The default implementation of the
AWT Peer finalize method is to call dispose. This in turn makes
[synchronized] calls to the [single] hashtable in WToolkit.
Now we have some general background, I'll illustrate one of the
situations under which the deadlock can occur - the stack traces
shown are from an real application where this problem is occuring
fairly frequently (many times per day).
Consider an Application where:
(1) Thread A creates a PopupMenu Item and attaches it to a component,
the call stack is as follows:
java.util.Hashtable.put
sun.awt.windows.WToolkit.createMenuItem
java.awt.MenuItem.addNotify
java.awt.Menu.addNotify
java.awt.Menu.addNotify
java.awt.PopupMenu.addNotify
java.awt.Component.add
oracle...
...<Thread A>...
(2) At pretty much the same time, the Finalizer decides to wake up and
do a little house cleaning. It locates an old AWT Component peer,
that's not referenced any more, and decides to finalize it. The call
stack is as follows:
java.util.Hashtable.get
sun.awt.windows.WToolkit.targetDisposedPeer
sun.awt.windows.WMenuItemPeer.dispose
sun.awt.windows.WMenuItemPeer.finalize
...<Finalizer Thread>...
At this time it blocks since Thread A (from 1 above) "owns" the lock
on the single Hashtable object (SunToolkit.peerMap).
(3) Thread A, in processing the Hashtable's put request, attempts to
allocate a new HashtableEntry. In doing this, Java internals
realize that there isn't enough memory/resources in the current
pool, for whatever reason, and forces an internal *in thread*
memory cleanup by executing part of the Finalizer code.
This occurs when the Hashtable does the following:
...
HashtableEntry e = new HashtableEntry();
...
(code extracted from the implementation of the Hashtable.put method):
It is at this point that the deadlock occurs, since Thread A now needs
the Finalizer internal lock object, and has the Hashtable lock, and
the Finalizer has the Finalizer internal lock, and needs the Hashtable lock.
In other words:
"Thread A" OWNS SunToolkit.peerMap lock, WANTS Finalizer lock.
"Finalizer" OWNS Finalizer lock, WANTS SunToolkit.peerMap lock.
Point to note:
--------------
Code in the finalize() method, or called as a direct result of it,
of *any* object should be extremely careful when making calls that
may result in Obtaining locks on other objects.
it. Unfortunately it (again) requires a fairly complicated testcase and doesn't always
occur - you'll see why when we explain why it appears to be occurring.
If you need us to provide the testcase for this (after you've read the logical explanation)
then please feel free to contact us.
The short of it is that it looks like you'll be needing to modify WComponentPeer.java and
WMenuItemPeer.java so that their finalize methods don't deal with the Hashtable (since
they should have been disposed elsewhere) - we haven't tested this yet...
Short Problem Description:
--------------------------
The Windows AWT implementation, and potentially others based upon
SunToolkit.java, have a high potential to become deadlocked with
the Finalizer thread once it becomes active.
This occurs because:
(1) the AWT Peers implement finalize methods that make calls to
[potentially locked] syncronized [shared] objects
(2) the Finalizer maintains an internal Finalizer lock (_finalmeq_lock)
to stop certain portions of the finalizer running concurrently AND
(3) the potentially locked object makes allocation calls against the
heap while locked. (as Hashtable does) AND
(4) due to a memory allocation failure in (3) the finalizer is run
synchronously.
If the Finalizer attempts to finalize an AWT Peer object, whilst another
[AWT Peer] is trying to be allocated then a deadlock can occur.
Scope of problem:
-----------------
It's possible that any application that makes extensive use of the AWT
could run into this problem.
The likelyhood of hitting this problem increases as the application
becomes more dynamic in creating and destroying it's AWT Components.
In short, it affects the scalability of AWT Applications, since it only
starts to occur once the finalizer starts to get involved - which is
mostly during high Application stress periods.
This occurs more often that you'd think...
Problem Details:
----------------
The class sun.awt.SunToolkit maintains a single Hashtable (peerMap) of
currently active component/peer objects. Whenever a peer is created,
through one of the createXXXX methods, a Component/Peer mapping entry
is added to the Hashtable. When a peer is disposed, it must remove it's
mapping entry from the Hashtable.
Subclasses of sun.awt.SunToolkit, such as sun.awt.windows.WToolkit,
implement various createXXXX methods to create assorted peers, each
of which is added to the single Hashtable (peerMap).
This code typically looks like this:
public MenuItemPeer createMenuItem(MenuItem target)
{
MenuItemPeer = new WMenuItemPeer(target);
peerMap.put(target, peer);
return peer;
}
In turn, each of these peer objects must "report" that it is going away,
by calling the method targetDisposedPeer (made public in WToolkit) so that
it's mapping entry can be removed from the Hashtable.
protected void finalize() throws Throwable
{
dispose();
super.finalize();
}
public void dispose()
{
WToolkit.targetDisposedPeer(target, this);
_dispose();
}
This code is typically called when the peer object is dispose()'d. In the
case of the Windows Toolkit, this call is implemented in:
sun.awt.windows.WComponentPeer AND
sun.awt.windows.WMenuItemPeer
(In our case it's MenuItems that cause the problem we are seeing)
When any Object is being finalized, by the Finalizer thread, it's
finalize method is invoked. The default implementation of the
AWT Peer finalize method is to call dispose. This in turn makes
[synchronized] calls to the [single] hashtable in WToolkit.
Now we have some general background, I'll illustrate one of the
situations under which the deadlock can occur - the stack traces
shown are from an real application where this problem is occuring
fairly frequently (many times per day).
Consider an Application where:
(1) Thread A creates a PopupMenu Item and attaches it to a component,
the call stack is as follows:
java.util.Hashtable.put
sun.awt.windows.WToolkit.createMenuItem
java.awt.MenuItem.addNotify
java.awt.Menu.addNotify
java.awt.Menu.addNotify
java.awt.PopupMenu.addNotify
java.awt.Component.add
oracle...
...<Thread A>...
(2) At pretty much the same time, the Finalizer decides to wake up and
do a little house cleaning. It locates an old AWT Component peer,
that's not referenced any more, and decides to finalize it. The call
stack is as follows:
java.util.Hashtable.get
sun.awt.windows.WToolkit.targetDisposedPeer
sun.awt.windows.WMenuItemPeer.dispose
sun.awt.windows.WMenuItemPeer.finalize
...<Finalizer Thread>...
At this time it blocks since Thread A (from 1 above) "owns" the lock
on the single Hashtable object (SunToolkit.peerMap).
(3) Thread A, in processing the Hashtable's put request, attempts to
allocate a new HashtableEntry. In doing this, Java internals
realize that there isn't enough memory/resources in the current
pool, for whatever reason, and forces an internal *in thread*
memory cleanup by executing part of the Finalizer code.
This occurs when the Hashtable does the following:
...
HashtableEntry e = new HashtableEntry();
...
(code extracted from the implementation of the Hashtable.put method):
It is at this point that the deadlock occurs, since Thread A now needs
the Finalizer internal lock object, and has the Hashtable lock, and
the Finalizer has the Finalizer internal lock, and needs the Hashtable lock.
In other words:
"Thread A" OWNS SunToolkit.peerMap lock, WANTS Finalizer lock.
"Finalizer" OWNS Finalizer lock, WANTS SunToolkit.peerMap lock.
Point to note:
--------------
Code in the finalize() method, or called as a direct result of it,
of *any* object should be extremely careful when making calls that
may result in Obtaining locks on other objects.