8214897: ZGC: Concurrent Class Unloading

Co-authored-by: Per Liden <per.liden@oracle.com>
Co-authored-by: Stefan Karlsson <stefan.karlsson@oracle.com>
Reviewed-by: pliden
This commit is contained in:
Erik Österlund 2018-12-11 11:08:39 +01:00
parent cc116b1259
commit 9897ff01e9
22 changed files with 909 additions and 158 deletions

View File

@ -74,15 +74,17 @@
// * 63-47 Fixed (17-bits, always zero)
//
const size_t ZPlatformPageSizeSmallShift = 21; // 2M
const size_t ZPlatformPageSizeSmallShift = 21; // 2M
const size_t ZPlatformAddressOffsetBits = 42; // 4TB
const size_t ZPlatformAddressOffsetBits = 42; // 4TB
const uintptr_t ZPlatformAddressMetadataShift = ZPlatformAddressOffsetBits;
const uintptr_t ZPlatformAddressMetadataShift = ZPlatformAddressOffsetBits;
const uintptr_t ZPlatformAddressSpaceStart = (uintptr_t)1 << ZPlatformAddressOffsetBits;
const uintptr_t ZPlatformAddressSpaceSize = ((uintptr_t)1 << ZPlatformAddressOffsetBits) * 4;
const uintptr_t ZPlatformAddressSpaceStart = (uintptr_t)1 << ZPlatformAddressOffsetBits;
const uintptr_t ZPlatformAddressSpaceSize = ((uintptr_t)1 << ZPlatformAddressOffsetBits) * 4;
const size_t ZPlatformCacheLineSize = 64;
const size_t ZPlatformNMethodDisarmedOffset = 4;
const size_t ZPlatformCacheLineSize = 64;
#endif // OS_CPU_LINUX_X86_ZGLOBALS_LINUX_X86_HPP

View File

