Skip to content

Commit 57f8b91

Browse files
committed
8333658: NMT: Use an allocator with 4-byte pointers to save memory in NativeCallStackStorage
Reviewed-by: stuefe, azafari
1 parent 6c67933 commit 57f8b91

File tree

4 files changed

+339
-51
lines changed

4 files changed

+339
-51
lines changed
+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
23+
*/
24+
25+
#ifndef SHARE_NMT_ARRAYWITHFREELIST_HPP
26+
#define SHARE_NMT_ARRAYWITHFREELIST_HPP
27+
28+
#include "utilities/growableArray.hpp"
29+
#include <type_traits>
30+
31+
// A flat array of elements E, backed by C-heap, growing on-demand. It allows for
32+
// returning arbitrary elements and keeps them in a freelist. Elements can be uniquely
33+
// identified via array index.
34+
template<typename E, MEMFLAGS flag>
35+
class ArrayWithFreeList {
36+
37+
// An E must be trivially copyable and destructible, but it may be constructed
38+
// however it likes.
39+
constexpr void static_assert_E_satisfies_type_requirements() const {
40+
static_assert(std::is_trivially_copyable<E>::value && std::is_trivially_destructible<E>::value, "must be");
41+
}
42+
43+
public:
44+
using I = int32_t;
45+
static constexpr const I nil = -1;
46+
47+
private:
48+
// A free list allocator element is either a link to the next free space
49+
// or an actual element.
50+
union BackingElement {
51+
I link;
52+
E e;
53+
};
54+
55+
GrowableArrayCHeap<BackingElement, flag> _backing_storage;
56+
I _free_start;
57+
58+
bool is_in_bounds(I i) {
59+
return i >= 0 && i < _backing_storage.length();
60+
}
61+
62+
public:
63+
NONCOPYABLE(ArrayWithFreeList<E COMMA flag>);
64+
65+
ArrayWithFreeList(int initial_capacity = 8)
66+
: _backing_storage(initial_capacity),
67+
_free_start(nil) {}
68+
69+
template<typename... Args>
70+
I allocate(Args... args) {
71+
static_assert_E_satisfies_type_requirements();
72+
BackingElement* be;
73+
I i;
74+
if (_free_start != nil) {
75+
// Must point to already existing index
76+
be = &_backing_storage.at(_free_start);
77+
i = _free_start;
78+
_free_start = be->link;
79+
} else {
80+
// There are no free elements, allocate a new one.
81+
i = _backing_storage.append(BackingElement());
82+
be = _backing_storage.adr_at(i);
83+
}
84+
85+
::new (be) E{args...};
86+
return i;
87+
}
88+
89+
void deallocate(I i) {
90+
static_assert_E_satisfies_type_requirements();
91+
assert(i == nil || is_in_bounds(i), "out of bounds free");
92+
if (i == nil) return;
93+
BackingElement& be_freed = _backing_storage.at(i);
94+
be_freed.link = _free_start;
95+
_free_start = i;
96+
}
97+
98+
E& at(I i) {
99+
static_assert_E_satisfies_type_requirements();
100+
assert(i != nil, "null pointer dereference");
101+
assert(is_in_bounds(i), "out of bounds dereference");
102+
return _backing_storage.at(i).e;
103+
}
104+
};
105+
106+
#endif // SHARE_NMT_ARRAYWITHFREELIST_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#include "precompiled.hpp"
26+
#include "memory/allocation.hpp"
27+
#include "nmt/nmtNativeCallStackStorage.hpp"
28+
29+
NativeCallStackStorage::StackIndex NativeCallStackStorage::put(const NativeCallStack& value) {
30+
int bucket = value.calculate_hash() % _table_size;
31+
TableEntryIndex link = _table[bucket];
32+
while (link != TableEntryStorage::nil) {
33+
TableEntry& l = _entry_storage.at(link);
34+
if (value.equals(get(l.stack))) {
35+
return l.stack;
36+
}
37+
link = l.next;
38+
}
39+
int idx = _stacks.append(value);
40+
StackIndex si{idx};
41+
TableEntryIndex new_link = _entry_storage.allocate(_table[bucket], si);
42+
_table[bucket] = new_link;
43+
return si;
44+
}
45+
NativeCallStackStorage::NativeCallStackStorage(bool is_detailed_mode, int table_size)
46+
: _table_size(table_size),
47+
_table(nullptr),
48+
_stacks(),
49+
_is_detailed_mode(is_detailed_mode),
50+
_fake_stack() {
51+
if (_is_detailed_mode) {
52+
_table = NEW_C_HEAP_ARRAY(TableEntryIndex, _table_size, mtNMT);
53+
for (int i = 0; i < _table_size; i++) {
54+
_table[i] = TableEntryStorage::nil;
55+
}
56+
}
57+
}
58+
NativeCallStackStorage::~NativeCallStackStorage() {
59+
FREE_C_HEAP_ARRAY(LinkPtr, _table);
60+
}

