[9, 8u40] RFR (M): 8057020: LambdaForm caches should support eviction
Peter Levart
peter.levart at gmail.com
Mon Dec 8 21:29:20 UTC 2014
On 12/08/2014 09:09 PM, Vladimir Ivanov wrote:
> Peter,
>
>> Just one more thought...
>>
>> In lines:
>>
>> 358 } else if (stale < 0 && k.get() == null) {
>> 359 stale = i; // remember 1st stale entry
>> index
>> 360 }
>>
>>
>> ...an index to 1st stale entry is remembered while scanning the array so
>> that at the end, instead of always allocating new slot, an existing slot
>> can be re-used. This has an inadvertent effect on the lifetime of
>> SoftReference(s) that are "touched" in the process (LRU policy of
>> cleaning). Here we see that an additional method like
>> SoftReference.isPresent() that didn't "touch" the access time, would be
>> helpful.
> Good point!
>
> However, I don't think that cache eviction policy matters for
> LabmdaForms. On average, there are very limited number of LambdaForm
> (up to 8k on Octane/Nashorn in single VM mode). So, in normal
> situation there should be no need to clear the caches. It's there
> mostly for correctness - avoid OOME in some corner cases (e.g. random
> enumeration of method handles).
Yes, there's nothing wrong. It's just that some LambdaForms can get
longer lifetime than they deserve (according to strict LRU policy), but
the unreferenced ones should all be released before OOME is thrown anyway.
Regards, Peter
>
> Best regards,
> Vladimir Ivanov
>
>>
>> Regards, Peter
>>
>> On 12/06/2014 01:30 PM, Peter Levart wrote:
>>> (Sorry for a re-send, forgot to include core-libs-dev)...
>>>
>>> Hi Vladimir,
>>>
>>> First, just a nit. I think that in LambdaFormEditor:
>>>
>>> 289 private LambdaForm putInCache(Transform key, LambdaForm
>>> form) {
>>> 290 key = key.withResult(form);
>>> 291 for (int pass = 0; ; pass++) {
>>> 292 Object c = lambdaForm.transformCache;
>>> 293 if (c instanceof ConcurrentHashMap) {
>>> 294 @SuppressWarnings("unchecked")
>>> 295 ConcurrentHashMap<Transform,Transform> m =
>>> (ConcurrentHashMap<Transform,Transform>) c;
>>> 296 Transform k = m.putIfAbsent(key, key);
>>> 297 if (k == null) return form;
>>> 298 LambdaForm result = k.get();
>>> 299 if (result != null) {
>>> 300 return result;
>>> 301 } else {
>>> 302 if (m.replace(key, k, key)) {
>>> 303 return form;
>>> 304 } else {
>>> 305 continue;
>>> 306 }
>>> 307 }
>>> 308 }
>>> 309 assert(pass == 0);
>>> 310 synchronized (lambdaForm) {
>>> 311 c = lambdaForm.transformCache;
>>> 312 if (c instanceof ConcurrentHashMap)
>>> 313 continue;
>>>
>>> ...
>>>
>>> 372 lambdaForm.transformCache = c = m;
>>> ^^^ put
>>> assignment to 'c' back in
>>> 373 // The second iteration will update for this
>>> query, concurrently.
>>> 374 continue;
>>>
>>>
>>> ...you could move the assignment to 'c' in line 292 out of for loop
>>> and put it back in line 372, since once 'c' is instance of CHM,
>>> lambdaForm.transformCache never changes again and if 'c' is not CHM
>>> yet, it is re-assigned in lines 311 and 372 before next loop.
>>>
>>> Am I right?
>>>
>>> Now what scares me (might be that I don't have an intimacy with
>>> LambdaForm class like you do). There is a situation where you publish
>>> LambdaForm instances via data race.
>>>
>>> One form of LambdaForm.transformCache is an array of Transform objects
>>> (the other two forms are not problematic). Transform class has all
>>> fields final except the 'referent' field of SoftReference, which holds
>>> a LambdaForm instance. In the following line:
>>>
>>> 377 ta[idx] = key;
>>>
>>>
>>> ...you publish Transform object to an element of array with relaxed
>>> write, and in the following lines:
>>>
>>> 271 } else {
>>> 272 Transform[] ta = (Transform[])c;
>>> 273 for (int i = 0; i < ta.length; i++) {
>>> 274 Transform t = ta[i];
>>> 275 if (t == null) break;
>>> 276 if (t.equals(key)) { k = t; break; }
>>> 277 }
>>> 278 }
>>> 279 assert(k == null || key.equals(k));
>>> 280 return (k != null) ? k.get() : null;
>>>
>>>
>>> ...you obtain the element of the array with no synchronization and a
>>> relaxed read and might return a non-null referent (the LambdaForm)
>>> which is then returned as an interned instance.
>>>
>>> So can LambdaForm instances be published via data races without fear
>>> that they would appear half-initialized?
>>>
>>> That's what I didn't know when I used a lazySet coupled with volatile
>>> get to access array elements in my version:
>>>
>>> http://cr.openjdk.java.net/~plevart/misc/LambdaFormEditor.WeakCache/webrev.01/
>>>
>>>
>>>
>>>
>>> Regards, Peter
>>>
>>> On 12/03/2014 12:45 PM, Vladimir Ivanov wrote:
>>>> Aleksey, thanks for the review.
>>>>
>>>> I haven't tried -XX:SoftRefLRUPolicyMSPerMB=0, but I did extensive
>>>> testing on Octane/Nashorn with multiple low -Xmx levels + frequent
>>>> Full GCs (8060147 [1] was the result of those experiments) and stress
>>>> tested cache eviction with jdk/java/lang/invoke/LFCache tests in long
>>>> running mode.
>>>>
>>>> Best regards,
>>>> Vladimir Ivanov
>>>>
>>>> [1] https://bugs.openjdk.java.net/browse/JDK-8060147
>>>>
>>>> On 12/3/14, 3:11 PM, Aleksey Shipilev wrote:
>>>>> On 12/01/2014 07:58 PM, Vladimir Ivanov wrote:
>>>>>> http://cr.openjdk.java.net/~vlivanov/8057020/webrev.00/
>>>>>> https://bugs.openjdk.java.net/browse/JDK-8057020
>>>>>
>>>>> Looks okay, although the cache management logic gives me a headache
>>>>> after the vacation. I thought I spotted a few bugs, but those were
>>>>> only
>>>>> false positives.
>>>>>
>>>>>> The fix is to use SoftReferences to keep LambdaForms alive as
>>>>>> long as
>>>>>> possible, but avoid throwing OOME until the caches are evicted. I
>>>>>> experimented with WeakReferences, but it doesn't hold LambdaForms
>>>>>> for
>>>>>> long enough: LambdaForm cache hit rate degrades significantly and it
>>>>>> negatively affects application startup and warmup, since every
>>>>>> instantiated LambdaForm is precompiled to bytecode before usage.
>>>>>>
>>>>>> Testing: jdk/java/lang/invoke/LFCache in stress mode + jck
>>>>>> (api/java_lang/invoke), jdk/java/lang/invoke,
>>>>>> jdk/java/util/streams, octane
>>>>>
>>>>> SoftReferences are tricky in the way they can get suddenly drop the
>>>>> referent, and normal testing would not catch it (e.g. the normal
>>>>> operation would reclaim softrefs under your feet almost never). Does
>>>>> this code survive with -XX:SoftRefLRUPolicyMSPerMB=0?
>>>>>
>>>>> Thanks,
>>>>> -Aleksey.
>>>>>
>>>>>
>>>> _______________________________________________
>>>> mlvm-dev mailing list
>>>> mlvm-dev at openjdk.java.net
>>>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>>>
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/mlvm-dev/attachments/20141208/491c4ea9/attachment-0001.html>
More information about the mlvm-dev
mailing list