8296785: Use realloc for CHeap-allocated BitMaps
Reviewed-by: stuefe, aboldtch
This commit is contained in:
parent
a53be204cb
commit
373e52c0ab
@ -627,6 +627,8 @@ class ArrayAllocator : public AllStatic {
|
||||
static E* allocate_malloc(size_t length, MEMFLAGS flags);
|
||||
static E* allocate_mmap(size_t length, MEMFLAGS flags);
|
||||
|
||||
static E* reallocate_malloc(E* addr, size_t new_length, MEMFLAGS flags);
|
||||
|
||||
static void free_malloc(E* addr, size_t length);
|
||||
static void free_mmap(E* addr, size_t length);
|
||||
|
||||
@ -656,6 +658,7 @@ class MallocArrayAllocator : public AllStatic {
|
||||
static size_t size_for(size_t length);
|
||||
|
||||
static E* allocate(size_t length, MEMFLAGS flags);
|
||||
static E* reallocate(E* addr, size_t new_length, MEMFLAGS flags);
|
||||
static void free(E* addr);
|
||||
};
|
||||
|
||||
|
@ -101,7 +101,12 @@ E* MallocArrayAllocator<E>::allocate(size_t length, MEMFLAGS flags) {
|
||||
return (E*)AllocateHeap(size_for(length), flags);
|
||||
}
|
||||
|
||||
template<class E>
|
||||
template <class E>
|
||||
E* MallocArrayAllocator<E>::reallocate(E* addr, size_t new_length, MEMFLAGS flags) {
|
||||
return (E*)ReallocateHeap((char*)addr, size_for(new_length), flags);
|
||||
}
|
||||
|
||||
template <class E>
|
||||
void MallocArrayAllocator<E>::free(E* addr) {
|
||||
FreeHeap(addr);
|
||||
}
|
||||
@ -130,8 +135,17 @@ E* ArrayAllocator<E>::allocate(size_t length, MEMFLAGS flags) {
|
||||
return allocate_mmap(length, flags);
|
||||
}
|
||||
|
||||
template <class E>
|
||||
E* ArrayAllocator<E>::reallocate_malloc(E* addr, size_t new_length, MEMFLAGS flags) {
|
||||
return MallocArrayAllocator<E>::reallocate(addr, new_length, flags);
|
||||
}
|
||||
|
||||
template <class E>
|
||||
E* ArrayAllocator<E>::reallocate(E* old_addr, size_t old_length, size_t new_length, MEMFLAGS flags) {
|
||||
if (should_use_malloc(old_length) && should_use_malloc(new_length)) {
|
||||
return reallocate_malloc(old_addr, new_length, flags);
|
||||
}
|
||||
|
||||
E* new_addr = (new_length > 0)
|
||||
? allocate(new_length, flags)
|
||||
: NULL;
|
||||
@ -147,17 +161,17 @@ E* ArrayAllocator<E>::reallocate(E* old_addr, size_t old_length, size_t new_leng
|
||||
return new_addr;
|
||||
}
|
||||
|
||||
template<class E>
|
||||
template <class E>
|
||||
void ArrayAllocator<E>::free_malloc(E* addr, size_t length) {
|
||||
MallocArrayAllocator<E>::free(addr);
|
||||
}
|
||||
|
||||
template<class E>
|
||||
template <class E>
|
||||
void ArrayAllocator<E>::free_mmap(E* addr, size_t length) {
|
||||
MmapArrayAllocator<E>::free(addr, length);
|
||||
}
|
||||
|
||||
template<class E>
|
||||
template <class E>
|
||||
void ArrayAllocator<E>::free(E* addr, size_t length) {
|
||||
if (addr != NULL) {
|
||||
if (should_use_malloc(length)) {
|
||||
|
@ -36,40 +36,69 @@ using idx_t = BitMap::idx_t;
|
||||
|
||||
STATIC_ASSERT(sizeof(bm_word_t) == BytesPerWord); // "Implementation assumption."
|
||||
|
||||
// For the BitMaps with allocators that don't support reallocate
|
||||
template <class BitMapWithAllocator>
|
||||
bm_word_t* GrowableBitMap<BitMapWithAllocator>::reallocate(bm_word_t* old_map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear) {
|
||||
size_t old_size_in_words = calc_size_in_words(old_size_in_bits);
|
||||
size_t new_size_in_words = calc_size_in_words(new_size_in_bits);
|
||||
|
||||
bm_word_t* map = NULL;
|
||||
BitMapWithAllocator* derived = static_cast<BitMapWithAllocator*>(this);
|
||||
|
||||
if (new_size_in_words > 0) {
|
||||
map = derived->allocate(new_size_in_words);
|
||||
|
||||
if (old_map != NULL) {
|
||||
Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) map,
|
||||
MIN2(old_size_in_words, new_size_in_words));
|
||||
}
|
||||
|
||||
if (clear && (new_size_in_bits > old_size_in_bits)) {
|
||||
// If old_size_in_bits is not word-aligned, then the preceding
|
||||
// copy can include some trailing bits in the final copied word
|
||||
// that also need to be cleared. See clear_range_within_word.
|
||||
bm_word_t mask = bit_mask(old_size_in_bits) - 1;
|
||||
map[raw_to_words_align_down(old_size_in_bits)] &= mask;
|
||||
// Clear the remaining full words.
|
||||
clear_range_of_words(map, old_size_in_words, new_size_in_words);
|
||||
}
|
||||
}
|
||||
static bm_word_t* pseudo_reallocate(const BitMapWithAllocator& derived, bm_word_t* old_map, size_t old_size_in_words, size_t new_size_in_words) {
|
||||
assert(new_size_in_words > 0, "precondition");
|
||||
|
||||
bm_word_t* map = derived.allocate(new_size_in_words);
|
||||
if (old_map != NULL) {
|
||||
derived->free(old_map, old_size_in_words);
|
||||
Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) map,
|
||||
MIN2(old_size_in_words, new_size_in_words));
|
||||
}
|
||||
|
||||
derived.free(old_map, old_size_in_words);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
template <class BitMapWithAllocator>
|
||||
void GrowableBitMap<BitMapWithAllocator>::initialize(idx_t size_in_bits, bool clear) {
|
||||
assert(map() == NULL, "precondition");
|
||||
assert(size() == 0, "precondition");
|
||||
|
||||
resize(size_in_bits, clear);
|
||||
}
|
||||
|
||||
template <class BitMapWithAllocator>
|
||||
void GrowableBitMap<BitMapWithAllocator>::reinitialize(idx_t new_size_in_bits, bool clear) {
|
||||
// Remove previous bits - no need to clear
|
||||
resize(0, false /* clear */);
|
||||
|
||||
initialize(new_size_in_bits, clear);
|
||||
}
|
||||
|
||||
template <class BitMapWithAllocator>
|
||||
void GrowableBitMap<BitMapWithAllocator>::resize(idx_t new_size_in_bits, bool clear) {
|
||||
const size_t old_size_in_bits = size();
|
||||
bm_word_t* const old_map = map();
|
||||
|
||||
const size_t old_size_in_words = calc_size_in_words(size());
|
||||
const size_t new_size_in_words = calc_size_in_words(new_size_in_bits);
|
||||
|
||||
BitMapWithAllocator* derived = static_cast<BitMapWithAllocator*>(this);
|
||||
|
||||
if (new_size_in_words == 0) {
|
||||
derived->free(old_map, old_size_in_words);
|
||||
update(NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bm_word_t* map = derived->reallocate(old_map, old_size_in_words, new_size_in_words);
|
||||
if (clear && (new_size_in_bits > old_size_in_bits)) {
|
||||
// If old_size_in_bits is not word-aligned, then the preceding
|
||||
// copy can include some trailing bits in the final copied word
|
||||
// that also need to be cleared. See clear_range_within_word.
|
||||
bm_word_t mask = bit_mask(old_size_in_bits) - 1;
|
||||
map[raw_to_words_align_down(old_size_in_bits)] &= mask;
|
||||
// Clear the remaining full words.
|
||||
clear_range_of_words(map, old_size_in_words, new_size_in_words);
|
||||
}
|
||||
|
||||
update(map, new_size_in_bits);
|
||||
}
|
||||
|
||||
ArenaBitMap::ArenaBitMap(Arena* arena, idx_t size_in_bits, bool clear)
|
||||
: GrowableBitMap<ArenaBitMap>(), _arena(arena) {
|
||||
initialize(size_in_bits, clear);
|
||||
@ -79,6 +108,10 @@ bm_word_t* ArenaBitMap::allocate(idx_t size_in_words) const {
|
||||
return (bm_word_t*)_arena->Amalloc(size_in_words * BytesPerWord);
|
||||
}
|
||||
|
||||
bm_word_t* ArenaBitMap::reallocate(bm_word_t* old_map, size_t old_size_in_words, size_t new_size_in_words) const {
|
||||
return pseudo_reallocate(*this, old_map, old_size_in_words, new_size_in_words);
|
||||
}
|
||||
|
||||
ResourceBitMap::ResourceBitMap(idx_t size_in_bits, bool clear)
|
||||
: GrowableBitMap<ResourceBitMap>() {
|
||||
initialize(size_in_bits, clear);
|
||||
@ -88,6 +121,10 @@ bm_word_t* ResourceBitMap::allocate(idx_t size_in_words) const {
|
||||
return (bm_word_t*)NEW_RESOURCE_ARRAY(bm_word_t, size_in_words);
|
||||
}
|
||||
|
||||
bm_word_t* ResourceBitMap::reallocate(bm_word_t* old_map, size_t old_size_in_words, size_t new_size_in_words) const {
|
||||
return pseudo_reallocate(*this, old_map, old_size_in_words, new_size_in_words);
|
||||
}
|
||||
|
||||
CHeapBitMap::CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags, bool clear)
|
||||
: GrowableBitMap<CHeapBitMap>(), _flags(flags) {
|
||||
initialize(size_in_bits, clear);
|
||||
@ -105,6 +142,10 @@ void CHeapBitMap::free(bm_word_t* map, idx_t size_in_words) const {
|
||||
ArrayAllocator<bm_word_t>::free(map, size_in_words);
|
||||
}
|
||||
|
||||
bm_word_t* CHeapBitMap::reallocate(bm_word_t* map, size_t old_size_in_words, size_t new_size_in_words) const {
|
||||
return ArrayAllocator<bm_word_t>::reallocate(map, old_size_in_words, new_size_in_words, _flags);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
void BitMap::verify_size(idx_t size_in_bits) {
|
||||
assert(size_in_bits <= max_size_in_bits(),
|
||||
|
@ -326,43 +326,28 @@ class BitMap {
|
||||
template <class BitMapWithAllocator>
|
||||
class GrowableBitMap : public BitMap {
|
||||
protected:
|
||||
bm_word_t* reallocate(bm_word_t* map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear);
|
||||
|
||||
GrowableBitMap() : GrowableBitMap(nullptr, 0) {}
|
||||
GrowableBitMap(bm_word_t* map, idx_t size_in_bits) : BitMap(map, size_in_bits) {}
|
||||
|
||||
public:
|
||||
// Set up and clear the bitmap memory.
|
||||
// Set up and optionally clear the bitmap memory.
|
||||
//
|
||||
// Precondition: The bitmap was default constructed and has
|
||||
// not yet had memory allocated via resize or (re)initialize.
|
||||
void initialize(idx_t size_in_bits, bool clear = true) {
|
||||
assert(map() == NULL, "precondition");
|
||||
assert(size() == 0, "precondition");
|
||||
void initialize(idx_t size_in_bits, bool clear = true);
|
||||
|
||||
resize(size_in_bits, clear);
|
||||
}
|
||||
|
||||
// Set up and clear the bitmap memory.
|
||||
// Set up and optionally clear the bitmap memory.
|
||||
//
|
||||
// Can be called on previously initialized bitmaps.
|
||||
void reinitialize(idx_t new_size_in_bits, bool clear = true) {
|
||||
// Remove previous bits - no need to clear
|
||||
resize(0, false /* clear */);
|
||||
|
||||
initialize(new_size_in_bits, clear);
|
||||
}
|
||||
void reinitialize(idx_t new_size_in_bits, bool clear = true);
|
||||
|
||||
// Protected functions, that are used by BitMap sub-classes that support them.
|
||||
|
||||
// Resize the backing bitmap memory.
|
||||
//
|
||||
// Old bits are transferred to the new memory
|
||||
// and the extended memory is cleared.
|
||||
void resize(idx_t new_size_in_bits, bool clear = true) {
|
||||
bm_word_t* new_map = reallocate(map(), size(), new_size_in_bits, clear);
|
||||
update(new_map, new_size_in_bits);
|
||||
}
|
||||
// and the extended memory is optionally cleared.
|
||||
void resize(idx_t new_size_in_bits, bool clear = true);
|
||||
};
|
||||
|
||||
// A concrete implementation of the "abstract" BitMap class.
|
||||
@ -381,12 +366,12 @@ class ArenaBitMap : public GrowableBitMap<ArenaBitMap> {
|
||||
NONCOPYABLE(ArenaBitMap);
|
||||
|
||||
public:
|
||||
// Clears the bitmap memory.
|
||||
ArenaBitMap(Arena* arena, idx_t size_in_bits, bool clear = true);
|
||||
|
||||
bm_word_t* allocate(idx_t size_in_words) const;
|
||||
bm_word_t* reallocate(bm_word_t* old_map, size_t old_size_in_words, size_t new_size_in_words) const;
|
||||
void free(bm_word_t* map, idx_t size_in_words) const {
|
||||
// ArenaBitMaps currently don't free memory.
|
||||
// ArenaBitMaps don't free memory.
|
||||
}
|
||||
};
|
||||
|
||||
@ -397,8 +382,9 @@ class ResourceBitMap : public GrowableBitMap<ResourceBitMap> {
|
||||
explicit ResourceBitMap(idx_t size_in_bits, bool clear = true);
|
||||
|
||||
bm_word_t* allocate(idx_t size_in_words) const;
|
||||
bm_word_t* reallocate(bm_word_t* old_map, size_t old_size_in_words, size_t new_size_in_words) const;
|
||||
void free(bm_word_t* map, idx_t size_in_words) const {
|
||||
// ResourceBitMaps currently don't free memory.
|
||||
// ResourceBitMaps don't free memory.
|
||||
}
|
||||
};
|
||||
|
||||
@ -417,6 +403,7 @@ class CHeapBitMap : public GrowableBitMap<CHeapBitMap> {
|
||||
~CHeapBitMap();
|
||||
|
||||
bm_word_t* allocate(idx_t size_in_words) const;
|
||||
bm_word_t* reallocate(bm_word_t* old_map, size_t old_size_in_words, size_t new_size_in_words) const;
|
||||
void free(bm_word_t* map, idx_t size_in_words) const;
|
||||
};
|
||||
|
||||
|
99
test/hotspot/gtest/memory/test_arrayAllocator.cpp
Normal file
99
test/hotspot/gtest/memory/test_arrayAllocator.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.inline.hpp"
|
||||
#include "unittest.hpp"
|
||||
|
||||
using Element = struct TestArrayAllocatorElement {
|
||||
double a;
|
||||
int b;
|
||||
};
|
||||
|
||||
static void fill(Element* elements, int start, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
new (&elements[start + i]) Element{0.0, start + i};
|
||||
}
|
||||
}
|
||||
|
||||
static Element* allocate_and_fill(int size) {
|
||||
Element* const elements = MallocArrayAllocator<Element>::allocate(size, mtTest);
|
||||
|
||||
fill(elements, 0, size);
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
TEST_VM(ArrayAllocator, allocate) {
|
||||
const int size = 10;
|
||||
|
||||
Element* const elements = allocate_and_fill(size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
ASSERT_EQ(elements[i].b, i);
|
||||
}
|
||||
|
||||
MallocArrayAllocator<Element>::free(elements);
|
||||
}
|
||||
|
||||
TEST_VM(ArrayAllocator, reallocate_0) {
|
||||
const int size = 10;
|
||||
|
||||
Element* const elements = allocate_and_fill(size);
|
||||
|
||||
Element* const ret = MallocArrayAllocator<Element>::reallocate(elements, 0, mtTest);
|
||||
ASSERT_NE(ret, nullptr) << "We've chosen to NOT return nullptr when reallcting with 0";
|
||||
|
||||
MallocArrayAllocator<Element>::free(ret);
|
||||
}
|
||||
|
||||
TEST_VM(ArrayAllocator, reallocate_shrink) {
|
||||
const int size = 10;
|
||||
|
||||
Element* const elements = allocate_and_fill(size);
|
||||
|
||||
Element* const ret = MallocArrayAllocator<Element>::reallocate(elements, size / 2, mtTest);
|
||||
|
||||
for (int i = 0; i < size / 2; i++) {
|
||||
ASSERT_EQ(ret[i].b, i);
|
||||
}
|
||||
|
||||
MallocArrayAllocator<Element>::free(ret);
|
||||
}
|
||||
|
||||
TEST_VM(ArrayAllocator, reallocate_grow) {
|
||||
const int size = 10;
|
||||
|
||||
Element* const elements = allocate_and_fill(size);
|
||||
|
||||
Element* const ret = MallocArrayAllocator<Element>::reallocate(elements, size * 2, mtTest);
|
||||
|
||||
fill(ret, size, size);
|
||||
|
||||
for (int i = 0; i < size * 2; i++) {
|
||||
ASSERT_EQ(ret[i].b, i);
|
||||
}
|
||||
|
||||
MallocArrayAllocator<Element>::free(ret);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user