blob: bd99720219a03dc40db1b065978b707ba4597527 [file] [log] [blame]
/*
* Copyright (c) 2017, 2023, 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_MEMORY_METASPACECLOSURE_HPP
#define SHARE_MEMORY_METASPACECLOSURE_HPP
#include "logging/log.hpp"
#include "memory/allocation.hpp"
#include "metaprogramming/enableIf.hpp"
#include "oops/array.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/macros.hpp"
#include "utilities/resizeableResourceHash.hpp"
#include <type_traits>
// The metadata hierarchy is separate from the oop hierarchy
class MetaspaceObj; // no C++ vtable
//class Array; // no C++ vtable
class Annotations; // no C++ vtable
class ConstantPoolCache; // no C++ vtable
class ConstMethod; // no C++ vtable
class MethodCounters; // no C++ vtable
class Symbol; // no C++ vtable
class Metadata; // has C++ vtable (so do all subclasses)
class ConstantPool;
class MethodData;
class Method;
class Klass;
class InstanceKlass;
class InstanceMirrorKlass;
class InstanceClassLoaderKlass;
class InstanceRefKlass;
class ArrayKlass;
class ObjArrayKlass;
class TypeArrayKlass;
// class MetaspaceClosure --
//
// This class is used for iterating the objects in the HotSpot Metaspaces. It
// provides an API to walk all the reachable objects starting from a set of
// root references (such as all Klass'es in the SystemDictionary).
//
// Currently it is used for compacting the CDS archive by eliminate temporary
// objects allocated during archive creation time. See ArchiveBuilder for an example.
//
// To support MetaspaceClosure, each subclass of MetaspaceObj must provide
// a method of the type void metaspace_pointers_do(MetaspaceClosure*). This method
// should call MetaspaceClosure::push() on every pointer fields of this
// class that points to a MetaspaceObj. See Annotations::metaspace_pointers_do()
// for an example.
class MetaspaceClosure {
public:
enum Writability {
_writable,
_not_writable,
_default
};
// class MetaspaceClosure::Ref --
//
// MetaspaceClosure can be viewed as a very simple type of copying garbage
// collector. For it to function properly, it requires each subclass of
// MetaspaceObj to provide two methods:
//
// size_t size(); -- to determine how much data to copy
// void metaspace_pointers_do(MetaspaceClosure*); -- to locate all the embedded pointers
//
// Calling these methods would be trivial if these two were virtual methods.
// However, to save space, MetaspaceObj has NO vtable. The vtable is introduced
// only in the Metadata class.
//
// To work around the lack of a vtable, we use the Ref class with templates
// (see MSORef, OtherArrayRef, MSOArrayRef, and MSOPointerArrayRef)
// so that we can statically discover the type of a object. The use of Ref
// depends on the fact that:
//
// [1] We don't use polymorphic pointers for MetaspaceObj's that are not subclasses
// of Metadata. I.e., we don't do this:
// class Klass {
// MetaspaceObj *_obj;
// Array<int>* foo() { return (Array<int>*)_obj; }
// Symbol* bar() { return (Symbol*) _obj; }
//
// [2] All Array<T> dimensions are statically declared.
class Ref : public CHeapObj<mtMetaspace> {
Writability _writability;
Ref* _next;
void* _user_data;
NONCOPYABLE(Ref);
protected:
virtual void** mpp() const = 0;
Ref(Writability w) : _writability(w), _next(nullptr), _user_data(nullptr) {}
public:
virtual bool not_null() const = 0;
virtual int size() const = 0;
virtual void metaspace_pointers_do(MetaspaceClosure *it) const = 0;
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const = 0;
virtual MetaspaceObj::Type msotype() const = 0;
virtual bool is_read_only_by_default() const = 0;
virtual ~Ref() {}
address obj() const {
return *addr();
}
address* addr() const {
return (address*)mpp();
}
void update(address new_loc) const;
Writability writability() const { return _writability; };
void set_user_data(void* data) { _user_data = data; }
void* user_data() { return _user_data; }
void set_next(Ref* n) { _next = n; }
Ref* next() const { return _next; }
};
private:
// MSORef -- iterate an instance of MetaspaceObj
template <class T> class MSORef : public Ref {
T** _mpp;
T* dereference() const {
return *_mpp;
}
protected:
virtual void** mpp() const {
return (void**)_mpp;
}
public:
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 not_null() const { return dereference() != nullptr; }
virtual int size() const { return dereference()->size(); }
virtual MetaspaceObj::Type msotype() const { return dereference()->type(); }
virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
dereference()->metaspace_pointers_do(it);
}
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
((T*)new_loc)->metaspace_pointers_do(it);
}
};
// abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef
template <class T> class ArrayRef : public Ref {
Array<T>** _mpp;
protected:
Array<T>* dereference() const {
return *_mpp;
}
virtual void** mpp() const {
return (void**)_mpp;
}
ArrayRef(Array<T>** mpp, Writability w) : Ref(w), _mpp(mpp) {}
// all Arrays are read-only by default
virtual bool is_read_only_by_default() const { return true; }
virtual bool not_null() const { return dereference() != nullptr; }
virtual int size() const { return dereference()->size(); }
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 {
Array<T>* array = ArrayRef<T>::dereference();
log_trace(cds)("Iter(OtherArray): %p [%d]", array, array->length());
}
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
Array<T>* array = (Array<T>*)new_loc;
log_trace(cds)("Iter(OtherArray): %p [%d]", array, array->length());
}
};
// MSOArrayRef -- iterate an instance of Array<T>, where T is a subtype of MetaspaceObj.
// We recursively call T::metaspace_pointers_do() for each element in this array.
template <class T> class MSOArrayRef : public ArrayRef<T> {
public:
MSOArrayRef(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 {
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 {
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(MSOPointerArray): %p [%d]", array, array->length());
for (int i = 0; i < array->length(); i++) {
T** mpp = array->adr_at(i);
it->push(mpp);
}
}
};
// Normally, chains of references like a->b->c->d are iterated recursively. However,
// if recursion is too deep, we save the Refs in _pending_refs, and push them later in
// MetaspaceClosure::finish(). This avoids overflowing the C stack.
static const int MAX_NEST_LEVEL = 5;
Ref* _pending_refs;
int _nest_level;
Ref* _enclosing_ref;
void push_impl(Ref* ref);
void do_push(Ref* ref);
public:
MetaspaceClosure(): _pending_refs(nullptr), _nest_level(0), _enclosing_ref(nullptr) {}
~MetaspaceClosure();
void finish();
// enclosing_ref() is used to compute the offset of a field in a C++ class. For example
// class Foo { intx scala; Bar* ptr; }
// Foo *f = 0x100;
// when the f->ptr field is iterated with do_ref() on 64-bit platforms, we will have
// do_ref(Ref* r) {
// r->addr() == 0x108; // == &f->ptr;
// enclosing_ref()->obj() == 0x100; // == foo
// So we know that we are iterating upon a field at offset 8 of the object at 0x100.
//
// Note that if we have stack overflow, do_pending_ref(r) will be called first and
// do_ref(r) will be called later, for the same r. In this case, enclosing_ref() is valid only
// when do_pending_ref(r) is called, and will return null when do_ref(r) is called.
Ref* enclosing_ref() const {
return _enclosing_ref;
}
// This is called when a reference is placed in _pending_refs. Override this
// function if you're using enclosing_ref(). See notes above.
virtual void do_pending_ref(Ref* ref) {}
// returns true if we want to keep iterating the pointers embedded inside <ref>
virtual bool do_ref(Ref* ref, bool read_only) = 0;
private:
template <class REF_TYPE, typename T>
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:
//
// MetaspaceClosure* it = ...;
// Klass* o = ...; it->push(&o); => MSORef
// Array<int>* a1 = ...; it->push(&a1); => OtherArrayRef
// 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);
}
template <typename T, ENABLE_IF(!std::is_base_of<MetaspaceObj, T>::value)>
void push(Array<T>** mpp, Writability w = _default) {
push_with_ref<OtherArrayRef<T>>(mpp, w);
}
template <typename T, ENABLE_IF(std::is_base_of<MetaspaceObj, T>::value)>
void push(Array<T>** mpp, Writability w = _default) {
push_with_ref<MSOArrayRef<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 = nullptr;
it->push(&h);
Array<Hashtable<bool, mtInternal>*>* a6 = nullptr;
it->push(&a6);
Array<int*>* a7 = nullptr;
it->push(&a7);
}
#endif
};
// This is a special MetaspaceClosure that visits each unique MetaspaceObj once.
class UniqueMetaspaceClosure : public MetaspaceClosure {
static const int INITIAL_TABLE_SIZE = 15889;
static const int MAX_TABLE_SIZE = 1000000;
// Do not override. Returns true if we are discovering ref->obj() for the first time.
virtual bool do_ref(Ref* ref, bool read_only);
public:
// Gets called the first time we discover an object.
virtual bool do_unique_ref(Ref* ref, bool read_only) = 0;
UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
private:
ResizeableResourceHashtable<address, bool, AnyObj::C_HEAP,
mtClassShared> _has_been_visited;
};
#endif // SHARE_MEMORY_METASPACECLOSURE_HPP