RFR (S) JDK-8008962: NPG: Memory regression: One extra Monitor per ConstantPool
Coleen Phillimore
coleen.phillimore at oracle.com
Sun Mar 17 10:48:09 PDT 2013
On 3/17/2013 1:43 PM, Ioi Lam wrote:
> I have a kind of related questions: if locking of an oop is contended,
> do we allocate a Monitor object for this oop? Will the Monitor be
> eventually freed even if the oop is sill alive?
I don't know if the object monitor is deflated, or when.
>
> The old code in constantPool.cpp allocated a Monitor, not a Mutex:
>
> set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock"));
Yes, I think it should have been a Mutex* because there's no
wait/notify. They're both the the same size.
Coleen
>
> Thanks
> - Ioi
>
> On 03/17/2013 10:33 AM, Coleen Phillimore wrote:
>>
>> Hi David,
>>
>> You reviewed the init_lock change. I'm not sure if you reviewed the
>> Mutex* that we added for the constant pool change as well. Both added
>> footprint relative to permgen. You were pretty dubious about
>> zeroing out the init_lock too iirc. More below:
>>
>> On 3/17/2013 3:43 AM, David Holmes wrote:
>>> On 17/03/2013 2:04 PM, Ioi Lam wrote:
>>>> On 03/16/2013 04:37 PM, David Holmes wrote:
>>>>> Hi Ioi,
>>>>>
>>>>> On 16/03/2013 5:07 PM, Ioi Lam wrote:
>>>>>> Please review:
>>>>>> http://cr.openjdk.java.net/~iklam/8008962/constpool_lock_001/
>>>>>> <http://cr.openjdk.java.net/%7Eiklam/8008962/constpool_lock_001/>
>>>>>>
>>>>>> Bug: 8008962: NPG: Memory regression: One extra Monitor per
>>>>>> ConstantPool
>>>>>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8008962
>>>>>> https://jbs.oracle.com/bugs/browse/JDK-8008962 (Oracle Internal)
>>>>>>
>>>>>> Summary of fix:
>>>>>>
>>>>>> The fix is implemented according to Coleen's suggestion in the bug's
>>>>>> comments.
>>>>>>
>>>>>> Instead of using a Monitor for each ConstantPool (~150 bytes per
>>>>>> class), I re-use InstanceKlass::init_lock() for locking the
>>>>>> ConstantPool.
>>>>>>
>>>>>> init_lock used to be cleared after the class had been initialized.
>>>>>> There was complicated code that needed to deal with the clearing
>>>>>> (using volatile, OrderAccess::storestore(), etc). This code is no
>>>>>> longer necessary and has been removed.
>>>>>
>>>>> But the whole point of removing it was that we did not want the extra
>>>>> memory usage to exist for the lifetime of the class! This will
>>>>> increase dynamic footprint in a potentially unacceptable way.
>>>>>
>>>> We need a lock for the lifetime of the ConstantPool (see below), so
>>>> I am
>>>> just re-using the init_lock and make it live forever. If I remove the
>>>> init_lock then I have to create another lock object.
>>>
>>> I understand, but the need for a new lock object is problematic.
>>> With the permgen removal the klass stopped being an oop and a new
>>> object had to be introduced just for class initialization locking.
>>> That was bad for dynamic footprint but we compromised by making the
>>> lock object short-lived. Then we needed a lock-object for the CP.
>>> This originally introduced a new lock object and that caused this
>>> footprint issue. So you are proposing to re-use the init-lock, but
>>> given that was short-lived and will now be long lived, you have not
>>> made any net gain in footprint in the long term.
>>
>> Relative to when we had permgen, this isn't a net gain, but the
>> init_lock is much smaller than the Mutex* that we have in the
>> Constant Pool. If uncontended the init_lock is only 3 words, the
>> mutex is something like 156 bytes. So we have made net gain relative
>> to the current sources.
>>
>>>
>>>>>> The overhead is reduced to one int[0] per class.
>>>>>>
>>>>>> Because the lock is used at two different levels (class
>>>>>> initialization, and ConstantPool access), I thought about the
>>>>>> possibility of deadlocks.
>>>>>
>>>>> Top understand the deadlock potential I need to understand what locks
>>>>> the constant pool and why/when.
>>>>
>>>> There are various places such as ConstantPool::klass_at_impl that need
>>>> to make atomic modifications of an CP entry and its corresponding tag.
>>>> These can be called well after the class has finished initialization.
>>>
>>> The question is more, can they be called before or during class
>>> initialization?
>>>
>>
>> A good test would be to call class.forName in the <clinit>
>> function. This lock can be taken during initialization but if it's
>> a reentrant lock, it should be okay. I'm sure at least hundreds of
>> tests already do this, but verifying this with a specific test and
>> making sure would be a good thing to do.
>>
>> Coleen
>>>>> Is there a possibility of a self-deadlock if during class
>>>>> initialization we have to lock the constant-pool ourselves?
>>>> The locking is done using ObjectLocker with an oop, so it is self
>>>> reentrant, just like a regular Java monitor entry. Unlike mutexes,
>>>> there
>>>> won't be self deadlocks.
>>>
>>> Okay. But recursive locking can also be problematic if you don't
>>> fully understand the circumstances under which it can occur -
>>> because you effectively lose atomicity relative to actions in the
>>> current thread.
>>>
>>> David
>>> -----
>>>
>>>> Thanks
>>>> - Ioi
>>>>>
>>>>> Thanks,
>>>>> David
>>>>> ------
>>>>>
>>>>>
>>>>>> However,
>>>>>> it seems the only case of a deadlock would be some sort of
>>>>>> circular
>>>>>> dependency
>>>>>> in running <clinit> (as in the following example). However,
>>>>>> in this
>>>>>> case, a
>>>>>> deadlock would have already happened, just from grabbing the
>>>>>> initialization
>>>>>> locks, before we would even have a chance to access the
>>>>>> ConstantPools. Hence,
>>>>>> reusing the lock for ConstantPool access doesn't add any new
>>>>>> paths
>>>>>> for deadlocks.
>>>>>>
>>>>>> -------------------------------
>>>>>> public class ConcurClinit {
>>>>>> static void sleep(int ms) {
>>>>>> try {Thread.sleep(ms);} catch (Throwable t) {;}
>>>>>> }
>>>>>> static class A {
>>>>>> static {
>>>>>> sleep(1000);
>>>>>> B.xxx();
>>>>>> sleep(1000);
>>>>>> }
>>>>>> static void xxx() {}
>>>>>> }
>>>>>> static class B {
>>>>>> static {
>>>>>> sleep(1000);
>>>>>> A.xxx();
>>>>>> sleep(1000);
>>>>>> }
>>>>>> static void xxx() {}
>>>>>> }
>>>>>> public static void main(String args[]) {
>>>>>> (new Thread() {
>>>>>> public void run() {
>>>>>> A a = new A();
>>>>>> }
>>>>>> }).start();
>>>>>> (new Thread() {
>>>>>> public void run() {
>>>>>> B b = new B();
>>>>>> }
>>>>>> }).start();
>>>>>> }
>>>>>> }
>>>>>> -------------------------------
>>>>>>
>>>>>> Tests executed:
>>>>>>
>>>>>> * JPRT
>>>>>> * JCK (vm, lang, api/signaturetest)
>>>>>> * UTE (vm.runtime.testlist, vm.quick.testlist,
>>>>>> vm.parallel_class_loading.testlist
>>>>>> )
>>>>>>
>>>>>> Thanks,
>>>>>> Ioi
>>>>
>>
>
More information about the hotspot-runtime-dev
mailing list