8381636: Add built-in AOT support to GrowableArray

Co-authored-by: Stefan Karlsson <stefank@openjdk.org>
Reviewed-by: matsaave, kvn, coleenp
This commit is contained in:
Ioi Lam 2026-04-10 17:55:02 +00:00
parent a3309eb665
commit d3d8a0d904
13 changed files with 72 additions and 189 deletions

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2026, 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 "cds/aotGrowableArray.hpp"
#include "cds/aotMetaspace.hpp"
#include "memory/allocation.inline.hpp"
#include "utilities/growableArray.hpp"
void AOTGrowableArrayHelper::deallocate(void* mem) {
if (!AOTMetaspace::in_aot_cache(mem)) {
GrowableArrayCHeapAllocator::deallocate(mem);
}
}

View File

@ -1,76 +0,0 @@
/*
* Copyright (c) 2026, 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_AOT_AOTGROWABLEARRAY_HPP
#define SHARE_AOT_AOTGROWABLEARRAY_HPP
#include <memory/metaspaceClosureType.hpp>
#include <utilities/growableArray.hpp>
class AOTGrowableArrayHelper {
public:
static void deallocate(void* mem);
};
// An AOTGrowableArray<T> provides the same functionality as a GrowableArray<T> that
// uses the C heap allocator. In addition, AOTGrowableArray<T> can be iterated with
// MetaspaceClosure. This type should be used for growable arrays that need to be
// stored in the AOT cache. See ModuleEntry::_reads for an example.
template <typename E>
class AOTGrowableArray : public GrowableArrayWithAllocator<E, AOTGrowableArray<E>> {
friend class VMStructs;
friend class GrowableArrayWithAllocator<E, AOTGrowableArray>;
static E* allocate(int max, MemTag mem_tag) {
return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag);
}
E* allocate() {
return allocate(this->_capacity, mtClass);
}
void deallocate(E* mem) {
#if INCLUDE_CDS
AOTGrowableArrayHelper::deallocate(mem);
#else
GrowableArrayCHeapAllocator::deallocate(mem);
#endif
}
public:
AOTGrowableArray(int initial_capacity, MemTag mem_tag) :
GrowableArrayWithAllocator<E, AOTGrowableArray>(
allocate(initial_capacity, mem_tag),
initial_capacity) {}
AOTGrowableArray() : AOTGrowableArray(0, mtClassShared) {}
// methods required by MetaspaceClosure
void metaspace_pointers_do(MetaspaceClosure* it);
int size_in_heapwords() const { return (int)heap_word_size(sizeof(*this)); }
MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; }
static bool is_read_only_by_default() { return false; }
};
#endif // SHARE_AOT_AOTGROWABLEARRAY_HPP

View File

@ -1,37 +0,0 @@
/*
* Copyright (c) 2026, 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_CDS_AOTGROWABLEARRAY_INLINE_HPP
#define SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP
#include "cds/aotGrowableArray.hpp"
#include "memory/metaspaceClosure.hpp"
template <typename E>
void AOTGrowableArray<E>::metaspace_pointers_do(MetaspaceClosure* it) {
it->push_c_array(AOTGrowableArray<E>::data_addr(), AOTGrowableArray<E>::capacity());
}
#endif // SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP

View File

@ -22,7 +22,6 @@
*
*/
#include "cds/aotGrowableArray.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
@ -41,6 +40,7 @@
#include "oops/typeArrayKlass.hpp"
#include "runtime/arguments.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
// Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables.
// (In GCC this is the field <Type>::_vptr, i.e., first word in the object.)
@ -58,10 +58,10 @@
#ifndef PRODUCT
// AOTGrowableArray has a vtable only when in non-product builds (due to
// GrowableArray has a vtable only when in non-product builds (due to
// the virtual printing functions in AnyObj).
using GrowableArray_ModuleEntry_ptr = AOTGrowableArray<ModuleEntry*>;
using GrowableArray_ModuleEntry_ptr = GrowableArray<ModuleEntry*>;
#define DEBUG_CPP_VTABLE_TYPES_DO(f) \
f(GrowableArray_ModuleEntry_ptr) \

View File