src/hotspot/share/nmt/nmtNativeCallStackStorage.hpp

+20-51
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@
2525
#ifndef SHARE_NMT_NMTNATIVECALLSTACKSTORAGE_HPP
2626
#define SHARE_NMT_NMTNATIVECALLSTACKSTORAGE_HPP
2727

28-
#include "memory/allocation.hpp"
29-
#include "memory/arena.hpp"
28+
#include "nmt/arrayWithFreeList.hpp"
3029
#include "utilities/growableArray.hpp"
3130
#include "utilities/nativeCallStack.hpp"
3231

@@ -40,64 +39,41 @@
4039
// - Have fast comparisons
4140
// - Have constant time access
4241
// We achieve this by using a closed hashtable for finding previously existing NCS:s and referring to them by an index that's smaller than a pointer.
43-
class NativeCallStackStorage : public CHeapObj<mtNMT> {
42+
class NativeCallStackStorage : public CHeapObjBase {
4443
public:
4544
struct StackIndex {
4645
friend NativeCallStackStorage;
47-
48-
private:
49-
static constexpr const int32_t _invalid = -1;
50-
5146
int32_t _stack_index;
52-
StackIndex(int32_t stack_index)
53-
: _stack_index(stack_index) {
54-
}
55-
5647
public:
48+
static constexpr const int32_t invalid = -1;
5749
static bool equals(const StackIndex& a, const StackIndex& b) {
5850
return a._stack_index == b._stack_index;
5951
}
6052

6153
bool is_invalid() {
62-
return _stack_index == _invalid;
63-
}
64-
65-
StackIndex()
66-
: _stack_index(_invalid) {
54+
return _stack_index == invalid;
6755
}
6856
};
6957

7058
private:
71-
struct Link : public ArenaObj {
72-
Link* next;
59+
struct TableEntry;
60+
using TableEntryStorage = ArrayWithFreeList<TableEntry, mtNMT>;
61+
using TableEntryIndex = typename TableEntryStorage::I;
62+
63+
TableEntryStorage _entry_storage;
64+
65+
struct TableEntry {
66+
TableEntryIndex next;
7367
StackIndex stack;
74-
Link(Link* next, StackIndex v)
75-
: next(next),
76-
stack(v) {
77-
}
7868
};
79-
StackIndex put(const NativeCallStack& value) {
80-
int bucket = value.calculate_hash() % _table_size;
81-
Link* link = _table[bucket];
82-
while (link != nullptr) {
83-
if (value.equals(get(link->stack))) {
84-
return link->stack;
85-
}
86-
link = link->next;
87-
}
88-
int idx = _stacks.append(value);
89-
Link* new_link = new (&_arena) Link(_table[bucket], StackIndex(idx));
90-
_table[bucket] = new_link;
91-
return new_link->stack;
92-
}
9369

94-
// For storage of the Links
95-
Arena _arena;
70+
StackIndex put(const NativeCallStack& value);
71+
9672
// Pick a prime number of buckets.
9773
// 4099 gives a 50% probability of collisions at 76 stacks (as per birthday problem).
9874
static const constexpr int default_table_size = 4099;
99-
int _table_size;
100-
Link** _table;
75+
const int _table_size;
76+
TableEntryIndex* _table;
10177
GrowableArrayCHeap<NativeCallStack, mtNMT> _stacks;
10278
const bool _is_detailed_mode;
10379

@@ -107,7 +83,7 @@ class NativeCallStackStorage : public CHeapObj<mtNMT> {
10783
StackIndex push(const NativeCallStack& stack) {
10884
// Not in detailed mode, so not tracking stacks.
10985
if (!_is_detailed_mode) {
110-
return StackIndex();
86+
return StackIndex{StackIndex::invalid};
11187
}
11288
return put(stack);
11389
}
@@ -119,16 +95,9 @@ class NativeCallStackStorage : public CHeapObj<mtNMT> {
11995
return _stacks.at(si._stack_index);
12096
}
12197

122-
NativeCallStackStorage(bool is_detailed_mode, int table_size = default_table_size)
123-
: _arena(mtNMT), _table_size(table_size), _table(nullptr), _stacks(),
124-
_is_detailed_mode(is_detailed_mode), _fake_stack() {
125-
if (_is_detailed_mode) {
126-
_table = NEW_ARENA_ARRAY(&_arena, Link*, _table_size);
127-
for (int i = 0; i < _table_size; i++) {
128-
_table[i] = nullptr;
129-
}
130-
}
131-
}
98+
NativeCallStackStorage(bool is_detailed_mode, int table_size = default_table_size);
99+
100+
~NativeCallStackStorage();
132101
};
133102

134103
#endif // SHARE_NMT_NMTNATIVECALLSTACKSTORAGE_HPP

0 commit comments

Comments
 (0)