@ -80,10 +80,6 @@ void ZArguments::initialize() {
FLAG_SET_DEFAULT(UseCompressedOops, false);
FLAG_SET_DEFAULT(UseCompressedClassPointers, false);
// ClassUnloading not (yet) supported
FLAG_SET_DEFAULT(ClassUnloading, false);
FLAG_SET_DEFAULT(ClassUnloadingWithConcurrentMark, false);
// Verification before startup and after exit not (yet) supported
FLAG_SET_DEFAULT(VerifyDuringStartup, false);
FLAG_SET_DEFAULT(VerifyBeforeExit, false);

View File

@ -81,6 +81,7 @@ public:
static void load_barrier_on_oop_fields(oop o);
static oop load_barrier_on_weak_oop_field_preloaded(volatile oop* p, oop o);
static oop load_barrier_on_phantom_oop_field_preloaded(volatile oop* p, oop o);
static void load_barrier_on_root_oop_field(oop* p);
// Weak load barrier
static oop weak_load_barrier_on_oop_field(volatile oop* p);
@ -99,6 +100,7 @@ public:
// Keep alive barrier
static void keep_alive_barrier_on_weak_oop_field(volatile oop* p);
static void keep_alive_barrier_on_phantom_oop_field(volatile oop* p);
static void keep_alive_barrier_on_phantom_root_oop_field(oop* p);
// Mark barrier
static void mark_barrier_on_oop_field(volatile oop* p, bool finalizable);

View File

@ -111,11 +111,12 @@ inline void ZBarrier::root_barrier(oop* p, oop o) {
const uintptr_t good_addr = slow_path(addr);
// Non-atomic healing helps speed up root scanning. This is safe to do
// since we are always healing roots in a safepoint, which means we are
// never racing with mutators modifying roots while we are healing them.
// It's also safe in case multiple GC threads try to heal the same root,
// since they would always heal the root in the same way and it does not
// matter in which order it happens.
// since we are always healing roots in a safepoint, or under a lock,
// which ensures we are never racing with mutators modifying roots while
// we are healing them. It's also safe in case multiple GC threads try
// to heal the same root if it is aligned, since they would always heal
// the root in the same way and it does not matter in which order it
// happens. For misaligned oops, there needs to be mutual exclusion.
*p = ZOop::to_oop(good_addr);
}
@ -188,6 +189,11 @@ inline oop ZBarrier::load_barrier_on_phantom_oop_field_preloaded(volatile oop* p
return load_barrier_on_oop_field_preloaded(p, o);
}
inline void ZBarrier::load_barrier_on_root_oop_field(oop* p) {
const oop o = *p;
root_barrier<is_good_or_null_fast_path, load_barrier_on_oop_slow_path>(p, o);
}
//
// Weak load barrier
//
@ -269,6 +275,13 @@ inline void ZBarrier::keep_alive_barrier_on_phantom_oop_field(volatile oop* p) {
barrier<is_good_or_null_fast_path, keep_alive_barrier_on_phantom_oop_slow_path>(p, o);
}
inline void ZBarrier::keep_alive_barrier_on_phantom_root_oop_field(oop* p) {
// This operation is only valid when resurrection is blocked.
assert(ZResurrection::is_blocked(), "Invalid phase");
const oop o = *p;
root_barrier<is_good_or_null_fast_path, keep_alive_barrier_on_phantom_oop_slow_path>(p, o);
}
//
// Mark barrier
//

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "gc/z/zBarrierSet.hpp"
#include "gc/z/zBarrierSetAssembler.hpp"
#include "gc/z/zBarrierSetNMethod.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zHeap.inline.hpp"
#include "gc/z/zThreadLocalData.hpp"
@ -39,11 +40,20 @@
class ZBarrierSetC1;
class ZBarrierSetC2;
static BarrierSetNMethod* make_barrier_set_nmethod() {
// NMethod barriers are only used when class unloading is enabled
if (!ClassUnloading) {
return NULL;
}
return new ZBarrierSetNMethod();
}
ZBarrierSet::ZBarrierSet() :
BarrierSet(make_barrier_set_assembler<ZBarrierSetAssembler>(),
make_barrier_set_c1<ZBarrierSetC1>(),
make_barrier_set_c2<ZBarrierSetC2>(),
NULL /* barrier_set_nmethod */,
make_barrier_set_nmethod(),
BarrierSet::FakeRtti(BarrierSet::ZBarrierSet)) {}
ZBarrierSetAssembler* ZBarrierSet::assembler() {

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "code/nmethod.hpp"
#include "gc/z/zBarrierSetNMethod.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zLock.inline.hpp"
#include "gc/z/zOopClosures.hpp"
#include "gc/z/zNMethodTable.hpp"
#include "gc/z/zThreadLocalData.hpp"
#include "logging/log.hpp"
bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
ZLocker<ZReentrantLock> locker(ZNMethodTable::lock_for_nmethod(nm));
log_trace(nmethod, barrier)("Entered critical zone for %p", nm);
if (!is_armed(nm)) {
// Some other thread got here first and healed the oops
// and disarmed the nmethod.
return true;
}
if (nm->is_unloading()) {
// We can end up calling nmethods that are unloading
// since we clear compiled ICs lazily. Returning false
// will re-resovle the call and update the compiled IC.
return false;
}
// Heal oops and disarm
ZNMethodOopClosure cl;
nm->oops_do(&cl);
nm->fix_oop_relocations();
OrderAccess::release();
disarm(nm);
return true;
}
int ZBarrierSetNMethod::disarmed_value() const {
// We override the default BarrierSetNMethod::disarmed_value() since
// this can be called by GC threads, which doesn't keep an up to date
// address_bad_mask.
const uintptr_t disarmed_addr = ((uintptr_t)&ZAddressBadMask) + ZNMethodDisarmedOffset;
return *((int*)disarmed_addr);
}
ByteSize ZBarrierSetNMethod::thread_disarmed_offset() const {
return ZThreadLocalData::nmethod_disarmed_offset();
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP
#define SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP
#include "gc/shared/barrierSetNMethod.hpp"
#include "memory/allocation.hpp"
class nmethod;
class ZBarrierSetNMethod : public BarrierSetNMethod {
protected:
virtual int disarmed_value() const;
virtual bool nmethod_entry_barrier(nmethod* nm);
public:
virtual ByteSize thread_disarmed_offset() const;
};
#endif // SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP

View File

@ -259,12 +259,10 @@ bool ZCollectedHeap::block_is_obj(const HeapWord* addr) const {
}
void ZCollectedHeap::register_nmethod(nmethod* nm) {
assert_locked_or_safepoint(CodeCache_lock);
ZNMethodTable::register_nmethod(nm);
}
void ZCollectedHeap::unregister_nmethod(nmethod* nm) {
assert_locked_or_safepoint(CodeCache_lock);
ZNMethodTable::unregister_nmethod(nm);
}

View File

@ -91,6 +91,9 @@ const uintptr_t ZAddressSpaceStart = ZPlatformAddressSpaceStart;
const uintptr_t ZAddressSpaceSize = ZPlatformAddressSpaceSize;
const uintptr_t ZAddressSpaceEnd = ZAddressSpaceStart + ZAddressSpaceSize;
// NMethod entry barrier
const size_t ZNMethodDisarmedOffset = ZPlatformNMethodDisarmedOffset;
// Cache line size
const size_t ZCacheLineSize = ZPlatformCacheLineSize;

View File

@ -69,6 +69,7 @@ ZHeap::ZHeap() :
_weak_roots_processor(&_workers),
_relocate(&_workers),
_relocation_set(),
_unload(&_workers),
_serviceability(heap_min_size(), heap_max_size()) {
// Install global heap instance
assert(_heap == NULL, "Already initialized");
@ -353,9 +354,6 @@ bool ZHeap::mark_end() {
// Enter mark completed phase
ZGlobalPhase = ZPhaseMarkCompleted;
// Resize metaspace
MetaspaceGC::compute_new_size();
// Update statistics
ZStatSample(ZSamplerHeapUsedAfterMark, used());
ZStatHeap::set_at_mark_end(capacity(), allocated(), used());
@ -366,6 +364,9 @@ bool ZHeap::mark_end() {
// Process weak roots
_weak_roots_processor.process_weak_roots();
// Prepare to unload unused classes and code
_unload.prepare();
return true;
}
@ -380,6 +381,9 @@ void ZHeap::process_non_strong_references() {
// Process concurrent weak roots
_weak_roots_processor.process_concurrent_weak_roots();
// Unload unused classes and code
_unload.unload();
// Unblock resurrection of weak/phantom references
ZResurrection::unblock();
@ -463,8 +467,8 @@ void ZHeap::reset_relocation_set() {
void ZHeap::relocate_start() {
assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
// Update statistics
ZStatSample(ZSamplerHeapUsedBeforeRelocation, used());
// Finish unloading of classes and code
_unload.finish();
// Flip address view
ZAddressMasks::flip_to_remapped();
@ -474,6 +478,7 @@ void ZHeap::relocate_start() {
ZGlobalPhase = ZPhaseRelocate;
// Update statistics
ZStatSample(ZSamplerHeapUsedBeforeRelocation, used());
ZStatHeap::set_at_relocate_start(capacity(), allocated(), used());
// Remap/Relocate roots

View File

@ -41,6 +41,7 @@
#include "gc/z/zRootsIterator.hpp"
#include "gc/z/zWeakRootsProcessor.hpp"
#include "gc/z/zServiceability.hpp"
#include "gc/z/zUnload.hpp"
#include "gc/z/zWorkers.hpp"
#include "memory/allocation.hpp"
@ -59,6 +60,7 @@ private:
ZWeakRootsProcessor _weak_roots_processor;
ZRelocate _relocate;
ZRelocationSet _relocation_set;
ZUnload _unload;
ZServiceability _serviceability;
size_t heap_min_size() const;

View File

@ -287,6 +287,14 @@ void ZMark::follow_partial_array(ZMarkStackEntry entry, bool finalizable) {
}
void ZMark::follow_array_object(objArrayOop obj, bool finalizable) {
if (finalizable) {
ZMarkBarrierOopClosure<true /* finalizable */> cl;
cl.do_klass(obj->klass());
} else {
ZMarkBarrierOopClosure<false /* finalizable */> cl;
cl.do_klass(obj->klass());
}
const uintptr_t addr = (uintptr_t)obj->base();
const size_t size = (size_t)obj->length() * oopSize;

View File

@ -23,45 +23,62 @@
#include "precompiled.hpp"
#include "code/relocInfo.hpp"
#include "code/nativeInst.hpp"
#include "code/nmethod.hpp"
#include "code/icBuffer.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetNMethod.hpp"
#include "gc/z/zArray.inline.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zHash.inline.hpp"
#include "gc/z/zLock.inline.hpp"
#include "gc/z/zNMethodTable.hpp"
#include "gc/z/zOopClosures.inline.hpp"
#include "gc/z/zTask.hpp"
#include "gc/z/zWorkers.hpp"
#include "logging/log.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/os.hpp"
#include "utilities/debug.hpp"
class ZNMethodWithImmediateOops {
class ZNMethodDataImmediateOops {
private:
nmethod* const _nm;
const size_t _nimmediate_oops;
const size_t _nimmediate_oops;
static size_t header_size();
ZNMethodWithImmediateOops(nmethod* nm, const GrowableArray<oop*>& immediate_oops);
ZNMethodDataImmediateOops(const GrowableArray<oop*>& immediate_oops);
public:
static ZNMethodWithImmediateOops* create(nmethod* nm, const GrowableArray<oop*>& immediate_oops);
static void destroy(ZNMethodWithImmediateOops* nmi);
static ZNMethodDataImmediateOops* create(const GrowableArray<oop*>& immediate_oops);
static void destroy(ZNMethodDataImmediateOops* data_immediate_oops);
nmethod* method() const;
size_t immediate_oops_count() const;
oop** immediate_oops_begin() const;
oop** immediate_oops_end() const;
};
size_t ZNMethodWithImmediateOops::header_size() {
const size_t size = sizeof(ZNMethodWithImmediateOops);
size_t ZNMethodDataImmediateOops::header_size() {
const size_t size = sizeof(ZNMethodDataImmediateOops);
assert(is_aligned(size, sizeof(oop*)), "Header misaligned");
return size;
}
ZNMethodWithImmediateOops::ZNMethodWithImmediateOops(nmethod* nm, const GrowableArray<oop*>& immediate_oops) :
_nm(nm),
ZNMethodDataImmediateOops* ZNMethodDataImmediateOops::create(const GrowableArray<oop*>& immediate_oops) {
// Allocate memory for the ZNMethodDataImmediateOops object
// plus the immediate oop* array that follows right after.
const size_t size = ZNMethodDataImmediateOops::header_size() + (sizeof(oop*) * immediate_oops.length());
void* const data_immediate_oops = NEW_C_HEAP_ARRAY(uint8_t, size, mtGC);
return ::new (data_immediate_oops) ZNMethodDataImmediateOops(immediate_oops);
}
void ZNMethodDataImmediateOops::destroy(ZNMethodDataImmediateOops* data_immediate_oops) {
ZNMethodTable::safe_delete(data_immediate_oops);
}
ZNMethodDataImmediateOops::ZNMethodDataImmediateOops(const GrowableArray<oop*>& immediate_oops) :
_nimmediate_oops(immediate_oops.length()) {
// Save all immediate oops
for (size_t i = 0; i < _nimmediate_oops; i++) {
@ -69,41 +86,97 @@ ZNMethodWithImmediateOops::ZNMethodWithImmediateOops(nmethod* nm, const Growable
}
}
ZNMethodWithImmediateOops* ZNMethodWithImmediateOops::create(nmethod* nm, const GrowableArray<oop*>& immediate_oops) {
// Allocate memory for the ZNMethodWithImmediateOops object
// plus the immediate oop* array that follows right after.
const size_t size = header_size() + (sizeof(oop*) * immediate_oops.length());
void* const method_with_immediate_oops = NEW_C_HEAP_ARRAY(uint8_t, size, mtGC);
return ::new (method_with_immediate_oops) ZNMethodWithImmediateOops(nm, immediate_oops);
}
void ZNMethodWithImmediateOops::destroy(ZNMethodWithImmediateOops* nmi) {
FREE_C_HEAP_ARRAY(uint8_t, nmi);
}
nmethod* ZNMethodWithImmediateOops::method() const {
return _nm;
}
size_t ZNMethodWithImmediateOops::immediate_oops_count() const {
size_t ZNMethodDataImmediateOops::immediate_oops_count() const {
return _nimmediate_oops;
}
oop** ZNMethodWithImmediateOops::immediate_oops_begin() const {
oop** ZNMethodDataImmediateOops::immediate_oops_begin() const {
// The immediate oop* array starts immediately after this object
return (oop**)((uintptr_t)this + header_size());
}
oop** ZNMethodWithImmediateOops::immediate_oops_end() const {
oop** ZNMethodDataImmediateOops::immediate_oops_end() const {
return immediate_oops_begin() + immediate_oops_count();
}
class ZNMethodData {
private:
ZReentrantLock _lock;
ZNMethodDataImmediateOops* volatile _immediate_oops;
ZNMethodData(nmethod* nm);
public:
static ZNMethodData* create(nmethod* nm);
static void destroy(ZNMethodData* data);
ZReentrantLock* lock();
ZNMethodDataImmediateOops* immediate_oops() const;
ZNMethodDataImmediateOops* swap_immediate_oops(const GrowableArray<oop*>& immediate_oops);
};
ZNMethodData* ZNMethodData::create(nmethod* nm) {
void* const method = NEW_C_HEAP_ARRAY(uint8_t, sizeof(ZNMethodData), mtGC);
return ::new (method) ZNMethodData(nm);
}
void ZNMethodData::destroy(ZNMethodData* data) {
ZNMethodDataImmediateOops::destroy(data->immediate_oops());
ZNMethodTable::safe_delete(data);
}
ZNMethodData::ZNMethodData(nmethod* nm) :
_lock(),
_immediate_oops(NULL) {}
ZReentrantLock* ZNMethodData::lock() {
return &_lock;
}
ZNMethodDataImmediateOops* ZNMethodData::immediate_oops() const {
return OrderAccess::load_acquire(&_immediate_oops);
}
ZNMethodDataImmediateOops* ZNMethodData::swap_immediate_oops(const GrowableArray<oop*>& immediate_oops) {
ZNMethodDataImmediateOops* const data_immediate_oops =
immediate_oops.is_empty() ? NULL : ZNMethodDataImmediateOops::create(immediate_oops);
return Atomic::xchg(data_immediate_oops, &_immediate_oops);
}
static ZNMethodData* gc_data(const nmethod* nm) {
return nm->gc_data<ZNMethodData>();
}
static void set_gc_data(nmethod* nm, ZNMethodData* data) {
return nm->set_gc_data<ZNMethodData>(data);
}
ZNMethodTableEntry* ZNMethodTable::_table = NULL;
size_t ZNMethodTable::_size = 0;
ZLock ZNMethodTable::_iter_lock;
ZNMethodTableEntry* ZNMethodTable::_iter_table = NULL;
size_t ZNMethodTable::_iter_table_size = 0;
ZArray<void*> ZNMethodTable::_iter_deferred_deletes;
size_t ZNMethodTable::_nregistered = 0;
size_t ZNMethodTable::_nunregistered = 0;
volatile size_t ZNMethodTable::_claimed = 0;
void ZNMethodTable::safe_delete(void* data) {
if (data == NULL) {
return;
}
ZLocker<ZLock> locker(&_iter_lock);
if (_iter_table != NULL) {
// Iteration in progress, defer delete
_iter_deferred_deletes.add(data);
} else {
// Iteration not in progress, delete now
FREE_C_HEAP_ARRAY(uint8_t, data);
}
}
ZNMethodTableEntry ZNMethodTable::create_entry(nmethod* nm) {
GrowableArray<oop*> immediate_oops;
bool non_immediate_oops = false;
@ -132,29 +205,27 @@ ZNMethodTableEntry ZNMethodTable::create_entry(nmethod* nm) {
}
}
// oops_count() returns the number of oops in the oop table plus one
if (immediate_oops.is_empty() && nm->oops_count() == 1) {
// No oops found, return empty entry
return ZNMethodTableEntry();
// Attach GC data to nmethod
ZNMethodData* data = gc_data(nm);
if (data == NULL) {
data = ZNMethodData::create(nm);
set_gc_data(nm, data);
}
if (immediate_oops.is_empty()) {
// No immediate oops found, return entry without immediate oops
return ZNMethodTableEntry(nm, non_immediate_oops);
}
// Attach immediate oops in GC data
ZNMethodDataImmediateOops* const old_data_immediate_oops = data->swap_immediate_oops(immediate_oops);
ZNMethodDataImmediateOops::destroy(old_data_immediate_oops);
// Return entry with immediate oops
return ZNMethodTableEntry(ZNMethodWithImmediateOops::create(nm, immediate_oops), non_immediate_oops);
// Create entry
return ZNMethodTableEntry(nm, non_immediate_oops, !immediate_oops.is_empty());
}
void ZNMethodTable::destroy_entry(ZNMethodTableEntry entry) {
if (entry.immediate_oops()) {
ZNMethodWithImmediateOops::destroy(entry.method_with_immediate_oops());
ZReentrantLock* ZNMethodTable::lock_for_nmethod(nmethod* nm) {
ZNMethodData* const data = gc_data(nm);
if (data == NULL) {
return NULL;
}
}
nmethod* ZNMethodTable::method(ZNMethodTableEntry entry) {
return entry.immediate_oops() ? entry.method_with_immediate_oops()->method() : entry.method();
return data->lock();
}
size_t ZNMethodTable::first_index(const nmethod* nm, size_t size) {
@ -171,7 +242,7 @@ size_t ZNMethodTable::next_index(size_t prev_index, size_t size) {
}
bool ZNMethodTable::register_entry(ZNMethodTableEntry* table, size_t size, ZNMethodTableEntry entry) {
const nmethod* const nm = method(entry);
const nmethod* const nm = entry.method();
size_t index = first_index(nm, size);
for (;;) {
@ -183,9 +254,8 @@ bool ZNMethodTable::register_entry(ZNMethodTableEntry* table, size_t size, ZNMet
return true;
}
if (table_entry.registered() && method(table_entry) == nm) {
if (table_entry.registered() && table_entry.method() == nm) {
// Replace existing entry
destroy_entry(table_entry);
table[index] = entry;
return false;
}
@ -194,7 +264,7 @@ bool ZNMethodTable::register_entry(ZNMethodTableEntry* table, size_t size, ZNMet
}
}
bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, const nmethod* nm) {
bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, nmethod* nm) {
if (size == 0) {
// Table is empty
return false;
@ -210,10 +280,13 @@ bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, con
return false;
}
if (table_entry.registered() && method(table_entry) == nm) {
if (table_entry.registered() && table_entry.method() == nm) {
// Remove entry
destroy_entry(table_entry);
table[index] = ZNMethodTableEntry(true /* unregistered */);
// Destroy GC data
ZNMethodData::destroy(gc_data(nm));
set_gc_data(nm, NULL);
return true;
}
@ -222,6 +295,7 @@ bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, con
}
void ZNMethodTable::rebuild(size_t new_size) {
ZLocker<ZLock> locker(&_iter_lock);
assert(is_power_of_2(new_size), "Invalid size");
log_debug(gc, nmethod)("Rebuilding NMethod Table: "
@ -243,8 +317,10 @@ void ZNMethodTable::rebuild(size_t new_size) {
}
}
// Delete old table
delete [] _table;
if (_iter_table != _table) {
// Delete old table
delete [] _table;
}
// Install new table
_table = new_table;
@ -294,8 +370,8 @@ void ZNMethodTable::log_register(const nmethod* nm, ZNMethodTableEntry entry) {
p2i(nm),
nm->compiler_name(),
nm->oops_count() - 1,
entry.immediate_oops() ? entry.method_with_immediate_oops()->immediate_oops_count() : 0,
BOOL_TO_STR(entry.non_immediate_oops()));
entry.immediate_oops() ? gc_data(nm)->immediate_oops()->immediate_oops_count() : 0,
entry.non_immediate_oops() ? "Yes" : "No");
LogTarget(Trace, gc, nmethod, oops) log_oops;
if (!log_oops.is_enabled()) {
@ -312,12 +388,14 @@ void ZNMethodTable::log_register(const nmethod* nm, ZNMethodTableEntry entry) {
if (entry.immediate_oops()) {
// Print nmethod immediate oops
const ZNMethodWithImmediateOops* const nmi = entry.method_with_immediate_oops();
oop** const begin = nmi->immediate_oops_begin();
oop** const end = nmi->immediate_oops_end();
for (oop** p = begin; p < end; p++) {
log_oops.print(" ImmediateOop[" SIZE_FORMAT "] " PTR_FORMAT " @ " PTR_FORMAT " (%s)",
(p - begin), p2i(**p), p2i(*p), (**p)->klass()->external_name());
const ZNMethodDataImmediateOops* const nmi = gc_data(nm)->immediate_oops();
if (nmi != NULL) {
oop** const begin = nmi->immediate_oops_begin();
oop** const end = nmi->immediate_oops_end();
for (oop** p = begin; p < end; p++) {
log_oops.print(" ImmediateOop[" SIZE_FORMAT "] " PTR_FORMAT " @ " PTR_FORMAT " (%s)",
(p - begin), p2i(**p), p2i(*p), (**p)->klass()->external_name());
}
}
}
}
@ -343,21 +421,17 @@ size_t ZNMethodTable::unregistered_nmethods() {
}
void ZNMethodTable::register_nmethod(nmethod* nm) {
assert(CodeCache_lock->owned_by_self(), "Lock must be held");
ResourceMark rm;
// Grow/Shrink/Prune table if needed
rebuild_if_needed();
// Create entry
const ZNMethodTableEntry entry = create_entry(nm);
log_register(nm, entry);
if (!entry.registered()) {
// Method doesn't have any oops, ignore it
return;
}
// Grow/Shrink/Prune table if needed
rebuild_if_needed();
// Insert new entry
if (register_entry(_table, _size, entry)) {
// New entry registered. When register_entry() instead returns
@ -365,11 +439,31 @@ void ZNMethodTable::register_nmethod(nmethod* nm) {
// to increase number of registered entries in that case.
_nregistered++;
}
// Disarm nmethod entry barrier
disarm_nmethod(nm);
}
void ZNMethodTable::sweeper_wait_for_iteration() {
// The sweeper must wait for any ongoing iteration to complete
// before it can unregister an nmethod.
if (!Thread::current()->is_Code_cache_sweeper_thread()) {
return;
}
assert(CodeCache_lock->owned_by_self(), "Lock must be held");
while (_iter_table != NULL) {
MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
os::naked_short_sleep(1);
}
}
void ZNMethodTable::unregister_nmethod(nmethod* nm) {
ResourceMark rm;
sweeper_wait_for_iteration();
log_unregister(nm);
// Remove entry
@ -383,20 +477,45 @@ void ZNMethodTable::unregister_nmethod(nmethod* nm) {
}
}
void ZNMethodTable::gc_prologue() {
_claimed = 0;
void ZNMethodTable::disarm_nmethod(nmethod* nm) {
BarrierSetNMethod* const bs = BarrierSet::barrier_set()->barrier_set_nmethod();
if (bs != NULL) {
bs->disarm(nm);
}
}
void ZNMethodTable::gc_epilogue() {
assert(_claimed >= _size, "Failed to claim all table entries");
void ZNMethodTable::nmethod_entries_do_begin() {
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
ZLocker<ZLock> locker(&_iter_lock);
// Prepare iteration
_iter_table = _table;
_iter_table_size = _size;
_claimed = 0;
assert(_iter_deferred_deletes.is_empty(), "Should be emtpy");
}
void ZNMethodTable::nmethod_entries_do_end() {
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
ZLocker<ZLock> locker(&_iter_lock);
// Finish iteration
if (_iter_table != _table) {
delete [] _iter_table;
}
_iter_table = NULL;
assert(_claimed >= _iter_table_size, "Failed to claim all table entries");
// Process deferred deletes
ZArrayIterator<void*> iter(&_iter_deferred_deletes);
for (void* data; iter.next(&data);) {
FREE_C_HEAP_ARRAY(uint8_t, data);
}
_iter_deferred_deletes.clear();
}
void ZNMethodTable::entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl) {
nmethod* const nm = method(entry);
if (!nm->is_alive()) {
// No need to visit oops
return;
}
nmethod* const nm = entry.method();
// Process oops table
oop* const begin = nm->oops_begin();
@ -407,29 +526,52 @@ void ZNMethodTable::entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl) {
}
}
// Process immediate oops
if (entry.immediate_oops()) {
// Process immediate oops
const ZNMethodWithImmediateOops* const nmi = entry.method_with_immediate_oops();
oop** const begin = nmi->immediate_oops_begin();
oop** const end = nmi->immediate_oops_end();
for (oop** p = begin; p < end; p++) {
cl->do_oop(*p);
const ZNMethodDataImmediateOops* const nmi = gc_data(nm)->immediate_oops();
if (nmi != NULL) {
oop** const begin = nmi->immediate_oops_begin();
oop** const end = nmi->immediate_oops_end();
for (oop** p = begin; p < end; p++) {
if (**p != Universe::non_oop_word()) {
cl->do_oop(*p);
}
}
}
}
// Process non-immediate oops
if (entry.non_immediate_oops()) {
// Process non-immediate oops
nmethod* const nm = entry.method();
nm->fix_oop_relocations();
}
}
class ZNMethodTableEntryToOopsDo : public ZNMethodTableEntryClosure {
private:
OopClosure* _cl;
public:
ZNMethodTableEntryToOopsDo(OopClosure* cl) :
_cl(cl) {}
void do_nmethod_entry(ZNMethodTableEntry entry) {
ZNMethodTable::entry_oops_do(entry, _cl);
}
};
void ZNMethodTable::oops_do(OopClosure* cl) {
ZNMethodTableEntryToOopsDo entry_cl(cl);
nmethod_entries_do(&entry_cl);
}
void ZNMethodTable::nmethod_entries_do(ZNMethodTableEntryClosure* cl) {
for (;;) {
// Claim table partition. Each partition is currently sized to span
// two cache lines. This number is just a guess, but seems to work well.
const size_t partition_size = (ZCacheLineSize * 2) / sizeof(ZNMethodTableEntry);
const size_t partition_start = MIN2(Atomic::add(partition_size, &_claimed) - partition_size, _size);
const size_t partition_end = MIN2(partition_start + partition_size, _size);
const size_t partition_start = MIN2(Atomic::add(partition_size, &_claimed) - partition_size, _iter_table_size);
const size_t partition_end = MIN2(partition_start + partition_size, _iter_table_size);
if (partition_start == partition_end) {
// End of table
break;
@ -437,10 +579,141 @@ void ZNMethodTable::oops_do(OopClosure* cl) {
// Process table partition
for (size_t i = partition_start; i < partition_end; i++) {
const ZNMethodTableEntry entry = _table[i];
const ZNMethodTableEntry entry = _iter_table[i];
if (entry.registered()) {
entry_oops_do(entry, cl);
cl->do_nmethod_entry(entry);
}
}
}
}
class ZNMethodTableUnlinkClosure : public ZNMethodTableEntryClosure {
private:
bool _unloading_occurred;
volatile bool _failed;
void set_failed() {
Atomic::store(true, &_failed);
}
public:
ZNMethodTableUnlinkClosure(bool unloading_occurred) :
_unloading_occurred(unloading_occurred),
_failed(false) {}
virtual void do_nmethod_entry(ZNMethodTableEntry entry) {
if (failed()) {
return;
}
nmethod* const nm = entry.method();
if (!nm->is_alive()) {
return;
}
if (nm->is_unloading()) {
// Unlinking of the dependencies must happen before the
// handshake separating unlink and purge.
nm->flush_dependencies(false /* delete_immediately */);
return;
}
ZLocker<ZReentrantLock> locker(ZNMethodTable::lock_for_nmethod(nm));
// Heal oops and disarm
ZNMethodOopClosure cl;
ZNMethodTable::entry_oops_do(entry, &cl);
ZNMethodTable::disarm_nmethod(nm);
// Clear compiled ICs and exception caches
if (!nm->unload_nmethod_caches(_unloading_occurred)) {
set_failed();
}
}
bool failed() const {
return Atomic::load(&_failed);
}
};
class ZNMethodTableUnlinkTask : public ZTask {
private:
ZNMethodTableUnlinkClosure _cl;
ICRefillVerifier* _verifier;
public:
ZNMethodTableUnlinkTask(bool unloading_occurred, ICRefillVerifier* verifier) :
ZTask("ZNMethodTableUnlinkTask"),
_cl(unloading_occurred),
_verifier(verifier) {
ZNMethodTable::nmethod_entries_do_begin();
}
~ZNMethodTableUnlinkTask() {
ZNMethodTable::nmethod_entries_do_end();
}
virtual void work() {
ICRefillVerifierMark mark(_verifier);
ZNMethodTable::nmethod_entries_do(&_cl);
}
bool success() const {
return !_cl.failed();
}
};
void ZNMethodTable::unlink(ZWorkers* workers, bool unloading_occurred) {
for (;;) {
ICRefillVerifier verifier;
{
ZNMethodTableUnlinkTask task(unloading_occurred, &verifier);
workers->run_concurrent(&task);
if (task.success()) {
return;
}
}
// Cleaning failed because we ran out of transitional IC stubs,
// so we have to refill and try again. Refilling requires taking
// a safepoint, so we temporarily leave the suspendible thread set.
SuspendibleThreadSetLeaver sts;
InlineCacheBuffer::refill_ic_stubs();
}
}
class ZNMethodTablePurgeClosure : public ZNMethodTableEntryClosure {
public:
virtual void do_nmethod_entry(ZNMethodTableEntry entry) {
nmethod* const nm = entry.method();
if (nm->is_alive() && nm->is_unloading()) {
nm->make_unloaded();
}
}
};
class ZNMethodTablePurgeTask : public ZTask {
private:
ZNMethodTablePurgeClosure _cl;
public:
ZNMethodTablePurgeTask() :
ZTask("ZNMethodTablePurgeTask"),
_cl() {
ZNMethodTable::nmethod_entries_do_begin();
}
~ZNMethodTablePurgeTask() {
ZNMethodTable::nmethod_entries_do_end();
}
virtual void work() {
ZNMethodTable::nmethod_entries_do(&_cl);
}
};
void ZNMethodTable::purge(ZWorkers* workers) {
ZNMethodTablePurgeTask task;
workers->run_concurrent(&task);
}

View File

@ -24,28 +24,40 @@
#ifndef SHARE_GC_Z_ZNMETHODTABLE_HPP
#define SHARE_GC_Z_ZNMETHODTABLE_HPP
#include "gc/z/zArray.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zLock.hpp"
#include "gc/z/zNMethodTableEntry.hpp"
#include "memory/allocation.hpp"
class ZWorkers;
class ZNMethodTableEntryClosure {
public:
virtual void do_nmethod_entry(ZNMethodTableEntry entry) = 0;
};
class ZNMethodTable : public AllStatic {
private:
static ZNMethodTableEntry* _table;
static size_t _size;
static ZLock _iter_lock;
static ZNMethodTableEntry* _iter_table;
static size_t _iter_table_size;
static ZArray<void*> _iter_deferred_deletes;
static size_t _nregistered;
static size_t _nunregistered;
static volatile size_t _claimed ATTRIBUTE_ALIGNED(ZCacheLineSize);
static ZNMethodTableEntry create_entry(nmethod* nm);
static void destroy_entry(ZNMethodTableEntry entry);
static nmethod* method(ZNMethodTableEntry entry);
static size_t first_index(const nmethod* nm, size_t size);
static size_t next_index(size_t prev_index, size_t size);
static void sweeper_wait_for_iteration();
static bool register_entry(ZNMethodTableEntry* table, size_t size, ZNMethodTableEntry entry);
static bool unregister_entry(ZNMethodTableEntry* table, size_t size, const nmethod* nm);
static bool unregister_entry(ZNMethodTableEntry* table, size_t size, nmethod* nm);
static void rebuild(size_t new_size);
static void rebuild_if_needed();
@ -53,19 +65,28 @@ private:
static void log_register(const nmethod* nm, ZNMethodTableEntry entry);
static void log_unregister(const nmethod* nm);
static void entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl);
public:
static void safe_delete(void* data);
static size_t registered_nmethods();
static size_t unregistered_nmethods();
static void register_nmethod(nmethod* nm);
static void unregister_nmethod(nmethod* nm);
static void disarm_nmethod(nmethod* nm);
static void gc_prologue();
static void gc_epilogue();
static ZReentrantLock* lock_for_nmethod(nmethod* nm);
static void oops_do(OopClosure* cl);
static void entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl);
static void nmethod_entries_do_begin();
static void nmethod_entries_do_end();
static void nmethod_entries_do(ZNMethodTableEntryClosure* cl);
static void unlink(ZWorkers* workers, bool unloading_occurred);
static void purge(ZWorkers* workers);
};
#endif // SHARE_GC_Z_ZNMETHODTABLE_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -43,38 +43,30 @@
// | |
// | 0-0 Registered Flag (1-bits) *
// |
// * 63-3 NMethod/ZNMethodWithImmediateOops Address (61-bits)
// * 63-3 NMethod Address (61-bits)
//
class nmethod;
class ZNMethodWithImmediateOops;
class ZNMethodTableEntry : public CHeapObj<mtGC> {
private:
typedef ZBitField<uint64_t, bool, 0, 1> field_registered;
typedef ZBitField<uint64_t, bool, 1, 1> field_unregistered;
typedef ZBitField<uint64_t, bool, 1, 1> field_immediate_oops;
typedef ZBitField<uint64_t, bool, 2, 1> field_non_immediate_oops;
typedef ZBitField<uint64_t, nmethod*, 3, 61, 3> field_method;
typedef ZBitField<uint64_t, ZNMethodWithImmediateOops*, 3, 61, 3> field_method_with_immediate_oops;
typedef ZBitField<uint64_t, bool, 0, 1> field_registered;
typedef ZBitField<uint64_t, bool, 1, 1> field_unregistered;
typedef ZBitField<uint64_t, bool, 1, 1> field_immediate_oops;
typedef ZBitField<uint64_t, bool, 2, 1> field_non_immediate_oops;
typedef ZBitField<uint64_t, nmethod*, 3, 61, 3> field_method;
uint64_t _entry;
public:
ZNMethodTableEntry(bool unregistered = false) :
explicit ZNMethodTableEntry(bool unregistered = false) :
_entry(field_unregistered::encode(unregistered) |
field_registered::encode(false)) {}
ZNMethodTableEntry(nmethod* method, bool non_immediate_oops) :
ZNMethodTableEntry(nmethod* method, bool non_immediate_oops, bool immediate_oops) :
_entry(field_method::encode(method) |
field_non_immediate_oops::encode(non_immediate_oops) |
field_immediate_oops::encode(false) |
field_registered::encode(true)) {}
ZNMethodTableEntry(ZNMethodWithImmediateOops* method_with_immediate_oops, bool non_immediate_oops) :
_entry(field_method_with_immediate_oops::encode(method_with_immediate_oops) |
field_non_immediate_oops::encode(non_immediate_oops) |
field_immediate_oops::encode(true) |
field_immediate_oops::encode(immediate_oops) |
field_registered::encode(true)) {}
bool registered() const {
@ -96,10 +88,6 @@ public:
nmethod* method() const {
return field_method::decode(_entry);
}
ZNMethodWithImmediateOops* method_with_immediate_oops() const {
return field_method_with_immediate_oops::decode(_entry);
}
};
#endif // SHARE_GC_Z_ZNMETHODTABLEENTRY_HPP

View File

@ -39,14 +39,23 @@ public:
#endif
};
class ZNMethodOopClosure : public OopClosure {
public:
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
};
template <bool finalizable>
class ZMarkBarrierOopClosure : public BasicOopIterateClosure {
class ZMarkBarrierOopClosure : public MetadataVisitingOopIterateClosure {
public:
ZMarkBarrierOopClosure();
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
virtual void do_klass(Klass* k);
virtual void do_cld(ClassLoaderData* cld);
#ifdef ASSERT
virtual bool should_verify_oops() {
return false;

View File

@ -24,6 +24,7 @@
#ifndef SHARE_GC_Z_ZOOPCLOSURES_INLINE_HPP
#define SHARE_GC_Z_ZOOPCLOSURES_INLINE_HPP
#include "classfile/classLoaderData.hpp"
#include "gc/z/zBarrier.inline.hpp"
#include "gc/z/zHeap.inline.hpp"
#include "gc/z/zOop.inline.hpp"
@ -40,9 +41,21 @@ inline void ZLoadBarrierOopClosure::do_oop(narrowOop* p) {
ShouldNotReachHere();
}
inline void ZNMethodOopClosure::do_oop(oop* p) {
if (ZResurrection::is_blocked()) {
ZBarrier::keep_alive_barrier_on_phantom_root_oop_field(p);
} else {
ZBarrier::load_barrier_on_root_oop_field(p);
}
}
inline void ZNMethodOopClosure::do_oop(narrowOop* p) {
ShouldNotReachHere();
}
template <bool finalizable>
inline ZMarkBarrierOopClosure<finalizable>::ZMarkBarrierOopClosure() :
BasicOopIterateClosure(finalizable ? NULL : ZHeap::heap()->reference_discoverer()) {}
MetadataVisitingOopIterateClosure(finalizable ? NULL : ZHeap::heap()->reference_discoverer()) {}
template <bool finalizable>
inline void ZMarkBarrierOopClosure<finalizable>::do_oop(oop* p) {
@ -54,6 +67,18 @@ inline void ZMarkBarrierOopClosure<finalizable>::do_oop(narrowOop* p) {
ShouldNotReachHere();
}
template <bool finalizable>
inline void ZMarkBarrierOopClosure<finalizable>::do_klass(Klass* k) {
ClassLoaderData* const cld = k->class_loader_data();
ZMarkBarrierOopClosure<finalizable>::do_cld(cld);
}
template <bool finalizable>
inline void ZMarkBarrierOopClosure<finalizable>::do_cld(ClassLoaderData* cld) {
const int claim = finalizable ? ClassLoaderData::_claim_finalizable : ClassLoaderData::_claim_strong;
cld->oops_do(this, claim);
}
inline bool ZPhantomIsAliveObjectClosure::do_object_b(oop o) {
return ZBarrier::is_alive_barrier_on_phantom_oop(o);
}

View File

@ -27,8 +27,11 @@
#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp"
#include "compiler/oopMap.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetNMethod.hpp"
#include "gc/shared/oopStorageParState.inline.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/z/zBarrierSetNMethod.hpp"
#include "gc/z/zGlobals.hpp"
#include "gc/z/zNMethodTable.hpp"
#include "gc/z/zOopClosures.inline.hpp"
@ -132,6 +135,30 @@ void ZParallelWeakOopsDo<T, F>::weak_oops_do(BoolObjectClosure* is_alive, ZRoots
}
}
class ZCodeBlobClosure : public CodeBlobToOopClosure {
private:
BarrierSetNMethod* _bs;
public:
ZCodeBlobClosure(OopClosure* cl) :
CodeBlobToOopClosure(cl, true /* fix_relocations */),
_bs(BarrierSet::barrier_set()->barrier_set_nmethod()) {}
virtual void do_code_blob(CodeBlob* cb) {
nmethod* const nm = cb->as_nmethod_or_null();
if (nm == NULL || nm->test_set_oops_do_mark()) {
return;
}
CodeBlobToOopClosure::do_code_blob(cb);
_bs->disarm(nm);
}
};
void ZRootsIteratorClosure::do_thread(Thread* thread) {
ZCodeBlobClosure code_cl(this);
thread->oops_do(this, ClassUnloading ? &code_cl : NULL);
}
ZRootsIterator::ZRootsIterator() :
_universe(this),
_object_synchronizer(this),
@ -145,16 +172,23 @@ ZRootsIterator::ZRootsIterator() :
ZStatTimer timer(ZSubPhasePauseRootsSetup);
Threads::change_thread_claim_parity();
COMPILER2_PRESENT(DerivedPointerTable::clear());
CodeCache::gc_prologue();
ZNMethodTable::gc_prologue();
if (ClassUnloading) {
nmethod::oops_do_marking_prologue();
} else {
ZNMethodTable::nmethod_entries_do_begin();
}
}
ZRootsIterator::~ZRootsIterator() {
ZStatTimer timer(ZSubPhasePauseRootsTeardown);
ResourceMark rm;
ZNMethodTable::gc_epilogue();
CodeCache::gc_epilogue();
if (ClassUnloading) {
nmethod::oops_do_marking_epilogue();
} else {
ZNMethodTable::nmethod_entries_do_end();
}
JvmtiExport::gc_epilogue();
COMPILER2_PRESENT(DerivedPointerTable::update_pointers());
Threads::assert_all_threads_claimed();
}
@ -209,7 +243,9 @@ void ZRootsIterator::oops_do(ZRootsIteratorClosure* cl, bool visit_jvmti_weak_ex
_jvmti_export.oops_do(cl);
_system_dictionary.oops_do(cl);
_threads.oops_do(cl);
_code_cache.oops_do(cl);
if (!ClassUnloading) {
_code_cache.oops_do(cl);
}
if (visit_jvmti_weak_export) {
_jvmti_weak_export.oops_do(cl);
}
@ -242,8 +278,13 @@ void ZConcurrentRootsIterator::do_jni_handles(ZRootsIteratorClosure* cl) {
void ZConcurrentRootsIterator::do_class_loader_data_graph(ZRootsIteratorClosure* cl) {
ZStatTimer timer(ZSubPhaseConcurrentRootsClassLoaderDataGraph);
CLDToOopClosure cld_cl(cl, _marking ? ClassLoaderData::_claim_strong : ClassLoaderData::_claim_none);
ClassLoaderDataGraph::cld_do(&cld_cl);
if (_marking) {
CLDToOopClosure cld_cl(cl, ClassLoaderData::_claim_strong);
ClassLoaderDataGraph::always_strong_cld_do(&cld_cl);
} else {
CLDToOopClosure cld_cl(cl, ClassLoaderData::_claim_none);
ClassLoaderDataGraph::cld_do(&cld_cl);
}
}
void ZConcurrentRootsIterator::oops_do(ZRootsIteratorClosure* cl) {

View File

@ -33,9 +33,7 @@
class ZRootsIteratorClosure : public OopClosure, public ThreadClosure {
public:
virtual void do_thread(Thread* thread) {
thread->oops_do(this, NULL);
}
virtual void do_thread(Thread* thread);
};
typedef OopStorage::ParState<true /* concurrent */, false /* is_const */> ZOopStorageIterator;

View File

@ -25,6 +25,7 @@
#define SHARE_GC_Z_ZTHREADLOCALDATA_HPP
#include "gc/z/zMarkStack.hpp"
#include "gc/z/zGlobals.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#include "utilities/sizes.hpp"
@ -62,6 +63,10 @@ public:
static ByteSize address_bad_mask_offset() {
return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _address_bad_mask);
}
static ByteSize nmethod_disarmed_offset() {
return address_bad_mask_offset() + in_ByteSize(ZNMethodDisarmedOffset);
}
};
#endif // SHARE_GC_Z_ZTHREADLOCALDATA_HPP

View File

@ -0,0 +1,194 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/codeBehaviours.hpp"
#include "code/codeCache.hpp"
#include "code/dependencyContext.hpp"
#include "gc/shared/gcBehaviours.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/z/zLock.inline.hpp"
#include "gc/z/zNMethodTable.hpp"
#include "gc/z/zOopClosures.hpp"
#include "gc/z/zStat.hpp"
#include "gc/z/zUnload.hpp"
#include "oops/access.inline.hpp"
static const ZStatSubPhase ZSubPhaseConcurrentClassesUnload("Concurrent Classes Unload");
class ZIsUnloadingOopClosure : public OopClosure {
private:
ZPhantomIsAliveObjectClosure _is_alive;
bool _is_unloading;
public:
ZIsUnloadingOopClosure() :
_is_alive(),
_is_unloading(false) {}
virtual void do_oop(oop* p) {
const oop o = RawAccess<>::oop_load(p);
if (o != NULL && !_is_alive.do_object_b(o)) {
_is_unloading = true;
}
}
virtual void do_oop(narrowOop* p) {
ShouldNotReachHere();
}
bool is_unloading() const {
return _is_unloading;
}
};
class ZIsUnloadingBehaviour : public IsUnloadingBehaviour {
private:
bool is_unloading(nmethod* nm) const {
ZIsUnloadingOopClosure cl;
nm->oops_do(&cl, true /* allow_zombie */);
return cl.is_unloading();
}
public:
virtual bool is_unloading(CompiledMethod* method) const {
nmethod* const nm = method->as_nmethod();
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
if (lock == NULL) {
return is_unloading(nm);
} else {
ZLocker<ZReentrantLock> locker(lock);
return is_unloading(nm);
}
}
};
class ZCompiledICProtectionBehaviour : public CompiledICProtectionBehaviour {
public:
virtual bool lock(CompiledMethod* method) {
nmethod* const nm = method->as_nmethod();
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
if (lock != NULL) {
lock->lock();
}
return true;
}
virtual void unlock(CompiledMethod* method) {
nmethod* const nm = method->as_nmethod();
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
if (lock != NULL) {
lock->unlock();
}
}
virtual bool is_safe(CompiledMethod* method) {
if (SafepointSynchronize::is_at_safepoint()) {
return true;
}
nmethod* const nm = method->as_nmethod();
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
return lock == NULL || lock->is_owned();
}
};
ZUnload::ZUnload(ZWorkers* workers) :
_workers(workers) {
if (!ClassUnloading) {
return;
}
static ZIsUnloadingBehaviour is_unloading_behaviour;
IsUnloadingBehaviour::set_current(&is_unloading_behaviour);
static ZCompiledICProtectionBehaviour ic_protection_behaviour;
CompiledICProtectionBehaviour::set_current(&ic_protection_behaviour);
}
void ZUnload::prepare() {
if (!ClassUnloading) {
return;
}
CodeCache::increment_unloading_cycle();
DependencyContext::cleaning_start();
}
void ZUnload::unlink() {
SuspendibleThreadSetJoiner sts;
bool unloading_occurred;
{
MutexLockerEx ml(ClassLoaderDataGraph_lock);
unloading_occurred = SystemDictionary::do_unloading(ZStatPhase::timer());
}
Klass::clean_weak_klass_links(unloading_occurred);
ZNMethodTable::unlink(_workers, unloading_occurred);
DependencyContext::cleaning_end();
}
void ZUnload::purge() {
{
SuspendibleThreadSetJoiner sts;
ZNMethodTable::purge(_workers);
}
ClassLoaderDataGraph::purge();
CodeCache::purge_exception_caches();
}
class ZUnloadRendezvousClosure : public ThreadClosure {
public:
void do_thread(Thread* thread) {}
};
void ZUnload::unload() {
if (!ClassUnloading) {
return;
}
ZStatTimer timer(ZSubPhaseConcurrentClassesUnload);
// Unlink stale metadata and nmethods
unlink();
// Make sure stale metadata and nmethods are no longer observable
ZUnloadRendezvousClosure cl;
Handshake::execute(&cl);
// Purge stale metadata and nmethods that were unlinked
purge();
}
void ZUnload::finish() {
// Resize and verify metaspace
MetaspaceGC::compute_new_size();
MetaspaceUtils::verify_metrics();
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef SHARE_GC_Z_ZUNLOAD_HPP
#define SHARE_GC_Z_ZUNLOAD_HPP
class ZWorkers;
class ZUnload {
private:
ZWorkers* const _workers;
void unlink();
void purge();
public:
ZUnload(ZWorkers* workers);
void prepare();
void unload();
void finish();
};
#endif // SHARE_GC_Z_ZUNLOAD_HPP