[9, 8u40] RFR (M): 8057020: LambdaForm caches should support eviction
Vladimir Ivanov
vladimir.x.ivanov at oracle.com
Mon Dec 8 20:09:08 UTC 2014
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).
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
>>
>>
>
More information about the mlvm-dev
mailing list