@ -23,7 +23,6 @@
*/
#include "cds/aotClassLocation.hpp"
#include "cds/aotGrowableArray.inline.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
#include "cds/cdsConfig.hpp"
@ -168,7 +167,7 @@ void ModuleEntry::add_read(ModuleEntry* m) {
} else {
if (reads() == nullptr) {
// Lazily create a module's reads list
AOTGrowableArray<ModuleEntry*>* new_reads = new (mtModule) AOTGrowableArray<ModuleEntry*>(MODULE_READS_SIZE, mtModule);
GrowableArray<ModuleEntry*>* new_reads = new (mtModule) GrowableArray<ModuleEntry*>(MODULE_READS_SIZE, mtModule);
set_reads(new_reads);
}

View File

@ -25,7 +25,6 @@
#ifndef SHARE_CLASSFILE_MODULEENTRY_HPP
#define SHARE_CLASSFILE_MODULEENTRY_HPP
#include "cds/aotGrowableArray.hpp"
#include "jni.h"
#include "memory/metaspaceClosureType.hpp"
#include "oops/oopHandle.hpp"
@ -70,7 +69,7 @@ private:
// for shared classes from this module
Symbol* _name; // name of this module
ClassLoaderData* _loader_data;
AOTGrowableArray<ModuleEntry*>* _reads; // list of modules that are readable by this module
GrowableArray<ModuleEntry*>* _reads; // list of modules that are readable by this module
Symbol* _version; // module version number
Symbol* _location; // module location
@ -118,10 +117,10 @@ public:
bool can_read(ModuleEntry* m) const;
bool has_reads_list() const;
AOTGrowableArray<ModuleEntry*>* reads() const {
GrowableArray<ModuleEntry*>* reads() const {
return _reads;
}
void set_reads(AOTGrowableArray<ModuleEntry*>* r) {
void set_reads(GrowableArray<ModuleEntry*>* r) {
_reads = r;
}
void pack_reads() {

View File

@ -22,7 +22,6 @@
*
*/
#include "cds/aotGrowableArray.inline.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
@ -83,7 +82,7 @@ void PackageEntry::add_qexport(ModuleEntry* m) {
if (!has_qual_exports_list()) {
// Lazily create a package's qualified exports list.
// Initial size is small, do not anticipate export lists to be large.
_qualified_exports = new (mtModule) AOTGrowableArray<ModuleEntry*>(QUAL_EXP_SIZE, mtModule);
_qualified_exports = new (mtModule) GrowableArray<ModuleEntry*>(QUAL_EXP_SIZE, mtModule);
}
// Determine, based on this newly established export to module m,

View File

@ -25,7 +25,6 @@
#ifndef SHARE_CLASSFILE_PACKAGEENTRY_HPP
#define SHARE_CLASSFILE_PACKAGEENTRY_HPP
#include "cds/aotGrowableArray.hpp"
#include "classfile/moduleEntry.hpp"
#include "memory/metaspaceClosureType.hpp"
#include "oops/symbol.hpp"
@ -116,7 +115,7 @@ private:
bool _must_walk_exports;
// Contains list of modules this package is qualifiedly exported to. Access
// to this list is protected by the Module_lock.
AOTGrowableArray<ModuleEntry*>* _qualified_exports;
GrowableArray<ModuleEntry*>* _qualified_exports;
JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
// Initial size of a package entry's list of qualified exports.

View File

@ -22,11 +22,11 @@
*
*/
#include "cds/aotGrowableArray.hpp"
#include "classfile/packageEntry.hpp"
#include "memory/metaspaceClosure.hpp"
#include "oops/array.hpp"
#include "oops/instanceKlass.hpp"
#include "utilities/growableArray.hpp"
// Sanity checks
static_assert(!HAS_METASPACE_POINTERS_DO(int));
@ -35,8 +35,6 @@ static_assert(HAS_METASPACE_POINTERS_DO(Array<int>));
static_assert(HAS_METASPACE_POINTERS_DO(Array<InstanceKlass*>));
static_assert(HAS_METASPACE_POINTERS_DO(InstanceKlass));
static_assert(HAS_METASPACE_POINTERS_DO(PackageEntry));
static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray<int>));
static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray<PackageEntry*>));
void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) {
if (_enclosing_ref != nullptr) {

View File

@ -25,7 +25,6 @@
#ifndef SHARE_MEMORY_METASPACECLOSURE_HPP
#define SHARE_MEMORY_METASPACECLOSURE_HPP
#include "cds/aotGrowableArray.hpp"
#include "cppstdlib/type_traits.hpp"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
@ -90,7 +89,9 @@ public:
// int size_in_heapwords() const;
//
// Currently, the iterable types include all subtypes of MetsapceObj, as well
// as GrowableArray, ModuleEntry and PackageEntry.
// as GrowableArray (C-heap allocated only), ModuleEntry, and PackageEntry.
//
// (Note that GrowableArray is supported specially and does not require the above functions.)
//
// Calling these functions would be trivial if these were virtual functions.
// However, to save space, MetaspaceObj has NO vtable. The vtable is introduced
@ -303,11 +304,38 @@ private:
};
//--------------------------------
// Support for AOTGrowableArray<T>
// Support for GrowableArray<T>
//--------------------------------
// GrowableArrayRef -- iterate an instance of GrowableArray<T>.
template <class T> class GrowableArrayRef : public Ref {
GrowableArray<T>** _mpp;
GrowableArray<T>* dereference() const {
return *_mpp;
}
public:
GrowableArrayRef(GrowableArray<T>** mpp, Writability w) : Ref(w), _mpp(mpp) {}
virtual void** mpp() const {
return (void**)_mpp;
}
virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
GrowableArray<T>* array = dereference();
log_trace(aot)("Iter(GrowableArray): %p [%d]", array, array->length());
array->assert_on_C_heap();
it->push_c_array(array->data_addr(), array->capacity());
}
virtual bool is_read_only_by_default() const { return false; }
virtual bool not_null() const { return dereference() != nullptr; }
virtual int size() const { return (int)heap_word_size(sizeof(*dereference())); }
virtual MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; }
};
// Abstract base class for MSOCArrayRef, MSOPointerCArrayRef and OtherCArrayRef.
// These are used for iterating the buffer held by AOTGrowableArray<T>.
// These are used for iterating the buffer held by GrowableArray<T>.
template <class T> class CArrayRef : public Ref {
T** _mpp;
int _num_elems; // Number of elements
@ -354,7 +382,7 @@ private:
// MSOCArrayRef<T> -- iterate a C array of type T, where T has metaspace_pointer_do().
// We recursively call T::metaspace_pointers_do() for each element in this array.
// This is for supporting AOTGrowableArray<T>.
// This is for supporting GrowableArray<T>.
//
// E.g., PackageEntry* _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry objects
// ...
@ -377,7 +405,7 @@ private:
// MSOPointerCArrayRef<T> -- iterate a C array of type T*, where T has metaspace_pointer_do().
// We recursively call MetaspaceClosure::push() for each pointer in this array.
// This is for supporting AOTGrowableArray<T*>.
// This is for supporting GrowableArray<T*>.
//
// E.g., PackageEntry** _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry pointers
// ...
@ -440,11 +468,11 @@ public:
// Array<Array<Klass*>*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef
// Array<Annotation*>* a5 = ...; it->push(&a5); => MSOPointerArrayRef
//
// AOTGrowableArrays have a separate "C array" buffer, so they are scanned in two steps:
// GrowableArrays have a separate "C array" buffer, so they are scanned in two steps:
//
// AOTGrowableArray<jlong>* ga1 = ...; it->push(&ga1); => MSORef => OtherCArrayRef
// AOTGrowableArray<Annotation>* ga2 = ...; it->push(&ga2); => MSORef => MSOCArrayRef
// AOTGrowableArray<Klass*>* ga3 = ...; it->push(&ga3); => MSORef => MSOPointerCArrayRef
// GrowableArray<jlong>* ga1 = ...; it->push(&ga1); => GrowableArrayRef => OtherCArrayRef
// GrowableArray<Annotation>* ga2 = ...; it->push(&ga2); => GrowableArrayRef => MSOCArrayRef
// GrowableArray<Klass*>* ga3 = ...; it->push(&ga3); => GrowableArrayRef => MSOPointerCArrayRef
//
// Note that the following will fail to compile:
//
@ -476,7 +504,12 @@ public:
push_with_ref<MSOPointerArrayRef<T>>(mpp, w);
}
// --- The buffer of AOTGrowableArray<T>
template <typename T>
void push(GrowableArray<T>** mpp, Writability w = _default) {
push_with_ref<GrowableArrayRef<T>>(mpp, w);
}
// --- The buffer of GrowableArray<T>
template <typename T, ENABLE_IF(!HAS_METASPACE_POINTERS_DO(T))>
void push_c_array(T** mpp, int num_elems, Writability w = _default) {
push_impl(new OtherCArrayRef<T>(mpp, num_elems, w));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2026, 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
@ -22,6 +22,7 @@
*
*/
#include "cds/aotMetaspace.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/javaThread.hpp"
@ -56,7 +57,9 @@ void* GrowableArrayCHeapAllocator::allocate(int max, int element_size, MemTag me
}
void GrowableArrayCHeapAllocator::deallocate(void* elements) {
FreeHeap(elements);
if (!AOTMetaspace::in_aot_cache(elements)) {
FreeHeap(elements);
}
}
#ifdef ASSERT

View File

@ -116,12 +116,6 @@ protected:
~GrowableArrayView() {}
protected:
// Used by AOTGrowableArray for MetaspaceClosure support.
E** data_addr() {
return &_data;
}
public:
bool operator==(const GrowableArrayView& rhs) const {
if (_len != rhs._len)
@ -303,6 +297,11 @@ public:
}
tty->print("}\n");
}
// MetaspaceClosure support
E** data_addr() {
return &_data;
}
};
template <typename E>
@ -821,6 +820,8 @@ public:
this->clear_and_deallocate();
}
}
void assert_on_C_heap() { assert(on_C_heap(), "must be on C heap"); }
};
// Leaner GrowableArray for CHeap backed data arrays, with compile-time decided MemTag.

