< prev index next >

src/hotspot/share/cds/aotStreamedHeapWriter.cpp

Print this page

  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:
< prev index next >