1 /*
2 * Copyright (c) 2025, 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 *
225 // Write a mapping from object index to buffer offset
226 for (int i = 1; i <= _source_objs->length(); i++) {
227 size_t buffer_offset = _dfs_to_archive_object_table[i];
228 write(buffer_offset);
229 }
230 }
231
232 void AOTStreamedHeapWriter::copy_roots_max_dfs_to_buffer(int roots_length) {
233 _root_highest_object_index_table_offset = _buffer_used;
234
235 for (int i = 0; i < roots_length; ++i) {
236 int highest_dfs = _roots_highest_dfs[i];
237 write(highest_dfs);
238 }
239
240 if ((roots_length % 2) != 0) {
241 write(-1); // Align up to a 64 bit word
242 }
243 }
244
245 static bool is_interned_string(oop obj) {
246 if (!java_lang_String::is_instance(obj)) {
247 return false;
248 }
249
250 ResourceMark rm;
251 int len;
252 jchar* name = java_lang_String::as_unicode_string_or_null(obj, len);
253 if (name == nullptr) {
254 fatal("Insufficient memory for dumping");
255 }
256 return StringTable::lookup(name, len) == obj;
257 }
258
259 static BitMap::idx_t bit_idx_for_buffer_offset(size_t buffer_offset) {
260 if (UseCompressedOops) {
261 return BitMap::idx_t(buffer_offset / sizeof(narrowOop));
262 } else {
263 return BitMap::idx_t(buffer_offset / sizeof(HeapWord));
264 }
265 }
266
267 bool AOTStreamedHeapWriter::is_dumped_interned_string(oop obj) {
268 return is_interned_string(obj) && HeapShared::get_cached_oop_info(obj) != nullptr;
269 }
270
271 void AOTStreamedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots) {
272 for (int i = 0; i < _source_objs->length(); i++) {
273 oop src_obj = _source_objs->at(i);
274 HeapShared::CachedOopInfo* info = HeapShared::get_cached_oop_info(src_obj);
275 assert(info != nullptr, "must be");
276 size_t buffer_offset = copy_one_source_obj_to_buffer(src_obj);
277 info->set_buffer_offset(buffer_offset);
278
279 OopHandle handle(Universe::vm_global(), src_obj);
280 _buffer_offset_to_source_obj_table->put_when_absent(buffer_offset, handle);
281 _buffer_offset_to_source_obj_table->maybe_grow();
282
283 int dfs_order = i + 1;
284 _dfs_to_archive_object_table[dfs_order] = buffer_offset;
285 }
286
287 copy_roots_to_buffer(roots);
288 copy_forwarding_to_buffer();
289 copy_roots_max_dfs_to_buffer(roots->length());
290
308 }
309
310 return !Klass::layout_helper_is_array(lh);
311 }
312
313 size_t AOTStreamedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
314 if (needs_explicit_size(src_obj)) {
315 // Explicitly write object size for more complex objects, to avoid having to
316 // pretend the buffer objects are objects when loading the objects, in order
317 // to read the size. Most of the time, the layout helper of the class is enough.
318 write<size_t>(src_obj->size());
319 }
320 size_t byte_size = src_obj->size() * HeapWordSize;
321 assert(byte_size > 0, "no zero-size objects");
322
323 size_t new_used = _buffer_used + byte_size;
324 assert(new_used > _buffer_used, "no wrap around");
325
326 ensure_buffer_space(new_used);
327
328 if (is_interned_string(src_obj)) {
329 java_lang_String::hash_code(src_obj); // Sets the hash code field(s)
330 java_lang_String::set_deduplication_forbidden(src_obj); // Allows faster interning at runtime
331 assert(java_lang_String::hash_is_set(src_obj), "hash must be set");
332 }
333
334 address from = cast_from_oop<address>(src_obj);
335 address to = offset_to_buffered_address<address>(_buffer_used);
336 assert(is_object_aligned(_buffer_used), "sanity");
337 assert(is_object_aligned(byte_size), "sanity");
338 memcpy(to, from, byte_size);
339
340 if (java_lang_Module::is_instance(src_obj)) {
341 // These native pointers will be restored explicitly at run time.
342 Modules::check_archived_module_oop(src_obj);
343 update_buffered_object_field<ModuleEntry*>(to, java_lang_Module::module_entry_offset(), nullptr);
344 } else if (java_lang_ClassLoader::is_instance(src_obj)) {
345 #ifdef ASSERT
346 // We only archive these loaders
347 if (src_obj != SystemDictionary::java_platform_loader() &&
348 src_obj != SystemDictionary::java_system_loader()) {
385
386 mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
387 }
388
389 void AOTStreamedHeapWriter::update_header_for_buffered_addr(address buffered_addr, oop src_obj, Klass* src_klass) {
390 assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses");
391 narrowKlass nk = ArchiveBuilder::current()->get_requested_narrow_klass(src_klass);
392
393 markWord mw = markWord::prototype();
394 oopDesc* fake_oop = (oopDesc*)buffered_addr;
395
396 // We need to retain the identity_hash, because it may have been used by some hashtables
397 // in the shared heap. This also has the side effect of pre-initializing the
398 // identity_hash for all shared objects, so they are less likely to be written
399 // into during run time, increasing the potential of memory sharing.
400 if (src_obj != nullptr) {
401 intptr_t src_hash = src_obj->identity_hash();
402 mw = mw.copy_set_hash(src_hash);
403 }
404
405 if (is_interned_string(src_obj)) {
406 // Mark the mark word of interned string so the loader knows to link these to
407 // the string table at runtime.
408 mw = mw.set_marked();
409 }
410
411 if (UseCompactObjectHeaders) {
412 fake_oop->set_mark(mw.set_narrow_klass(nk));
413 } else {
414 fake_oop->set_mark(mw);
415 fake_oop->set_narrow_klass(nk);
416 }
417 }
418
419 class AOTStreamedHeapWriter::EmbeddedOopMapper: public BasicOopIterateClosure {
420 oop _src_obj;
421 address _buffered_obj;
422 CHeapBitMap* _oopmap;
423 bool _is_java_lang_ref;
424
425 public:
|
1 /*
2 * Copyright (c) 2025, 2026, 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 *
225 // Write a mapping from object index to buffer offset
226 for (int i = 1; i <= _source_objs->length(); i++) {
227 size_t buffer_offset = _dfs_to_archive_object_table[i];
228 write(buffer_offset);
229 }
230 }
231
232 void AOTStreamedHeapWriter::copy_roots_max_dfs_to_buffer(int roots_length) {
233 _root_highest_object_index_table_offset = _buffer_used;
234
235 for (int i = 0; i < roots_length; ++i) {
236 int highest_dfs = _roots_highest_dfs[i];
237 write(highest_dfs);
238 }
239
240 if ((roots_length % 2) != 0) {
241 write(-1); // Align up to a 64 bit word
242 }
243 }
244
245 static BitMap::idx_t bit_idx_for_buffer_offset(size_t buffer_offset) {
246 if (UseCompressedOops) {
247 return BitMap::idx_t(buffer_offset / sizeof(narrowOop));
248 } else {
249 return BitMap::idx_t(buffer_offset / sizeof(HeapWord));
250 }
251 }
252
253 void AOTStreamedHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots) {
254 for (int i = 0; i < _source_objs->length(); i++) {
255 oop src_obj = _source_objs->at(i);
256 HeapShared::CachedOopInfo* info = HeapShared::get_cached_oop_info(src_obj);
257 assert(info != nullptr, "must be");
258 size_t buffer_offset = copy_one_source_obj_to_buffer(src_obj);
259 info->set_buffer_offset(buffer_offset);
260
261 OopHandle handle(Universe::vm_global(), src_obj);
262 _buffer_offset_to_source_obj_table->put_when_absent(buffer_offset, handle);
263 _buffer_offset_to_source_obj_table->maybe_grow();
264
265 int dfs_order = i + 1;
266 _dfs_to_archive_object_table[dfs_order] = buffer_offset;
267 }
268
269 copy_roots_to_buffer(roots);
270 copy_forwarding_to_buffer();
271 copy_roots_max_dfs_to_buffer(roots->length());
272
290 }
291
292 return !Klass::layout_helper_is_array(lh);
293 }
294
295 size_t AOTStreamedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
296 if (needs_explicit_size(src_obj)) {
297 // Explicitly write object size for more complex objects, to avoid having to
298 // pretend the buffer objects are objects when loading the objects, in order
299 // to read the size. Most of the time, the layout helper of the class is enough.
300 write<size_t>(src_obj->size());
301 }
302 size_t byte_size = src_obj->size() * HeapWordSize;
303 assert(byte_size > 0, "no zero-size objects");
304
305 size_t new_used = _buffer_used + byte_size;
306 assert(new_used > _buffer_used, "no wrap around");
307
308 ensure_buffer_space(new_used);
309
310 if (HeapShared::is_interned_string(src_obj)) {
311 java_lang_String::hash_code(src_obj); // Sets the hash code field(s)
312 java_lang_String::set_deduplication_forbidden(src_obj); // Allows faster interning at runtime
313 assert(java_lang_String::hash_is_set(src_obj), "hash must be set");
314 }
315
316 address from = cast_from_oop<address>(src_obj);
317 address to = offset_to_buffered_address<address>(_buffer_used);
318 assert(is_object_aligned(_buffer_used), "sanity");
319 assert(is_object_aligned(byte_size), "sanity");
320 memcpy(to, from, byte_size);
321
322 if (java_lang_Module::is_instance(src_obj)) {
323 // These native pointers will be restored explicitly at run time.
324 Modules::check_archived_module_oop(src_obj);
325 update_buffered_object_field<ModuleEntry*>(to, java_lang_Module::module_entry_offset(), nullptr);
326 } else if (java_lang_ClassLoader::is_instance(src_obj)) {
327 #ifdef ASSERT
328 // We only archive these loaders
329 if (src_obj != SystemDictionary::java_platform_loader() &&
330 src_obj != SystemDictionary::java_system_loader()) {
367
368 mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
369 }
370
371 void AOTStreamedHeapWriter::update_header_for_buffered_addr(address buffered_addr, oop src_obj, Klass* src_klass) {
372 assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses");
373 narrowKlass nk = ArchiveBuilder::current()->get_requested_narrow_klass(src_klass);
374
375 markWord mw = markWord::prototype();
376 oopDesc* fake_oop = (oopDesc*)buffered_addr;
377
378 // We need to retain the identity_hash, because it may have been used by some hashtables
379 // in the shared heap. This also has the side effect of pre-initializing the
380 // identity_hash for all shared objects, so they are less likely to be written
381 // into during run time, increasing the potential of memory sharing.
382 if (src_obj != nullptr) {
383 intptr_t src_hash = src_obj->identity_hash();
384 mw = mw.copy_set_hash(src_hash);
385 }
386
387 if (HeapShared::is_interned_string(src_obj)) {
388 // Mark the mark word of interned string so the loader knows to link these to
389 // the string table at runtime.
390 mw = mw.set_marked();
391 }
392
393 if (UseCompactObjectHeaders) {
394 fake_oop->set_mark(mw.set_narrow_klass(nk));
395 } else {
396 fake_oop->set_mark(mw);
397 fake_oop->set_narrow_klass(nk);
398 }
399 }
400
401 class AOTStreamedHeapWriter::EmbeddedOopMapper: public BasicOopIterateClosure {
402 oop _src_obj;
403 address _buffered_obj;
404 CHeapBitMap* _oopmap;
405 bool _is_java_lang_ref;
406
407 public:
|