RFR: 8212989: Allow CompiledMethod ExceptionCache have unloaded klasses
Erik Österlund
erik.osterlund at oracle.com
Thu Nov 1 22:14:27 UTC 2018
Hi Vladimir,
Thanks for the quick reply.
On 2018-11-01 21:14, Vladimir Kozlov wrote:
> Hi Erik,
>
> It seems these changed are not based on latest sources. I don't see
> call to CodeCache::do_unloading in CodeCache::do_unloading() which is
> present in latest code:
>
> http://hg.openjdk.java.net/jdk/jdk/file/5ea020bcaa0d/src/hotspot/share/code/codeCache.cpp#l683
>
Yeah this code is taken from the context of the future. Here is the
exact same patch rebased against jdk-jdk:
http://cr.openjdk.java.net/~eosterlund/8212989/webrev.01/
And new webrev with your suggestions:
http://cr.openjdk.java.net/~eosterlund/8212989/webrev.02/
And incremental of that to the jdk-jdk rebase:
http://cr.openjdk.java.net/~eosterlund/8212989/webrev.01_02/
>
> May be make some methods private, like
> CompiledMethod::exception_cache_acquire().
Sure. Made the accessors protected, as some are used in nmethod too and
it seems fine for subclasses to access.
> It seems you remove the only case when release_set_exception_cache() is
> called.
That is true. Removed.
> I don't see where purge_exception_caches() is used.
It's only called by a future version of ZGC from its new concurrent
class unloading feature that is currently baking in the ZGC repository.
The release_exception_cache member function deletes entries
synchronously if cleanup is triggered in a safepoint (all GCs except
ZGC), and otherwise defers deletion to the purge_exception_caches()
method, that is expected to happen one global handshake operation after
the exception caches have been unlinked by cleanup.
> Next sentence seems incomplete:
>
> + // unlinked. That is also when the
> CodeCache::exception_cache_free_list()
Fixed.
> Why you use exception_cache_acquire() in clean_exception_cache() and
> simple exception_cache() in add_exception_cache_entry()?
The reason is that in the context of insertion, you are holding the lock
protecting against concurrent insertions. Therefore, entries you load
are either entries that were protected by the same lock (other
insertions), or entries that are as old or older than entries that have
died a whole safepoint operation earlier (due to prepending inserts),
which makes its memory state stable. Therefore, no acquire is required
when reading the head in the insertion path.
Thanks,
/Erik
> Thanks,
> Vladimir
>
> On 11/1/18 5:58 AM, Erik Österlund wrote:
>> Hi,
>>
>> The ExceptionCaches of compiled methods is traditionally pruned in
>> safepoints during class unloading. This allows the exception cache to
>> have lock-free reads, while performing inserts under a lock, and
>> pruning under a safepoint. With concurrent class unloading, the
>> exception caches need to be cleaned concurrently.
>>
>> To retain the lock-free reads that seem good to keep lock-free, a
>> lock-free cleaning mechanism was introduced. Only one thread per
>> compiled method cleans each exception cache, but that thread races
>> with both single writers (under the ExceptionCache_lock) and any
>> number of readers.
>>
>> The head of the exception cache list is concurrently cleaned by both
>> inserting threads and cleaning threads. This allows having an
>> invariant that no newly prepended entries ever produce next pointers
>> to dead exception caches, that are concurrently removed from the list.
>> As for the internal next pointers, they are only pruned by the one
>> concurrent cleaning thread. This happens concurrent to reads that
>> simply skip over dead entries as they have different Klass* pointers
>> to the one being searched for.
>>
>> The single concurrent cleanup thread does not delete things removed
>> from the list straight away. Instead, they are placed on a purge list
>> that is freed up after a subsequent global handshaking operation. That
>> allows ABA-free CAS instructions in the lock-free paths, and allows
>> safe concurrent reading of entries in the exception cache list.
>>
>> Note that we already incorrectly "relied on memory_order_consume" for
>> the head pointer. But it used a volatile load. Assuming volatile loads
>> retain memory_order_consume semantics is not allowed in HotSpot
>> runtime code, so I changed those loads to use acquire accordingly.
>>
>> Webrev:
>> http://cr.openjdk.java.net/~eosterlund/8212989/webrev.00/
>>
>> Bug:
>> https://bugs.openjdk.java.net/browse/JDK-8212989
>>
>> Thanks,
>> /Erik
More information about the hotspot-dev
mailing list