jdk

Форк
0
/
nmtPreInit.cpp 
243 строки · 8.4 Кб
1
/*
2
 * Copyright (c) 2022, 2023 SAP SE. All rights reserved.
3
 * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
4
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
 *
6
 * This code is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License version 2 only, as
8
 * published by the Free Software Foundation.
9
 *
10
 * This code is distributed in the hope that it will be useful, but WITHOUT
11
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13
 * version 2 for more details (a copy is included in the LICENSE file that
14
 * accompanied this code).
15
 *
16
 * You should have received a copy of the GNU General Public License version
17
 * 2 along with this work; if not, write to the Free Software Foundation,
18
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
 *
20
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
 * or visit www.oracle.com if you need additional information or have any
22
 * questions.
23
 *
24
 */
25

26
#include "precompiled.hpp"
27
#include "nmt/nmtPreInit.hpp"
28
#include "runtime/os.hpp"
29
#include "utilities/align.hpp"
30
#include "utilities/debug.hpp"
31
#include "utilities/globalDefinitions.hpp"
32
#include "utilities/ostream.hpp"
33

34
// Obviously we cannot use os::malloc for any dynamic allocation during pre-NMT-init, so we must use
35
// raw malloc; to make this very clear, wrap them.
36
static void* raw_malloc(size_t s)               { ALLOW_C_FUNCTION(::malloc, return ::malloc(s);) }
37
static void* raw_realloc(void* old, size_t s)   { ALLOW_C_FUNCTION(::realloc, return ::realloc(old, s);) }
38
static void  raw_free(void* p)                  { ALLOW_C_FUNCTION(::free, ::free(p);) }
39

40
// To keep matters simple we just raise a fatal error on OOM. Since preinit allocation
41
// is just used for pre-VM-initialization mallocs, none of which are optional, we don't
42
// need a finer grained error handling.
43

44
static void* raw_checked_malloc(size_t s) {
45
  void* p = raw_malloc(s);
46
  if (p == nullptr) {
47
    vm_exit_out_of_memory(s, OOM_MALLOC_ERROR, "VM early initialization phase");
48
  }
49
  return p;
50
}
51

52
static void* raw_checked_realloc(void* old, size_t s) {
53
  void* p = raw_realloc(old, s);
54
  if (p == nullptr) {
55
    vm_exit_out_of_memory(s, OOM_MALLOC_ERROR, "VM early initialization phase");
56
  }
57
  return p;
58
}
59

60
// --------- NMTPreInitAllocation --------------
61

62
void* NMTPreInitAllocation::operator new(size_t count) {
63
  return raw_checked_malloc(count);
64
}
65

66
void NMTPreInitAllocation::operator delete(void* p) {
67
  raw_free(p);
68
}
69

70
NMTPreInitAllocation* NMTPreInitAllocation::do_alloc(size_t payload_size) {
71
  void* payload = raw_checked_malloc(payload_size);
72
  NMTPreInitAllocation* a = new NMTPreInitAllocation(payload_size, payload);
73
  return a;
74
}
75

76
NMTPreInitAllocation* NMTPreInitAllocation::do_reallocate(NMTPreInitAllocation* a, size_t new_payload_size) {
77
  assert(a->next == nullptr, "unhang from map first");
78
  void* new_payload = raw_checked_realloc(a->payload, new_payload_size);
79
  NMTPreInitAllocation* a2 = new NMTPreInitAllocation(new_payload_size, new_payload);
80
  delete a;
81
  return a2;
82
}
83

84
void NMTPreInitAllocation::do_free(NMTPreInitAllocation* a) {
85
  assert(a->next == nullptr, "unhang from map first");
86
  raw_free(a->payload);
87
  delete a;
88
}
89

90
// --------- NMTPreInitAllocationTable --------------
91

92
void* NMTPreInitAllocationTable::operator new(size_t count) {
93
  return raw_checked_malloc(count);
94
}
95

96
void NMTPreInitAllocationTable::operator delete(void* p) {
97
  return raw_free(p);
98
}
99

100
NMTPreInitAllocationTable::NMTPreInitAllocationTable() {
101
  ::memset(_entries, 0, sizeof(_entries));
102
}
103

104
NMTPreInitAllocationTable::~NMTPreInitAllocationTable() {
105
  // clear LU entries, but let payloads live!
106
  for (int i = 0; i < table_size; i++) {
107
    NMTPreInitAllocation* a = _entries[i];
108
    while (a != nullptr) {
109
      NMTPreInitAllocation* a2 = a->next;
110
      delete a;
111
      a = a2;
112
    }
113
  }
114
}
115

116
// print a string describing the current state
117
void NMTPreInitAllocationTable::print_state(outputStream* st) const {
118
  // Collect some statistics and print them
119
  int num_entries = 0;
120
  int num_primary_entries = 0;
121
  int longest_chain = 0;
122
  size_t sum_bytes = 0;
123
  for (int i = 0; i < table_size; i++) {
124
    int chain_len = 0;
125
    for (NMTPreInitAllocation* a = _entries[i]; a != nullptr; a = a->next) {
126
      chain_len++;
127
      sum_bytes += a->size;
128
    }
129
    if (chain_len > 0) {
130
      num_primary_entries++;
131
    }
132
    num_entries += chain_len;
133
    longest_chain = MAX2(chain_len, longest_chain);
134
  }
135
  st->print("entries: %d (primary: %d, empties: %d), sum bytes: " SIZE_FORMAT
136
            ", longest chain length: %d",
137
            num_entries, num_primary_entries, table_size - num_primary_entries,
138
            sum_bytes, longest_chain);
139
}
140