View File

@ -21,7 +21,6 @@
* questions.
*/
#include "cds/aotGrowableArray.inline.hpp"
#include "memory/allocation.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceClosure.hpp"
@ -168,9 +167,9 @@ TEST_VM(MetaspaceClosure, OtherArrayRef) {
EXPECT_TRUE(closure.has_visited(array)) << "must be";
}
// iterate an AOTGrowableArray<MyMetaData*>
// iterate an GrowableArray<MyMetaData*>
TEST_VM(MetaspaceClosure, GrowableArray_MSOPointer) {
AOTGrowableArray<MyMetaData*>* array = new(mtClass) AOTGrowableArray<MyMetaData*>(2, mtClass);
GrowableArray<MyMetaData*>* array = new(mtClass) GrowableArray<MyMetaData*>(2, mtClass);
MyMetaData x;
MyMetaData y;
@ -190,9 +189,9 @@ TEST_VM(MetaspaceClosure, GrowableArray_MSOPointer) {
EXPECT_TRUE(closure.has_visited(&z)) << "must be";
}
// iterate an AOTGrowableArray<MyMetaData>
// iterate an GrowableArray<MyMetaData>
TEST_VM(MetaspaceClosure, GrowableArray_MSO) {
AOTGrowableArray<MyMetaData>* array = new(mtClass) AOTGrowableArray<MyMetaData>(4, mtClass);
GrowableArray<MyMetaData>* array = new(mtClass) GrowableArray<MyMetaData>(4, mtClass);
for (int i = 0; i < array->length(); i++) {
EXPECT_TRUE(array->at(i)._a == nullptr) << "should be initialized to null";
@ -218,9 +217,9 @@ TEST_VM(MetaspaceClosure, GrowableArray_MSO) {
EXPECT_TRUE(closure.has_visited(&z)) << "must be";
}
// iterate an AOTGrowableArray<jlong>
// iterate an GrowableArray<jlong>
TEST_VM(MetaspaceClosure, GrowableArray_jlong) {
AOTGrowableArray<jlong>* array = new(mtClass) AOTGrowableArray<jlong>(4, mtClass);
GrowableArray<jlong>* array = new(mtClass) GrowableArray<jlong>(4, mtClass);
MyUniqueMetaspaceClosure closure;
closure.push(&array);