8259214: MetaspaceClosure support for Arrays of MetaspaceObj

Reviewed-by: fparain, ccheung
This commit is contained in:
Ioi Lam 2021-01-22 22:47:08 +00:00
parent bdc305e1cb
commit aa57d07ce8
2 changed files with 245 additions and 51 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,10 +27,13 @@
#include "logging/log.hpp" #include "logging/log.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "metaprogramming/enableIf.hpp"
#include "oops/array.hpp" #include "oops/array.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp" #include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp" #include "utilities/hashtable.inline.hpp"
#include "utilities/macros.hpp"
#include <type_traits>
// The metadata hierarchy is separate from the oop hierarchy // The metadata hierarchy is separate from the oop hierarchy
class MetaspaceObj; // no C++ vtable class MetaspaceObj; // no C++ vtable
@ -92,8 +95,8 @@ public:
// However, to save space, MetaspaceObj has NO vtable. The vtable is introduced // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced
// only in the Metadata class. // only in the Metadata class.
// //
// To work around the lack of a vtable, we use Ref class with templates // To work around the lack of a vtable, we use the Ref class with templates
// (see ObjectRef, PrimitiveArrayRef and PointerArrayRef) // (see MSORef, OtherArrayRef, MSOArrayRef, and MSOPointerArrayRef)
// so that we can statically discover the type of a object. The use of Ref // so that we can statically discover the type of a object. The use of Ref
// depends on the fact that: // depends on the fact that:
// //
@ -155,8 +158,8 @@ public:
}; };
private: private:
// -------------------------------------------------- ObjectRef // MSORef -- iterate an instance of MetaspaceObj
template <class T> class ObjectRef : public Ref { template <class T> class MSORef : public Ref {
T** _mpp; T** _mpp;
T* dereference() const { T* dereference() const {
return *_mpp; return *_mpp;
@ -167,7 +170,7 @@ private:
} }
public: public:
ObjectRef(T** mpp, Writability w) : Ref(w), _mpp(mpp) {} MSORef(T** mpp, Writability w) : Ref(w), _mpp(mpp) {}
virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); } virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); }
virtual bool not_null() const { return dereference() != NULL; } virtual bool not_null() const { return dereference() != NULL; }
@ -182,65 +185,80 @@ private:
} }
}; };
// -------------------------------------------------- PrimitiveArrayRef // abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef
template <class T> class PrimitiveArrayRef : public Ref { template <class T> class ArrayRef : public Ref {
Array<T>** _mpp; Array<T>** _mpp;
protected:
Array<T>* dereference() const { Array<T>* dereference() const {
return *_mpp; return *_mpp;
} }
protected:
virtual void** mpp() const { virtual void** mpp() const {
return (void**)_mpp; return (void**)_mpp;
} }
public: ArrayRef(Array<T>** mpp, Writability w) : Ref(w), _mpp(mpp) {}
PrimitiveArrayRef(Array<T>** mpp, Writability w) : Ref(w), _mpp(mpp) {}
// all Arrays are read-only by default // all Arrays are read-only by default
virtual bool is_read_only_by_default() const { return true; } virtual bool is_read_only_by_default() const { return true; }
virtual bool not_null() const { return dereference() != NULL; } virtual bool not_null() const { return dereference() != NULL; }
virtual int size() const { return dereference()->size(); } virtual int size() const { return dereference()->size(); }
virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T)); } virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T)); }
};
// OtherArrayRef -- iterate an instance of Array<T>, where T is NOT a subtype of MetaspaceObj.
// T can be a primitive type, such as int, or a structure. However, we do not scan
// the fields inside T, so you should not embed any pointers inside T.
template <class T> class OtherArrayRef : public ArrayRef<T> {
public:
OtherArrayRef(Array<T>** mpp, Writability w) : ArrayRef<T>(mpp, w) {}
virtual void metaspace_pointers_do(MetaspaceClosure *it) const { virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
Array<T>* array = dereference(); Array<T>* array = ArrayRef<T>::dereference();
log_trace(cds)("Iter(PrimitiveArray): %p [%d]", array, array->length()); log_trace(cds)("Iter(OtherArray): %p [%d]", array, array->length());
} }
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
Array<T>* array = (Array<T>*)new_loc; Array<T>* array = (Array<T>*)new_loc;
log_trace(cds)("Iter(PrimitiveArray): %p [%d]", array, array->length()); log_trace(cds)("Iter(OtherArray): %p [%d]", array, array->length());
} }
}; };
// -------------------------------------------------- PointerArrayRef // MSOArrayRef -- iterate an instance of Array<T>, where T is a subtype of MetaspaceObj.
template <class T> class PointerArrayRef : public Ref { // We recursively call T::metaspace_pointers_do() for each element in this array.
Array<T*>** _mpp; template <class T> class MSOArrayRef : public ArrayRef<T> {
Array<T*>* dereference() const {
return *_mpp;
}
protected:
virtual void** mpp() const {
return (void**)_mpp;
}
public: public:
PointerArrayRef(Array<T*>** mpp, Writability w) : Ref(w), _mpp(mpp) {} MSOArrayRef(Array<T>** mpp, Writability w) : ArrayRef<T>(mpp, w) {}
// all Arrays are read-only by default
virtual bool is_read_only_by_default() const { return true; }
virtual bool not_null() const { return dereference() != NULL; }
virtual int size() const { return dereference()->size(); }
virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T*)); }
virtual void metaspace_pointers_do(MetaspaceClosure *it) const { virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
metaspace_pointers_do_at_impl(it, dereference()); metaspace_pointers_do_at_impl(it, ArrayRef<T>::dereference());
}
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
metaspace_pointers_do_at_impl(it, (Array<T>*)new_loc);
}
private:
void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T>* array) const {
log_trace(cds)("Iter(MSOArray): %p [%d]", array, array->length());
for (int i = 0; i < array->length(); i++) {
T* elm = array->adr_at(i);
elm->metaspace_pointers_do(it);
}
}
};
// MSOPointerArrayRef -- iterate an instance of Array<T*>, where T is a subtype of MetaspaceObj.
// We recursively call MetaspaceClosure::push() for each pointer in this array.
template <class T> class MSOPointerArrayRef : public ArrayRef<T*> {
public:
MSOPointerArrayRef(Array<T*>** mpp, Writability w) : ArrayRef<T*>(mpp, w) {}
virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
metaspace_pointers_do_at_impl(it, ArrayRef<T*>::dereference());
} }
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
metaspace_pointers_do_at_impl(it, (Array<T*>*)new_loc); metaspace_pointers_do_at_impl(it, (Array<T*>*)new_loc);
} }
private: private:
void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T*>* array) const { void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T*>* array) const {
log_trace(cds)("Iter(ObjectArray): %p [%d]", array, array->length()); log_trace(cds)("Iter(MSOPointerArray): %p [%d]", array, array->length());
for (int i = 0; i < array->length(); i++) { for (int i = 0; i < array->length(); i++) {
T** mpp = array->adr_at(i); T** mpp = array->adr_at(i);
it->push(mpp); it->push(mpp);
@ -288,31 +306,69 @@ public:
// returns true if we want to keep iterating the pointers embedded inside <ref> // returns true if we want to keep iterating the pointers embedded inside <ref>
virtual bool do_ref(Ref* ref, bool read_only) = 0; virtual bool do_ref(Ref* ref, bool read_only) = 0;
// When you do: private:
// void MyType::metaspace_pointers_do(MetaspaceClosure* it) { template <class REF_TYPE, typename T>
// it->push(_my_field) void push_with_ref(T** mpp, Writability w) {
// } push_impl(new REF_TYPE(mpp, w));
}
public:
// When MetaspaceClosure::push(...) is called, pick the correct Ref subtype to handle it:
// //
// C++ will try to match the "most specific" template function. This one will // MetaspaceClosure* it = ...;
// will be matched if possible (if mpp is an Array<> of any pointer type). // Klass* o = ...; it->push(&o); => MSORef
template <typename T> void push(Array<T*>** mpp, Writability w = _default) { // Array<int>* a1 = ...; it->push(&a1); => OtherArrayRef
push_impl(new PointerArrayRef<T>(mpp, w)); // Array<Annotation>* a2 = ...; it->push(&a2); => MSOArrayRef
// Array<Klass*>* a3 = ...; it->push(&a3); => MSOPointerArrayRef
// Array<Array<Klass*>*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef
// Array<Annotation*>* a5 = ...; it->push(&a5); => MSOPointerArrayRef
//
// Note that the following will fail to compile (to prevent you from adding new fields
// into the MetaspaceObj subtypes that cannot be properly copied by CDS):
//
// Hashtable* h = ...; it->push(&h); => Hashtable is not a subclass of MetaspaceObj
// Array<Hashtable*>* a6 = ...; it->push(&a6); => Hashtable is not a subclass of MetaspaceObj
// Array<int*>* a7 = ...; it->push(&a7); => int is not a subclass of MetaspaceObj
template <typename T>
void push(T** mpp, Writability w = _default) {
static_assert(std::is_base_of<MetaspaceObj, T>::value, "Do not push pointers of arbitrary types");
push_with_ref<MSORef<T>>(mpp, w);
} }
// If the above function doesn't match (mpp is an Array<>, but T is not a pointer type), then template <typename T, ENABLE_IF(!std::is_base_of<MetaspaceObj, T>::value)>
// this is the second choice. void push(Array<T>** mpp, Writability w = _default) {
template <typename T> void push(Array<T>** mpp, Writability w = _default) { push_with_ref<OtherArrayRef<T>>(mpp, w);
push_impl(new PrimitiveArrayRef<T>(mpp, w));
} }
// If the above function doesn't match (mpp is not an Array<> type), then template <typename T, ENABLE_IF(std::is_base_of<MetaspaceObj, T>::value)>
// this will be matched by default. void push(Array<T>** mpp, Writability w = _default) {
template <class T> void push(T** mpp, Writability w = _default) { push_with_ref<MSOArrayRef<T>>(mpp, w);
push_impl(new ObjectRef<T>(mpp, w));
} }
template <typename T>
void push(Array<T*>** mpp, Writability w = _default) {
static_assert(std::is_base_of<MetaspaceObj, T>::value, "Do not push Arrays of arbitrary pointer types");
push_with_ref<MSOPointerArrayRef<T>>(mpp, w);
}
#if 0
// Enable this block if you're changing the push(...) methods, to test for types that should be
// disallowed. Each of the following "push" calls should result in a compile-time error.
void test_disallowed_types(MetaspaceClosure* it) {
Hashtable<bool, mtInternal>* h = NULL;
it->push(&h);
Array<Hashtable<bool, mtInternal>*>* a6 = NULL;
it->push(&a6);
Array<int*>* a7 = NULL;
it->push(&a7);
}
#endif
template <class T> void push_method_entry(T** mpp, intptr_t* p) { template <class T> void push_method_entry(T** mpp, intptr_t* p) {
Ref* ref = new ObjectRef<T>(mpp, _default); Ref* ref = new MSORef<T>(mpp, _default);
push_special(_method_entry_ref, ref, (intptr_t*)p); push_special(_method_entry_ref, ref, (intptr_t*)p);
if (!ref->keep_after_pushing()) { if (!ref->keep_after_pushing()) {
delete ref; delete ref;

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2021, 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 "memory/allocation.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceClosure.hpp"
#include "oops/array.hpp"
#include "oops/metadata.hpp"
#include "runtime/thread.hpp"
#include "unittest.hpp"
class MyMetaData : public MetaspaceObj {
public:
MyMetaData* _a;
MyMetaData* _b;
MyMetaData() : _a(NULL), _b(NULL) {}
MetaspaceObj::Type type() const {
return MetaspaceObj::SymbolType; // Just lie. It doesn't matter in this test
}
const char* internal_name() const {
return "MyMetaData";
}
int size() const {
return align_up((int)sizeof(MyMetaData), wordSize) / wordSize;
};
static bool is_read_only_by_default() {
return true;
}
void metaspace_pointers_do(MetaspaceClosure* it) {
it->push(&_a);
it->push(&_b);
}
};
class MyUniqueMetaspaceClosure : public MetaspaceClosure {
static constexpr int SIZE = 10;
MyMetaData* _visited[SIZE];
int _count;
public:
MyUniqueMetaspaceClosure() {
for (int i = 0; i < SIZE; i++) {
_visited[i] = NULL;
}
_count = 0;
}
virtual bool do_ref(Ref* ref, bool read_only) {
MyMetaData* ptr = (MyMetaData*)ref->obj();
assert(_count < SIZE, "out of bounds");
_visited[_count++] = ptr;
return true; // recurse
}
bool has_visited(MyMetaData* p) {
for (int i = 0; i < SIZE; i++) {
if (_visited[i] == p) {
return true;
}
}
return false;
}
};
// iterate an Array<MyMetaData*>
TEST_VM(MetaspaceClosure, MSOPointerArrayRef) {
Thread* THREAD = Thread::current();
ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data();
Array<MyMetaData*>* array = MetadataFactory::new_array<MyMetaData*>(cld, 4, THREAD);
for (int i = 0; i < array->length(); i++) {
EXPECT_TRUE(array->at(i) == NULL) << "should be initialized to null";
}
MyMetaData x;
MyMetaData y;
MyMetaData z;
array->at_put(0, &x);
array->at_put(2, &y);
y._a = &z;
MyUniqueMetaspaceClosure closure;
closure.push(&array);
EXPECT_TRUE(closure.has_visited(&x)) << "must be";
EXPECT_TRUE(closure.has_visited(&y)) << "must be";
EXPECT_TRUE(closure.has_visited(&z)) << "must be";
}
// iterate an Array<MyMetaData>
TEST_VM(MetaspaceClosure, MSOArrayRef) {
Thread* THREAD = Thread::current();
ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data();
Array<MyMetaData>* array = MetadataFactory::new_array<MyMetaData>(cld, 4, THREAD);
for (int i = 0; i < array->length(); i++) {
EXPECT_TRUE(array->at(i)._a == NULL) << "should be initialized to null";
EXPECT_TRUE(array->at(i)._b == NULL) << "should be initialized to null";
}
MyMetaData x;
MyMetaData y;
MyMetaData z;
array->adr_at(0)->_a = &x;
array->adr_at(2)->_b = &y;
y._a = &z;
MyUniqueMetaspaceClosure closure;
closure.push(&array);
EXPECT_TRUE(closure.has_visited(&x)) << "must be";
EXPECT_TRUE(closure.has_visited(&y)) << "must be";
EXPECT_TRUE(closure.has_visited(&z)) << "must be";
}