141
#ifdef ASSERT
142
void NMTPreInitAllocationTable::print_map(outputStream* st) const {
143
  for (int i = 0; i < table_size; i++) {
144
    st->print("[%d]: ", i);
145
    for (NMTPreInitAllocation* a = _entries[i]; a != nullptr; a = a->next) {
146
      st->print( PTR_FORMAT "(" SIZE_FORMAT ") ", p2i(a->payload), a->size);
147
    }
148
    st->cr();
149
  }
150
}
151

152
void NMTPreInitAllocationTable::verify() const {
153
  // This verifies the buildup of the lookup table, including the load and the chain lengths.
154
  // We should see chain lens of 0-1 under normal conditions. Under artificial conditions
155
  // (20000 VM args) we should see maybe 6-7. From a certain length on we can be sure something
156
  // is broken.
157
  const int longest_acceptable_chain_len = 30;
158
  int num_chains_too_long = 0;
159
  for (index_t i = 0; i < table_size; i++) {
160
    int len = 0;
161
    for (const NMTPreInitAllocation* a = _entries[i]; a != nullptr; a = a->next) {
162
      index_t i2 = index_for_key(a->payload);
163
      assert(i2 == i, "wrong hash");
164
      assert(a->size > 0, "wrong size");
165
      len++;
166
      // very paranoid: search for dups
167
      bool found = false;
168
      for (const NMTPreInitAllocation* a2 = _entries[i]; a2 != nullptr; a2 = a2->next) {
169
        if (a == a2) {
170
          assert(!found, "dup!");
171
          found = true;
172
        }
173
      }
174
    }
175
    if (len > longest_acceptable_chain_len) {
176
      num_chains_too_long++;
177
    }
178
  }
179
  if (num_chains_too_long > 0) {
180
    assert(false, "NMT preinit lookup table degenerated (%d/%d chains longer than %d)",
181
                  num_chains_too_long, table_size, longest_acceptable_chain_len);
182
  }
183
}
184
#endif // ASSERT
185

186
// --------- NMTPreinit --------------
187

188
NMTPreInitAllocationTable* NMTPreInit::_table = nullptr;
189

190
// Some statistics
191
unsigned NMTPreInit::_num_mallocs_pre = 0;
192
unsigned NMTPreInit::_num_reallocs_pre = 0;
193
unsigned NMTPreInit::_num_frees_pre = 0;
194

195
void NMTPreInit::create_table() {
196
  assert(_table == nullptr, "just once");
197
  _table = new NMTPreInitAllocationTable;
198
}
199

200
// Allocate with os::malloc (hidden to prevent having to include os.hpp)
201
void* NMTPreInit::do_os_malloc(size_t size, MEMFLAGS memflags) {
202
  return os::malloc(size, memflags);
203
}
204

205
// Switches from NMT pre-init state to NMT post-init state;
206
//  in post-init, no modifications to the lookup table are possible.
207
void NMTPreInit::pre_to_post(bool nmt_off) {
208

209
  assert(!MemTracker::is_initialized(), "just once");
210
  DEBUG_ONLY(verify();)
211
  if (nmt_off) {
212
    // NMT is disabled.
213
    // Since neither pre- nor post-init-allocations use headers, from now on any pre-init allocation
214
    // can be handled directly by os::realloc or os::free.
215
    // We also can get rid of the lookup table.
216
    // Note that we deliberately leak the headers (NMTPreInitAllocation) in order to speed up startup.
217
    // That may leak about 12KB of memory for ~500 surviving pre-init allocations, which is a typical
218
    // number. This is a compromise to keep the coding simple and startup time short. It could very
219
    // easily improved by keeping a header pool, similar to metaspace ChunkHeaderPool. But since NMTPreInit
220
    // had been critizised as "too complicated", I try to keep things short and simple.
221
    delete _table;
222
    _table = nullptr;
223
  }
224
}
225

226
#ifdef ASSERT
227
void NMTPreInit::verify() {
228
  if (_table != nullptr) {
229
    _table->verify();
230
  }
231
  assert(_num_reallocs_pre <= _num_mallocs_pre &&
232
         _num_frees_pre <= _num_mallocs_pre, "stats are off");
233
}
234
#endif // ASSERT
235

236
void NMTPreInit::print_state(outputStream* st) {
237
  if (_table != nullptr) {
238
    _table->print_state(st);
239
    st->cr();
240
  }
241
  st->print_cr("pre-init mallocs: %u, pre-init reallocs: %u, pre-init frees: %u",
242
               _num_mallocs_pre, _num_reallocs_pre, _num_frees_pre);
243
}
244

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.