| File: | jdk/src/hotspot/share/cds/heapShared.cpp |
| Warning: | line 1436, column 7 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | * Copyright (c) 2018, 2021, 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 "cds/archiveBuilder.hpp" | |||
| 27 | #include "cds/archiveUtils.hpp" | |||
| 28 | #include "cds/filemap.hpp" | |||
| 29 | #include "cds/heapShared.inline.hpp" | |||
| 30 | #include "cds/metaspaceShared.hpp" | |||
| 31 | #include "classfile/classLoaderData.hpp" | |||
| 32 | #include "classfile/classLoaderDataShared.hpp" | |||
| 33 | #include "classfile/javaClasses.inline.hpp" | |||
| 34 | #include "classfile/moduleEntry.hpp" | |||
| 35 | #include "classfile/stringTable.hpp" | |||
| 36 | #include "classfile/symbolTable.hpp" | |||
| 37 | #include "classfile/systemDictionary.hpp" | |||
| 38 | #include "classfile/systemDictionaryShared.hpp" | |||
| 39 | #include "classfile/vmClasses.hpp" | |||
| 40 | #include "classfile/vmSymbols.hpp" | |||
| 41 | #include "gc/shared/collectedHeap.hpp" | |||
| 42 | #include "gc/shared/gcLocker.hpp" | |||
| 43 | #include "gc/shared/gcVMOperations.hpp" | |||
| 44 | #include "logging/log.hpp" | |||
| 45 | #include "logging/logMessage.hpp" | |||
| 46 | #include "logging/logStream.hpp" | |||
| 47 | #include "memory/iterator.inline.hpp" | |||
| 48 | #include "memory/metadataFactory.hpp" | |||
| 49 | #include "memory/metaspaceClosure.hpp" | |||
| 50 | #include "memory/resourceArea.hpp" | |||
| 51 | #include "memory/universe.hpp" | |||
| 52 | #include "oops/compressedOops.inline.hpp" | |||
| 53 | #include "oops/fieldStreams.inline.hpp" | |||
| 54 | #include "oops/objArrayOop.hpp" | |||
| 55 | #include "oops/oop.inline.hpp" | |||
| 56 | #include "prims/jvmtiExport.hpp" | |||
| 57 | #include "runtime/fieldDescriptor.inline.hpp" | |||
| 58 | #include "runtime/globals_extension.hpp" | |||
| 59 | #include "runtime/init.hpp" | |||
| 60 | #include "runtime/java.hpp" | |||
| 61 | #include "runtime/javaCalls.hpp" | |||
| 62 | #include "runtime/safepointVerifiers.hpp" | |||
| 63 | #include "utilities/bitMap.inline.hpp" | |||
| 64 | #include "utilities/copy.hpp" | |||
| 65 | #if INCLUDE_G1GC1 | |||
| 66 | #include "gc/g1/g1CollectedHeap.hpp" | |||
| 67 | #endif | |||
| 68 | ||||
| 69 | #if INCLUDE_CDS_JAVA_HEAP1 | |||
| 70 | ||||
| 71 | bool HeapShared::_closed_regions_mapped = false; | |||
| 72 | bool HeapShared::_open_regions_mapped = false; | |||
| 73 | bool HeapShared::_is_loaded = false; | |||
| 74 | bool HeapShared::_disable_writing = false; | |||
| 75 | address HeapShared::_narrow_oop_base; | |||
| 76 | int HeapShared::_narrow_oop_shift; | |||
| 77 | DumpedInternedStrings *HeapShared::_dumped_interned_strings = NULL__null; | |||
| 78 | ||||
| 79 | uintptr_t HeapShared::_loaded_heap_bottom = 0; | |||
| 80 | uintptr_t HeapShared::_loaded_heap_top = 0; | |||
| 81 | uintptr_t HeapShared::_dumptime_base_0 = UINTPTR_MAX(18446744073709551615UL); | |||
| 82 | uintptr_t HeapShared::_dumptime_base_1 = UINTPTR_MAX(18446744073709551615UL); | |||
| 83 | uintptr_t HeapShared::_dumptime_base_2 = UINTPTR_MAX(18446744073709551615UL); | |||
| 84 | uintptr_t HeapShared::_dumptime_base_3 = UINTPTR_MAX(18446744073709551615UL); | |||
| 85 | uintptr_t HeapShared::_dumptime_top = 0; | |||
| 86 | intx HeapShared::_runtime_offset_0 = 0; | |||
| 87 | intx HeapShared::_runtime_offset_1 = 0; | |||
| 88 | intx HeapShared::_runtime_offset_2 = 0; | |||
| 89 | intx HeapShared::_runtime_offset_3 = 0; | |||
| 90 | bool HeapShared::_loading_failed = false; | |||
| 91 | // | |||
| 92 | // If you add new entries to the following tables, you should know what you're doing! | |||
| 93 | // | |||
| 94 | ||||
| 95 | // Entry fields for shareable subgraphs archived in the closed archive heap | |||
| 96 | // region. Warning: Objects in the subgraphs should not have reference fields | |||
| 97 | // assigned at runtime. | |||
| 98 | static ArchivableStaticFieldInfo closed_archive_subgraph_entry_fields[] = { | |||
| 99 | {"java/lang/Integer$IntegerCache", "archivedCache"}, | |||
| 100 | {"java/lang/Long$LongCache", "archivedCache"}, | |||
| 101 | {"java/lang/Byte$ByteCache", "archivedCache"}, | |||
| 102 | {"java/lang/Short$ShortCache", "archivedCache"}, | |||
| 103 | {"java/lang/Character$CharacterCache", "archivedCache"}, | |||
| 104 | {"java/util/jar/Attributes$Name", "KNOWN_NAMES"}, | |||
| 105 | {"sun/util/locale/BaseLocale", "constantBaseLocales"}, | |||
| 106 | }; | |||
| 107 | // Entry fields for subgraphs archived in the open archive heap region. | |||
| 108 | static ArchivableStaticFieldInfo open_archive_subgraph_entry_fields[] = { | |||
| 109 | {"jdk/internal/module/ArchivedModuleGraph", "archivedModuleGraph"}, | |||
| 110 | {"java/util/ImmutableCollections", "archivedObjects"}, | |||
| 111 | {"java/lang/ModuleLayer", "EMPTY_LAYER"}, | |||
| 112 | {"java/lang/module/Configuration", "EMPTY_CONFIGURATION"}, | |||
| 113 | {"jdk/internal/math/FDBigInteger", "archivedCaches"}, | |||
| 114 | }; | |||
| 115 | ||||
| 116 | // Entry fields for subgraphs archived in the open archive heap region (full module graph). | |||
| 117 | static ArchivableStaticFieldInfo fmg_open_archive_subgraph_entry_fields[] = { | |||
| 118 | {"jdk/internal/loader/ArchivedClassLoaders", "archivedClassLoaders"}, | |||
| 119 | {"jdk/internal/module/ArchivedBootLayer", "archivedBootLayer"}, | |||
| 120 | {"java/lang/Module$ArchivedData", "archivedData"}, | |||
| 121 | }; | |||
| 122 | ||||
| 123 | const static int num_closed_archive_subgraph_entry_fields = | |||
| 124 | sizeof(closed_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo); | |||
| 125 | const static int num_open_archive_subgraph_entry_fields = | |||
| 126 | sizeof(open_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo); | |||
| 127 | const static int num_fmg_open_archive_subgraph_entry_fields = | |||
| 128 | sizeof(fmg_open_archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo); | |||
| 129 | ||||
| 130 | GrowableArrayCHeap<oop, mtClassShared>* HeapShared::_pending_roots = NULL__null; | |||
| 131 | narrowOop HeapShared::_roots_narrow; | |||
| 132 | OopHandle HeapShared::_roots; | |||
| 133 | ||||
| 134 | #ifdef ASSERT1 | |||
| 135 | bool HeapShared::is_archived_object_during_dumptime(oop p) { | |||
| 136 | assert(HeapShared::can_write(), "must be")do { if (!(HeapShared::can_write())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 136, "assert(" "HeapShared::can_write()" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 137 | assert(DumpSharedSpaces, "this function is only used with -Xshare:dump")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 137, "assert(" "DumpSharedSpaces" ") failed", "this function is only used with -Xshare:dump" ); ::breakpoint(); } } while (0); | |||
| 138 | return Universe::heap()->is_archived_object(p); | |||
| 139 | } | |||
| 140 | #endif | |||
| 141 | ||||
| 142 | //////////////////////////////////////////////////////////////// | |||
| 143 | // | |||
| 144 | // Java heap object archiving support | |||
| 145 | // | |||
| 146 | //////////////////////////////////////////////////////////////// | |||
| 147 | void HeapShared::fixup_regions() { | |||
| 148 | FileMapInfo* mapinfo = FileMapInfo::current_info(); | |||
| 149 | if (is_mapped()) { | |||
| 150 | mapinfo->fixup_mapped_heap_regions(); | |||
| 151 | } else if (_loading_failed) { | |||
| 152 | fill_failed_loaded_region(); | |||
| 153 | } | |||
| 154 | if (is_fully_available()) { | |||
| 155 | _roots = OopHandle(Universe::vm_global(), decode_from_archive(_roots_narrow)); | |||
| 156 | if (!MetaspaceShared::use_full_module_graph()) { | |||
| 157 | // Need to remove all the archived java.lang.Module objects from HeapShared::roots(). | |||
| 158 | ClassLoaderDataShared::clear_archived_oops(); | |||
| 159 | } | |||
| 160 | } | |||
| 161 | SystemDictionaryShared::update_archived_mirror_native_pointers(); | |||
| 162 | } | |||
| 163 | ||||
| 164 | unsigned HeapShared::oop_hash(oop const& p) { | |||
| 165 | unsigned hash = (unsigned)p->identity_hash(); | |||
| 166 | return hash; | |||
| 167 | } | |||
| 168 | ||||
| 169 | static void reset_states(oop obj, TRAPSJavaThread* __the_thread__) { | |||
| 170 | Handle h_obj(THREAD__the_thread__, obj); | |||
| 171 | InstanceKlass* klass = InstanceKlass::cast(obj->klass()); | |||
| 172 | TempNewSymbol method_name = SymbolTable::new_symbol("resetArchivedStates"); | |||
| 173 | Symbol* method_sig = vmSymbols::void_method_signature(); | |||
| 174 | ||||
| 175 | while (klass != NULL__null) { | |||
| 176 | Method* method = klass->find_method(method_name, method_sig); | |||
| 177 | if (method != NULL__null) { | |||
| 178 | assert(method->is_private(), "must be")do { if (!(method->is_private())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 178, "assert(" "method->is_private()" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 179 | if (log_is_enabled(Debug, cds)(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) { | |||
| 180 | ResourceMark rm(THREAD__the_thread__); | |||
| 181 | log_debug(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>(" calling %s", method->name_and_sig_as_C_string()); | |||
| 182 | } | |||
| 183 | JavaValue result(T_VOID); | |||
| 184 | JavaCalls::call_special(&result, h_obj, klass, | |||
| 185 | method_name, method_sig, CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 186 | } | |||
| 187 | klass = klass->java_super(); | |||
| 188 | } | |||
| 189 | } | |||
| 190 | ||||
| 191 | void HeapShared::reset_archived_object_states(TRAPSJavaThread* __the_thread__) { | |||
| 192 | assert(DumpSharedSpaces, "dump-time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 192, "assert(" "DumpSharedSpaces" ") failed", "dump-time only" ); ::breakpoint(); } } while (0); | |||
| 193 | log_debug(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("Resetting platform loader"); | |||
| 194 | reset_states(SystemDictionary::java_platform_loader(), CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 195 | log_debug(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("Resetting system loader"); | |||
| 196 | reset_states(SystemDictionary::java_system_loader(), CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 197 | } | |||
| 198 | ||||
| 199 | HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = NULL__null; | |||
| 200 | oop HeapShared::find_archived_heap_object(oop obj) { | |||
| 201 | assert(DumpSharedSpaces, "dump-time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 201, "assert(" "DumpSharedSpaces" ") failed", "dump-time only" ); ::breakpoint(); } } while (0); | |||
| 202 | ArchivedObjectCache* cache = archived_object_cache(); | |||
| 203 | oop* p = cache->get(obj); | |||
| 204 | if (p != NULL__null) { | |||
| 205 | return *p; | |||
| 206 | } else { | |||
| 207 | return NULL__null; | |||
| 208 | } | |||
| 209 | } | |||
| 210 | ||||
| 211 | int HeapShared::append_root(oop obj) { | |||
| 212 | assert(DumpSharedSpaces, "dump-time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 212, "assert(" "DumpSharedSpaces" ") failed", "dump-time only" ); ::breakpoint(); } } while (0); | |||
| 213 | ||||
| 214 | // No GC should happen since we aren't scanning _pending_roots. | |||
| 215 | assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread")do { if (!(Thread::current() == (Thread*)VMThread::vm_thread( ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 215, "assert(" "Thread::current() == (Thread*)VMThread::vm_thread()" ") failed", "should be in vm thread"); ::breakpoint(); } } while (0); | |||
| 216 | ||||
| 217 | if (_pending_roots == NULL__null) { | |||
| 218 | _pending_roots = new GrowableArrayCHeap<oop, mtClassShared>(500); | |||
| 219 | } | |||
| 220 | ||||
| 221 | return _pending_roots->append(obj); | |||
| 222 | } | |||
| 223 | ||||
| 224 | objArrayOop HeapShared::roots() { | |||
| 225 | if (DumpSharedSpaces) { | |||
| 226 | assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread")do { if (!(Thread::current() == (Thread*)VMThread::vm_thread( ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 226, "assert(" "Thread::current() == (Thread*)VMThread::vm_thread()" ") failed", "should be in vm thread"); ::breakpoint(); } } while (0); | |||
| 227 | if (!HeapShared::can_write()) { | |||
| 228 | return NULL__null; | |||
| 229 | } | |||
| 230 | } else { | |||
| 231 | assert(UseSharedSpaces, "must be")do { if (!(UseSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 231, "assert(" "UseSharedSpaces" ") failed", "must be"); :: breakpoint(); } } while (0); | |||
| 232 | } | |||
| 233 | ||||
| 234 | objArrayOop roots = (objArrayOop)_roots.resolve(); | |||
| 235 | assert(roots != NULL, "should have been initialized")do { if (!(roots != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 235, "assert(" "roots != __null" ") failed", "should have been initialized" ); ::breakpoint(); } } while (0); | |||
| 236 | return roots; | |||
| 237 | } | |||
| 238 | ||||
| 239 | void HeapShared::set_roots(narrowOop roots) { | |||
| 240 | assert(UseSharedSpaces, "runtime only")do { if (!(UseSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 240, "assert(" "UseSharedSpaces" ") failed", "runtime only" ); ::breakpoint(); } } while (0); | |||
| 241 | assert(is_fully_available(), "must be")do { if (!(is_fully_available())) { (*g_assert_poison) = 'X'; ; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 241, "assert(" "is_fully_available()" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 242 | _roots_narrow = roots; | |||
| 243 | } | |||
| 244 | ||||
| 245 | // Returns an objArray that contains all the roots of the archived objects | |||
| 246 | oop HeapShared::get_root(int index, bool clear) { | |||
| 247 | assert(index >= 0, "sanity")do { if (!(index >= 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 247, "assert(" "index >= 0" ") failed", "sanity"); ::breakpoint (); } } while (0); | |||
| 248 | if (DumpSharedSpaces) { | |||
| 249 | assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread")do { if (!(Thread::current() == (Thread*)VMThread::vm_thread( ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 249, "assert(" "Thread::current() == (Thread*)VMThread::vm_thread()" ") failed", "should be in vm thread"); ::breakpoint(); } } while (0); | |||
| 250 | assert(_pending_roots != NULL, "sanity")do { if (!(_pending_roots != __null)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 250, "assert(" "_pending_roots != __null" ") failed", "sanity" ); ::breakpoint(); } } while (0); | |||
| 251 | return _pending_roots->at(index); | |||
| 252 | } else { | |||
| 253 | assert(UseSharedSpaces, "must be")do { if (!(UseSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 253, "assert(" "UseSharedSpaces" ") failed", "must be"); :: breakpoint(); } } while (0); | |||
| 254 | assert(!_roots.is_empty(), "must have loaded shared heap")do { if (!(!_roots.is_empty())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 254, "assert(" "!_roots.is_empty()" ") failed", "must have loaded shared heap" ); ::breakpoint(); } } while (0); | |||
| 255 | oop result = roots()->obj_at(index); | |||
| 256 | if (clear) { | |||
| 257 | clear_root(index); | |||
| 258 | } | |||
| 259 | return result; | |||
| 260 | } | |||
| 261 | } | |||
| 262 | ||||
| 263 | void HeapShared::clear_root(int index) { | |||
| 264 | assert(index >= 0, "sanity")do { if (!(index >= 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 264, "assert(" "index >= 0" ") failed", "sanity"); ::breakpoint (); } } while (0); | |||
| 265 | assert(UseSharedSpaces, "must be")do { if (!(UseSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 265, "assert(" "UseSharedSpaces" ") failed", "must be"); :: breakpoint(); } } while (0); | |||
| 266 | if (is_fully_available()) { | |||
| 267 | if (log_is_enabled(Debug, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) { | |||
| 268 | oop old = roots()->obj_at(index); | |||
| 269 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("Clearing root %d: was " PTR_FORMAT"0x%016" "l" "x", index, p2i(old)); | |||
| 270 | } | |||
| 271 | roots()->obj_at_put(index, NULL__null); | |||
| 272 | } | |||
| 273 | } | |||
| 274 | ||||
| 275 | oop HeapShared::archive_object(oop obj) { | |||
| 276 | assert(DumpSharedSpaces, "dump-time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 276, "assert(" "DumpSharedSpaces" ") failed", "dump-time only" ); ::breakpoint(); } } while (0); | |||
| 277 | ||||
| 278 | oop ao = find_archived_heap_object(obj); | |||
| 279 | if (ao != NULL__null) { | |||
| 280 | // already archived | |||
| 281 | return ao; | |||
| 282 | } | |||
| 283 | ||||
| 284 | int len = obj->size(); | |||
| 285 | if (G1CollectedHeap::heap()->is_archive_alloc_too_large(len)) { | |||
| 286 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("Cannot archive, object (" PTR_FORMAT"0x%016" "l" "x" ") is too large: " SIZE_FORMAT"%" "l" "u", | |||
| 287 | p2i(obj), (size_t)obj->size()); | |||
| 288 | return NULL__null; | |||
| 289 | } | |||
| 290 | ||||
| 291 | oop archived_oop = cast_to_oop(G1CollectedHeap::heap()->archive_mem_allocate(len)); | |||
| 292 | if (archived_oop != NULL__null) { | |||
| 293 | Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(obj), cast_from_oop<HeapWord*>(archived_oop), len); | |||
| 294 | // Reinitialize markword to remove age/marking/locking/etc. | |||
| 295 | // | |||
| 296 | // We need to retain the identity_hash, because it may have been used by some hashtables | |||
| 297 | // in the shared heap. This also has the side effect of pre-initializing the | |||
| 298 | // identity_hash for all shared objects, so they are less likely to be written | |||
| 299 | // into during run time, increasing the potential of memory sharing. | |||
| 300 | int hash_original = obj->identity_hash(); | |||
| 301 | archived_oop->set_mark(markWord::prototype().copy_set_hash(hash_original)); | |||
| 302 | assert(archived_oop->mark().is_unlocked(), "sanity")do { if (!(archived_oop->mark().is_unlocked())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 302, "assert(" "archived_oop->mark().is_unlocked()" ") failed" , "sanity"); ::breakpoint(); } } while (0); | |||
| 303 | ||||
| 304 | DEBUG_ONLY(int hash_archived = archived_oop->identity_hash())int hash_archived = archived_oop->identity_hash(); | |||
| 305 | assert(hash_original == hash_archived, "Different hash codes: original %x, archived %x", hash_original, hash_archived)do { if (!(hash_original == hash_archived)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 305, "assert(" "hash_original == hash_archived" ") failed", "Different hash codes: original %x, archived %x", hash_original , hash_archived); ::breakpoint(); } } while (0); | |||
| 306 | ||||
| 307 | ArchivedObjectCache* cache = archived_object_cache(); | |||
| 308 | cache->put(obj, archived_oop); | |||
| 309 | if (log_is_enabled(Debug, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) { | |||
| 310 | ResourceMark rm; | |||
| 311 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("Archived heap object " PTR_FORMAT"0x%016" "l" "x" " ==> " PTR_FORMAT"0x%016" "l" "x" " : %s", | |||
| 312 | p2i(obj), p2i(archived_oop), obj->klass()->external_name()); | |||
| 313 | } | |||
| 314 | } else { | |||
| 315 | log_error(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>( | |||
| 316 | "Cannot allocate space for object " PTR_FORMAT"0x%016" "l" "x" " in archived heap region", | |||
| 317 | p2i(obj)); | |||
| 318 | vm_direct_exit(-1, | |||
| 319 | err_msg("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " | |||
| 320 | SIZE_FORMAT"%" "l" "u" "M", MaxHeapSize/M)); | |||
| 321 | } | |||
| 322 | return archived_oop; | |||
| 323 | } | |||
| 324 | ||||
| 325 | void HeapShared::archive_klass_objects() { | |||
| 326 | GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses(); | |||
| 327 | assert(klasses != NULL, "sanity")do { if (!(klasses != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 327, "assert(" "klasses != __null" ") failed", "sanity"); :: breakpoint(); } } while (0); | |||
| 328 | for (int i = 0; i < klasses->length(); i++) { | |||
| 329 | Klass* k = ArchiveBuilder::get_relocated_klass(klasses->at(i)); | |||
| 330 | ||||
| 331 | // archive mirror object | |||
| 332 | java_lang_Class::archive_mirror(k); | |||
| 333 | ||||
| 334 | // archive the resolved_referenes array | |||
| 335 | if (k->is_instance_klass()) { | |||
| 336 | InstanceKlass* ik = InstanceKlass::cast(k); | |||
| 337 | ik->constants()->archive_resolved_references(); | |||
| 338 | } | |||
| 339 | } | |||
| 340 | } | |||
| 341 | ||||
| 342 | void HeapShared::run_full_gc_in_vm_thread() { | |||
| 343 | if (HeapShared::can_write()) { | |||
| 344 | // Avoid fragmentation while archiving heap objects. | |||
| 345 | // We do this inside a safepoint, so that no further allocation can happen after GC | |||
| 346 | // has finished. | |||
| 347 | if (GCLocker::is_active()) { | |||
| 348 | // Just checking for safety ... | |||
| 349 | // This should not happen during -Xshare:dump. If you see this, probably the Java core lib | |||
| 350 | // has been modified such that JNI code is executed in some clean up threads after | |||
| 351 | // we have finished class loading. | |||
| 352 | log_warning(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Warning))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Warning>("GC locker is held, unable to start extra compacting GC. This may produce suboptimal results."); | |||
| 353 | } else { | |||
| 354 | log_info(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Run GC ..."); | |||
| 355 | Universe::heap()->collect_as_vm_thread(GCCause::_archive_time_gc); | |||
| 356 | log_info(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Run GC done"); | |||
| 357 | } | |||
| 358 | } | |||
| 359 | } | |||
| 360 | ||||
| 361 | void HeapShared::archive_objects(GrowableArray<MemRegion>* closed_regions, | |||
| 362 | GrowableArray<MemRegion>* open_regions) { | |||
| 363 | ||||
| 364 | G1HeapVerifier::verify_ready_for_archiving(); | |||
| 365 | ||||
| 366 | { | |||
| 367 | NoSafepointVerifier nsv; | |||
| 368 | ||||
| 369 | // Cache for recording where the archived objects are copied to | |||
| 370 | create_archived_object_cache(); | |||
| 371 | ||||
| 372 | log_info(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Heap range = [" PTR_FORMAT"0x%016" "l" "x" " - " PTR_FORMAT"0x%016" "l" "x" "]", | |||
| 373 | p2i(CompressedOops::begin()), p2i(CompressedOops::end())); | |||
| 374 | log_info(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Dumping objects to closed archive heap region ..."); | |||
| 375 | copy_closed_objects(closed_regions); | |||
| 376 | ||||
| 377 | log_info(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Dumping objects to open archive heap region ..."); | |||
| 378 | copy_open_objects(open_regions); | |||
| 379 | ||||
| 380 | destroy_archived_object_cache(); | |||
| 381 | } | |||
| 382 | ||||
| 383 | G1HeapVerifier::verify_archive_regions(); | |||
| 384 | } | |||
| 385 | ||||
| 386 | void HeapShared::copy_closed_objects(GrowableArray<MemRegion>* closed_regions) { | |||
| 387 | assert(HeapShared::can_write(), "must be")do { if (!(HeapShared::can_write())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 387, "assert(" "HeapShared::can_write()" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 388 | ||||
| 389 | G1CollectedHeap::heap()->begin_archive_alloc_range(); | |||
| 390 | ||||
| 391 | // Archive interned string objects | |||
| 392 | StringTable::write_to_archive(_dumped_interned_strings); | |||
| 393 | ||||
| 394 | archive_object_subgraphs(closed_archive_subgraph_entry_fields, | |||
| 395 | num_closed_archive_subgraph_entry_fields, | |||
| 396 | true /* is_closed_archive */, | |||
| 397 | false /* is_full_module_graph */); | |||
| 398 | ||||
| 399 | G1CollectedHeap::heap()->end_archive_alloc_range(closed_regions, | |||
| 400 | os::vm_allocation_granularity()); | |||
| 401 | } | |||
| 402 | ||||
| 403 | void HeapShared::copy_open_objects(GrowableArray<MemRegion>* open_regions) { | |||
| 404 | assert(HeapShared::can_write(), "must be")do { if (!(HeapShared::can_write())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 404, "assert(" "HeapShared::can_write()" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 405 | ||||
| 406 | G1CollectedHeap::heap()->begin_archive_alloc_range(true /* open */); | |||
| 407 | ||||
| 408 | java_lang_Class::archive_basic_type_mirrors(); | |||
| 409 | ||||
| 410 | archive_klass_objects(); | |||
| 411 | ||||
| 412 | archive_object_subgraphs(open_archive_subgraph_entry_fields, | |||
| 413 | num_open_archive_subgraph_entry_fields, | |||
| 414 | false /* is_closed_archive */, | |||
| 415 | false /* is_full_module_graph */); | |||
| 416 | if (MetaspaceShared::use_full_module_graph()) { | |||
| 417 | archive_object_subgraphs(fmg_open_archive_subgraph_entry_fields, | |||
| 418 | num_fmg_open_archive_subgraph_entry_fields, | |||
| 419 | false /* is_closed_archive */, | |||
| 420 | true /* is_full_module_graph */); | |||
| 421 | ClassLoaderDataShared::init_archived_oops(); | |||
| 422 | } | |||
| 423 | ||||
| 424 | copy_roots(); | |||
| 425 | ||||
| 426 | G1CollectedHeap::heap()->end_archive_alloc_range(open_regions, | |||
| 427 | os::vm_allocation_granularity()); | |||
| 428 | } | |||
| 429 | ||||
| 430 | // Copy _pending_archive_roots into an objArray | |||
| 431 | void HeapShared::copy_roots() { | |||
| 432 | int length = _pending_roots != NULL__null ? _pending_roots->length() : 0; | |||
| 433 | size_t size = objArrayOopDesc::object_size(length); | |||
| 434 | Klass* k = Universe::objectArrayKlassObj(); // already relocated to point to archived klass | |||
| 435 | HeapWord* mem = G1CollectedHeap::heap()->archive_mem_allocate(size); | |||
| 436 | ||||
| 437 | memset(mem, 0, size * BytesPerWord); | |||
| 438 | { | |||
| 439 | // This is copied from MemAllocator::finish | |||
| 440 | oopDesc::set_mark(mem, markWord::prototype()); | |||
| 441 | oopDesc::release_set_klass(mem, k); | |||
| 442 | } | |||
| 443 | { | |||
| 444 | // This is copied from ObjArrayAllocator::initialize | |||
| 445 | arrayOopDesc::set_length(mem, length); | |||
| 446 | } | |||
| 447 | ||||
| 448 | _roots = OopHandle(Universe::vm_global(), cast_to_oop(mem)); | |||
| 449 | for (int i = 0; i < length; i++) { | |||
| 450 | roots()->obj_at_put(i, _pending_roots->at(i)); | |||
| 451 | } | |||
| 452 | log_info(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("archived obj roots[%d] = " SIZE_FORMAT"%" "l" "u" " words, klass = %p, obj = %p", length, size, k, mem); | |||
| 453 | } | |||
| 454 | ||||
| 455 | void HeapShared::init_narrow_oop_decoding(address base, int shift) { | |||
| 456 | _narrow_oop_base = base; | |||
| 457 | _narrow_oop_shift = shift; | |||
| 458 | } | |||
| 459 | ||||
| 460 | // | |||
| 461 | // Subgraph archiving support | |||
| 462 | // | |||
| 463 | HeapShared::DumpTimeKlassSubGraphInfoTable* HeapShared::_dump_time_subgraph_info_table = NULL__null; | |||
| 464 | HeapShared::RunTimeKlassSubGraphInfoTable HeapShared::_run_time_subgraph_info_table; | |||
| 465 | ||||
| 466 | // Get the subgraph_info for Klass k. A new subgraph_info is created if | |||
| 467 | // there is no existing one for k. The subgraph_info records the relocated | |||
| 468 | // Klass* of the original k. | |||
| 469 | KlassSubGraphInfo* HeapShared::init_subgraph_info(Klass* k, bool is_full_module_graph) { | |||
| 470 | assert(DumpSharedSpaces, "dump time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 470, "assert(" "DumpSharedSpaces" ") failed", "dump time only" ); ::breakpoint(); } } while (0); | |||
| 471 | bool created; | |||
| 472 | Klass* relocated_k = ArchiveBuilder::get_relocated_klass(k); | |||
| 473 | KlassSubGraphInfo* info = | |||
| 474 | _dump_time_subgraph_info_table->put_if_absent(relocated_k, KlassSubGraphInfo(relocated_k, is_full_module_graph), | |||
| 475 | &created); | |||
| 476 | assert(created, "must not initialize twice")do { if (!(created)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 476, "assert(" "created" ") failed", "must not initialize twice" ); ::breakpoint(); } } while (0); | |||
| 477 | return info; | |||
| 478 | } | |||
| 479 | ||||
| 480 | KlassSubGraphInfo* HeapShared::get_subgraph_info(Klass* k) { | |||
| 481 | assert(DumpSharedSpaces, "dump time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 481, "assert(" "DumpSharedSpaces" ") failed", "dump time only" ); ::breakpoint(); } } while (0); | |||
| 482 | Klass* relocated_k = ArchiveBuilder::get_relocated_klass(k); | |||
| 483 | KlassSubGraphInfo* info = _dump_time_subgraph_info_table->get(relocated_k); | |||
| 484 | assert(info != NULL, "must have been initialized")do { if (!(info != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 484, "assert(" "info != __null" ") failed", "must have been initialized" ); ::breakpoint(); } } while (0); | |||
| 485 | return info; | |||
| 486 | } | |||
| 487 | ||||
| 488 | // Add an entry field to the current KlassSubGraphInfo. | |||
| 489 | void KlassSubGraphInfo::add_subgraph_entry_field( | |||
| 490 | int static_field_offset, oop v, bool is_closed_archive) { | |||
| 491 | assert(DumpSharedSpaces, "dump time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 491, "assert(" "DumpSharedSpaces" ") failed", "dump time only" ); ::breakpoint(); } } while (0); | |||
| 492 | if (_subgraph_entry_fields == NULL__null) { | |||
| 493 | _subgraph_entry_fields = | |||
| 494 | new(ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, mtClass); | |||
| 495 | } | |||
| 496 | _subgraph_entry_fields->append(static_field_offset); | |||
| 497 | _subgraph_entry_fields->append(HeapShared::append_root(v)); | |||
| 498 | } | |||
| 499 | ||||
| 500 | // Add the Klass* for an object in the current KlassSubGraphInfo's subgraphs. | |||
| 501 | // Only objects of boot classes can be included in sub-graph. | |||
| 502 | void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k) { | |||
| 503 | assert(DumpSharedSpaces, "dump time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 503, "assert(" "DumpSharedSpaces" ") failed", "dump time only" ); ::breakpoint(); } } while (0); | |||
| 504 | Klass* relocated_k = ArchiveBuilder::get_relocated_klass(orig_k); | |||
| 505 | ||||
| 506 | if (_subgraph_object_klasses == NULL__null) { | |||
| 507 | _subgraph_object_klasses = | |||
| 508 | new(ResourceObj::C_HEAP, mtClass) GrowableArray<Klass*>(50, mtClass); | |||
| 509 | } | |||
| 510 | ||||
| 511 | assert(ArchiveBuilder::current()->is_in_buffer_space(relocated_k), "must be a shared class")do { if (!(ArchiveBuilder::current()->is_in_buffer_space(relocated_k ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 511, "assert(" "ArchiveBuilder::current()->is_in_buffer_space(relocated_k)" ") failed", "must be a shared class"); ::breakpoint(); } } while (0); | |||
| 512 | ||||
| 513 | if (_k == relocated_k) { | |||
| 514 | // Don't add the Klass containing the sub-graph to it's own klass | |||
| 515 | // initialization list. | |||
| 516 | return; | |||
| 517 | } | |||
| 518 | ||||
| 519 | if (relocated_k->is_instance_klass()) { | |||
| 520 | assert(InstanceKlass::cast(relocated_k)->is_shared_boot_class(),do { if (!(InstanceKlass::cast(relocated_k)->is_shared_boot_class ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 521, "assert(" "InstanceKlass::cast(relocated_k)->is_shared_boot_class()" ") failed", "must be boot class"); ::breakpoint(); } } while (0) | |||
| 521 | "must be boot class")do { if (!(InstanceKlass::cast(relocated_k)->is_shared_boot_class ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 521, "assert(" "InstanceKlass::cast(relocated_k)->is_shared_boot_class()" ") failed", "must be boot class"); ::breakpoint(); } } while (0); | |||
| 522 | // vmClasses::xxx_klass() are not updated, need to check | |||
| 523 | // the original Klass* | |||
| 524 | if (orig_k == vmClasses::String_klass() || | |||
| 525 | orig_k == vmClasses::Object_klass()) { | |||
| 526 | // Initialized early during VM initialization. No need to be added | |||
| 527 | // to the sub-graph object class list. | |||
| 528 | return; | |||
| 529 | } | |||
| 530 | } else if (relocated_k->is_objArray_klass()) { | |||
| 531 | Klass* abk = ObjArrayKlass::cast(relocated_k)->bottom_klass(); | |||
| 532 | if (abk->is_instance_klass()) { | |||
| 533 | assert(InstanceKlass::cast(abk)->is_shared_boot_class(),do { if (!(InstanceKlass::cast(abk)->is_shared_boot_class( ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 534, "assert(" "InstanceKlass::cast(abk)->is_shared_boot_class()" ") failed", "must be boot class"); ::breakpoint(); } } while (0) | |||
| 534 | "must be boot class")do { if (!(InstanceKlass::cast(abk)->is_shared_boot_class( ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 534, "assert(" "InstanceKlass::cast(abk)->is_shared_boot_class()" ") failed", "must be boot class"); ::breakpoint(); } } while (0); | |||
| 535 | } | |||
| 536 | if (relocated_k == Universe::objectArrayKlassObj()) { | |||
| 537 | // Initialized early during Universe::genesis. No need to be added | |||
| 538 | // to the list. | |||
| 539 | return; | |||
| 540 | } | |||
| 541 | } else { | |||
| 542 | assert(relocated_k->is_typeArray_klass(), "must be")do { if (!(relocated_k->is_typeArray_klass())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 542, "assert(" "relocated_k->is_typeArray_klass()" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 543 | // Primitive type arrays are created early during Universe::genesis. | |||
| 544 | return; | |||
| 545 | } | |||
| 546 | ||||
| 547 | if (log_is_enabled(Debug, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) { | |||
| 548 | if (!_subgraph_object_klasses->contains(relocated_k)) { | |||
| 549 | ResourceMark rm; | |||
| 550 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("Adding klass %s", orig_k->external_name()); | |||
| 551 | } | |||
| 552 | } | |||
| 553 | ||||
| 554 | _subgraph_object_klasses->append_if_missing(relocated_k); | |||
| 555 | _has_non_early_klasses |= is_non_early_klass(orig_k); | |||
| 556 | } | |||
| 557 | ||||
| 558 | bool KlassSubGraphInfo::is_non_early_klass(Klass* k) { | |||
| 559 | if (k->is_objArray_klass()) { | |||
| 560 | k = ObjArrayKlass::cast(k)->bottom_klass(); | |||
| 561 | } | |||
| 562 | if (k->is_instance_klass()) { | |||
| 563 | if (!SystemDictionaryShared::is_early_klass(InstanceKlass::cast(k))) { | |||
| 564 | ResourceMark rm; | |||
| 565 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("non-early: %s", k->external_name()); | |||
| 566 | return true; | |||
| 567 | } else { | |||
| 568 | return false; | |||
| 569 | } | |||
| 570 | } else { | |||
| 571 | return false; | |||
| 572 | } | |||
| 573 | } | |||
| 574 | ||||
| 575 | // Initialize an archived subgraph_info_record from the given KlassSubGraphInfo. | |||
| 576 | void ArchivedKlassSubGraphInfoRecord::init(KlassSubGraphInfo* info) { | |||
| 577 | _k = info->klass(); | |||
| 578 | _entry_field_records = NULL__null; | |||
| 579 | _subgraph_object_klasses = NULL__null; | |||
| 580 | _is_full_module_graph = info->is_full_module_graph(); | |||
| 581 | ||||
| 582 | if (_is_full_module_graph) { | |||
| 583 | // Consider all classes referenced by the full module graph as early -- we will be | |||
| 584 | // allocating objects of these classes during JVMTI early phase, so they cannot | |||
| 585 | // be processed by (non-early) JVMTI ClassFileLoadHook | |||
| 586 | _has_non_early_klasses = false; | |||
| 587 | } else { | |||
| 588 | _has_non_early_klasses = info->has_non_early_klasses(); | |||
| 589 | } | |||
| 590 | ||||
| 591 | if (_has_non_early_klasses) { | |||
| 592 | ResourceMark rm; | |||
| 593 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>( | |||
| 594 | "Subgraph of klass %s has non-early klasses and cannot be used when JVMTI ClassFileLoadHook is enabled", | |||
| 595 | _k->external_name()); | |||
| 596 | } | |||
| 597 | ||||
| 598 | // populate the entry fields | |||
| 599 | GrowableArray<int>* entry_fields = info->subgraph_entry_fields(); | |||
| 600 | if (entry_fields != NULL__null) { | |||
| 601 | int num_entry_fields = entry_fields->length(); | |||
| 602 | assert(num_entry_fields % 2 == 0, "sanity")do { if (!(num_entry_fields % 2 == 0)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 602, "assert(" "num_entry_fields % 2 == 0" ") failed", "sanity" ); ::breakpoint(); } } while (0); | |||
| 603 | _entry_field_records = | |||
| 604 | ArchiveBuilder::new_ro_array<int>(num_entry_fields); | |||
| 605 | for (int i = 0 ; i < num_entry_fields; i++) { | |||
| 606 | _entry_field_records->at_put(i, entry_fields->at(i)); | |||
| 607 | } | |||
| 608 | } | |||
| 609 | ||||
| 610 | // the Klasses of the objects in the sub-graphs | |||
| 611 | GrowableArray<Klass*>* subgraph_object_klasses = info->subgraph_object_klasses(); | |||
| 612 | if (subgraph_object_klasses != NULL__null) { | |||
| 613 | int num_subgraphs_klasses = subgraph_object_klasses->length(); | |||
| 614 | _subgraph_object_klasses = | |||
| 615 | ArchiveBuilder::new_ro_array<Klass*>(num_subgraphs_klasses); | |||
| 616 | for (int i = 0; i < num_subgraphs_klasses; i++) { | |||
| 617 | Klass* subgraph_k = subgraph_object_klasses->at(i); | |||
| 618 | if (log_is_enabled(Info, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) { | |||
| 619 | ResourceMark rm; | |||
| 620 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>( | |||
| 621 | "Archived object klass %s (%2d) => %s", | |||
| 622 | _k->external_name(), i, subgraph_k->external_name()); | |||
| 623 | } | |||
| 624 | _subgraph_object_klasses->at_put(i, subgraph_k); | |||
| 625 | ArchivePtrMarker::mark_pointer(_subgraph_object_klasses->adr_at(i)); | |||
| 626 | } | |||
| 627 | } | |||
| 628 | ||||
| 629 | ArchivePtrMarker::mark_pointer(&_k); | |||
| 630 | ArchivePtrMarker::mark_pointer(&_entry_field_records); | |||
| 631 | ArchivePtrMarker::mark_pointer(&_subgraph_object_klasses); | |||
| 632 | } | |||
| 633 | ||||
| 634 | struct CopyKlassSubGraphInfoToArchive : StackObj { | |||
| 635 | CompactHashtableWriter* _writer; | |||
| 636 | CopyKlassSubGraphInfoToArchive(CompactHashtableWriter* writer) : _writer(writer) {} | |||
| 637 | ||||
| 638 | bool do_entry(Klass* klass, KlassSubGraphInfo& info) { | |||
| 639 | if (info.subgraph_object_klasses() != NULL__null || info.subgraph_entry_fields() != NULL__null) { | |||
| 640 | ArchivedKlassSubGraphInfoRecord* record = | |||
| 641 | (ArchivedKlassSubGraphInfoRecord*)ArchiveBuilder::ro_region_alloc(sizeof(ArchivedKlassSubGraphInfoRecord)); | |||
| 642 | record->init(&info); | |||
| 643 | ||||
| 644 | unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary((address)klass); | |||
| 645 | u4 delta = ArchiveBuilder::current()->any_to_offset_u4(record); | |||
| 646 | _writer->add(hash, delta); | |||
| 647 | } | |||
| 648 | return true; // keep on iterating | |||
| 649 | } | |||
| 650 | }; | |||
| 651 | ||||
| 652 | // Build the records of archived subgraph infos, which include: | |||
| 653 | // - Entry points to all subgraphs from the containing class mirror. The entry | |||
| 654 | // points are static fields in the mirror. For each entry point, the field | |||
| 655 | // offset, value and is_closed_archive flag are recorded in the sub-graph | |||
| 656 | // info. The value is stored back to the corresponding field at runtime. | |||
| 657 | // - A list of klasses that need to be loaded/initialized before archived | |||
| 658 | // java object sub-graph can be accessed at runtime. | |||
| 659 | void HeapShared::write_subgraph_info_table() { | |||
| 660 | // Allocate the contents of the hashtable(s) inside the RO region of the CDS archive. | |||
| 661 | DumpTimeKlassSubGraphInfoTable* d_table = _dump_time_subgraph_info_table; | |||
| 662 | CompactHashtableStats stats; | |||
| 663 | ||||
| 664 | _run_time_subgraph_info_table.reset(); | |||
| 665 | ||||
| 666 | CompactHashtableWriter writer(d_table->_count, &stats); | |||
| 667 | CopyKlassSubGraphInfoToArchive copy(&writer); | |||
| 668 | d_table->iterate(©); | |||
| 669 | ||||
| 670 | writer.dump(&_run_time_subgraph_info_table, "subgraphs"); | |||
| 671 | } | |||
| 672 | ||||
| 673 | void HeapShared::serialize_subgraph_info_table_header(SerializeClosure* soc) { | |||
| 674 | _run_time_subgraph_info_table.serialize_header(soc); | |||
| 675 | } | |||
| 676 | ||||
| 677 | static void verify_the_heap(Klass* k, const char* which) { | |||
| 678 | if (VerifyArchivedFields > 0) { | |||
| 679 | ResourceMark rm; | |||
| 680 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Verify heap %s initializing static field(s) in %s", | |||
| 681 | which, k->external_name()); | |||
| 682 | ||||
| 683 | VM_Verify verify_op; | |||
| 684 | VMThread::execute(&verify_op); | |||
| 685 | ||||
| 686 | if (VerifyArchivedFields > 1 && is_init_completed()) { | |||
| 687 | // At this time, the oop->klass() of some archived objects in the heap may not | |||
| 688 | // have been loaded into the system dictionary yet. Nevertheless, oop->klass() should | |||
| 689 | // have enough information (object size, oop maps, etc) so that a GC can be safely | |||
| 690 | // performed. | |||
| 691 | // | |||
| 692 | // -XX:VerifyArchivedFields=2 force a GC to happen in such an early stage | |||
| 693 | // to check for GC safety. | |||
| 694 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Trigger GC %s initializing static field(s) in %s", | |||
| 695 | which, k->external_name()); | |||
| 696 | FlagSetting fs1(VerifyBeforeGC, true); | |||
| 697 | FlagSetting fs2(VerifyDuringGC, true); | |||
| 698 | FlagSetting fs3(VerifyAfterGC, true); | |||
| 699 | Universe::heap()->collect(GCCause::_java_lang_system_gc); | |||
| 700 | } | |||
| 701 | } | |||
| 702 | } | |||
| 703 | ||||
| 704 | // Before GC can execute, we must ensure that all oops reachable from HeapShared::roots() | |||
| 705 | // have a valid klass. I.e., oopDesc::klass() must have already been resolved. | |||
| 706 | // | |||
| 707 | // Note: if a ArchivedKlassSubGraphInfoRecord contains non-early classes, and JVMTI | |||
| 708 | // ClassFileLoadHook is enabled, it's possible for this class to be dynamically replaced. In | |||
| 709 | // this case, we will not load the ArchivedKlassSubGraphInfoRecord and will clear its roots. | |||
| 710 | void HeapShared::resolve_classes(JavaThread* THREAD__the_thread__) { | |||
| 711 | if (!is_fully_available()) { | |||
| 712 | return; // nothing to do | |||
| 713 | } | |||
| 714 | resolve_classes_for_subgraphs(closed_archive_subgraph_entry_fields, | |||
| 715 | num_closed_archive_subgraph_entry_fields, | |||
| 716 | THREAD__the_thread__); | |||
| 717 | resolve_classes_for_subgraphs(open_archive_subgraph_entry_fields, | |||
| 718 | num_open_archive_subgraph_entry_fields, | |||
| 719 | THREAD__the_thread__); | |||
| 720 | resolve_classes_for_subgraphs(fmg_open_archive_subgraph_entry_fields, | |||
| 721 | num_fmg_open_archive_subgraph_entry_fields, | |||
| 722 | THREAD__the_thread__); | |||
| 723 | } | |||
| 724 | ||||
| 725 | void HeapShared::resolve_classes_for_subgraphs(ArchivableStaticFieldInfo fields[], | |||
| 726 | int num, JavaThread* THREAD__the_thread__) { | |||
| 727 | for (int i = 0; i < num; i++) { | |||
| 728 | ArchivableStaticFieldInfo* info = &fields[i]; | |||
| 729 | TempNewSymbol klass_name = SymbolTable::new_symbol(info->klass_name); | |||
| 730 | InstanceKlass* k = SystemDictionaryShared::find_builtin_class(klass_name); | |||
| 731 | assert(k != NULL && k->is_shared_boot_class(), "sanity")do { if (!(k != __null && k->is_shared_boot_class( ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 731, "assert(" "k != __null && k->is_shared_boot_class()" ") failed", "sanity"); ::breakpoint(); } } while (0); | |||
| 732 | resolve_classes_for_subgraph_of(k, THREAD__the_thread__); | |||
| 733 | } | |||
| 734 | } | |||
| 735 | ||||
| 736 | void HeapShared::resolve_classes_for_subgraph_of(Klass* k, JavaThread* THREAD__the_thread__) { | |||
| 737 | ExceptionMark em(THREAD__the_thread__); | |||
| 738 | const ArchivedKlassSubGraphInfoRecord* record = | |||
| 739 | resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/false, THREAD__the_thread__); | |||
| 740 | if (HAS_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->has_pending_exception())) { | |||
| 741 | CLEAR_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->clear_pending_exception( )); | |||
| 742 | } | |||
| 743 | if (record == NULL__null) { | |||
| 744 | clear_archived_roots_of(k); | |||
| 745 | } | |||
| 746 | } | |||
| 747 | ||||
| 748 | void HeapShared::initialize_from_archived_subgraph(Klass* k, JavaThread* THREAD__the_thread__) { | |||
| 749 | if (!is_fully_available()) { | |||
| 750 | return; // nothing to do | |||
| 751 | } | |||
| 752 | ||||
| 753 | ExceptionMark em(THREAD__the_thread__); | |||
| 754 | const ArchivedKlassSubGraphInfoRecord* record = | |||
| 755 | resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/true, THREAD__the_thread__); | |||
| 756 | ||||
| 757 | if (HAS_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->has_pending_exception())) { | |||
| 758 | CLEAR_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->clear_pending_exception( )); | |||
| 759 | // None of the field value will be set if there was an exception when initializing the classes. | |||
| 760 | // The java code will not see any of the archived objects in the | |||
| 761 | // subgraphs referenced from k in this case. | |||
| 762 | return; | |||
| 763 | } | |||
| 764 | ||||
| 765 | if (record != NULL__null) { | |||
| 766 | init_archived_fields_for(k, record); | |||
| 767 | } | |||
| 768 | } | |||
| 769 | ||||
| 770 | const ArchivedKlassSubGraphInfoRecord* | |||
| 771 | HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAPSJavaThread* __the_thread__) { | |||
| 772 | assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces")do { if (!(!DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 772, "assert(" "!DumpSharedSpaces" ") failed", "Should not be called with DumpSharedSpaces" ); ::breakpoint(); } } while (0); | |||
| 773 | ||||
| 774 | if (!k->is_shared()) { | |||
| 775 | return NULL__null; | |||
| 776 | } | |||
| 777 | unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(k); | |||
| 778 | const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0); | |||
| 779 | ||||
| 780 | // Initialize from archived data. Currently this is done only | |||
| 781 | // during VM initialization time. No lock is needed. | |||
| 782 | if (record != NULL__null) { | |||
| 783 | if (record->is_full_module_graph() && !MetaspaceShared::use_full_module_graph()) { | |||
| 784 | if (log_is_enabled(Info, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) { | |||
| 785 | ResourceMark rm(THREAD__the_thread__); | |||
| 786 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("subgraph %s cannot be used because full module graph is disabled", | |||
| 787 | k->external_name()); | |||
| 788 | } | |||
| 789 | return NULL__null; | |||
| 790 | } | |||
| 791 | ||||
| 792 | if (record->has_non_early_klasses() && JvmtiExport::should_post_class_file_load_hook()) { | |||
| 793 | if (log_is_enabled(Info, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) { | |||
| 794 | ResourceMark rm(THREAD__the_thread__); | |||
| 795 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("subgraph %s cannot be used because JVMTI ClassFileLoadHook is enabled", | |||
| 796 | k->external_name()); | |||
| 797 | } | |||
| 798 | return NULL__null; | |||
| 799 | } | |||
| 800 | ||||
| 801 | resolve_or_init(k, do_init, CHECK_NULL__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return __null; (void)(0); | |||
| 802 | ||||
| 803 | // Load/link/initialize the klasses of the objects in the subgraph. | |||
| 804 | // NULL class loader is used. | |||
| 805 | Array<Klass*>* klasses = record->subgraph_object_klasses(); | |||
| 806 | if (klasses != NULL__null) { | |||
| 807 | for (int i = 0; i < klasses->length(); i++) { | |||
| 808 | Klass* klass = klasses->at(i); | |||
| 809 | if (!klass->is_shared()) { | |||
| 810 | return NULL__null; | |||
| 811 | } | |||
| 812 | resolve_or_init(klass, do_init, CHECK_NULL__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return __null; (void)(0); | |||
| 813 | } | |||
| 814 | } | |||
| 815 | } | |||
| 816 | ||||
| 817 | return record; | |||
| 818 | } | |||
| 819 | ||||
| 820 | void HeapShared::resolve_or_init(Klass* k, bool do_init, TRAPSJavaThread* __the_thread__) { | |||
| 821 | if (!do_init) { | |||
| 822 | if (k->class_loader_data() == NULL__null) { | |||
| 823 | Klass* resolved_k = SystemDictionary::resolve_or_null(k->name(), CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 824 | assert(resolved_k == k, "classes used by archived heap must not be replaced by JVMTI ClassFileLoadHook")do { if (!(resolved_k == k)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 824, "assert(" "resolved_k == k" ") failed", "classes used by archived heap must not be replaced by JVMTI ClassFileLoadHook" ); ::breakpoint(); } } while (0); | |||
| 825 | } | |||
| 826 | } else { | |||
| 827 | assert(k->class_loader_data() != NULL, "must have been resolved by HeapShared::resolve_classes")do { if (!(k->class_loader_data() != __null)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 827, "assert(" "k->class_loader_data() != __null" ") failed" , "must have been resolved by HeapShared::resolve_classes"); :: breakpoint(); } } while (0); | |||
| 828 | if (k->is_instance_klass()) { | |||
| 829 | InstanceKlass* ik = InstanceKlass::cast(k); | |||
| 830 | ik->initialize(CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 831 | } else if (k->is_objArray_klass()) { | |||
| 832 | ObjArrayKlass* oak = ObjArrayKlass::cast(k); | |||
| 833 | oak->initialize(CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 834 | } | |||
| 835 | } | |||
| 836 | } | |||
| 837 | ||||
| 838 | void HeapShared::init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record) { | |||
| 839 | verify_the_heap(k, "before"); | |||
| 840 | ||||
| 841 | // Load the subgraph entry fields from the record and store them back to | |||
| 842 | // the corresponding fields within the mirror. | |||
| 843 | oop m = k->java_mirror(); | |||
| 844 | Array<int>* entry_field_records = record->entry_field_records(); | |||
| 845 | if (entry_field_records != NULL__null) { | |||
| 846 | int efr_len = entry_field_records->length(); | |||
| 847 | assert(efr_len % 2 == 0, "sanity")do { if (!(efr_len % 2 == 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 847, "assert(" "efr_len % 2 == 0" ") failed", "sanity"); :: breakpoint(); } } while (0); | |||
| 848 | for (int i = 0; i < efr_len; i += 2) { | |||
| 849 | int field_offset = entry_field_records->at(i); | |||
| 850 | int root_index = entry_field_records->at(i+1); | |||
| 851 | oop v = get_root(root_index, /*clear=*/true); | |||
| 852 | m->obj_field_put(field_offset, v); | |||
| 853 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>(" " PTR_FORMAT"0x%016" "l" "x" " init field @ %2d = " PTR_FORMAT"0x%016" "l" "x", p2i(k), field_offset, p2i(v)); | |||
| 854 | } | |||
| 855 | ||||
| 856 | // Done. Java code can see the archived sub-graphs referenced from k's | |||
| 857 | // mirror after this point. | |||
| 858 | if (log_is_enabled(Info, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) { | |||
| 859 | ResourceMark rm; | |||
| 860 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("initialize_from_archived_subgraph %s " PTR_FORMAT"0x%016" "l" "x" "%s", | |||
| 861 | k->external_name(), p2i(k), JvmtiExport::is_early_phase() ? " (early)" : ""); | |||
| 862 | } | |||
| 863 | } | |||
| 864 | ||||
| 865 | verify_the_heap(k, "after "); | |||
| 866 | } | |||
| 867 | ||||
| 868 | void HeapShared::clear_archived_roots_of(Klass* k) { | |||
| 869 | unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(k); | |||
| 870 | const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0); | |||
| 871 | if (record != NULL__null) { | |||
| 872 | Array<int>* entry_field_records = record->entry_field_records(); | |||
| 873 | if (entry_field_records != NULL__null) { | |||
| 874 | int efr_len = entry_field_records->length(); | |||
| 875 | assert(efr_len % 2 == 0, "sanity")do { if (!(efr_len % 2 == 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 875, "assert(" "efr_len % 2 == 0" ") failed", "sanity"); :: breakpoint(); } } while (0); | |||
| 876 | for (int i = 0; i < efr_len; i += 2) { | |||
| 877 | int root_index = entry_field_records->at(i+1); | |||
| 878 | clear_root(root_index); | |||
| 879 | } | |||
| 880 | } | |||
| 881 | } | |||
| 882 | } | |||
| 883 | ||||
| 884 | class WalkOopAndArchiveClosure: public BasicOopIterateClosure { | |||
| 885 | int _level; | |||
| 886 | bool _is_closed_archive; | |||
| 887 | bool _record_klasses_only; | |||
| 888 | KlassSubGraphInfo* _subgraph_info; | |||
| 889 | oop _orig_referencing_obj; | |||
| 890 | oop _archived_referencing_obj; | |||
| 891 | public: | |||
| 892 | WalkOopAndArchiveClosure(int level, | |||
| 893 | bool is_closed_archive, | |||
| 894 | bool record_klasses_only, | |||
| 895 | KlassSubGraphInfo* subgraph_info, | |||
| 896 | oop orig, oop archived) : | |||
| 897 | _level(level), _is_closed_archive(is_closed_archive), | |||
| 898 | _record_klasses_only(record_klasses_only), | |||
| 899 | _subgraph_info(subgraph_info), | |||
| 900 | _orig_referencing_obj(orig), _archived_referencing_obj(archived) {} | |||
| 901 | void do_oop(narrowOop *p) { WalkOopAndArchiveClosure::do_oop_work(p); } | |||
| 902 | void do_oop( oop *p) { WalkOopAndArchiveClosure::do_oop_work(p); } | |||
| 903 | ||||
| 904 | protected: | |||
| 905 | template <class T> void do_oop_work(T *p) { | |||
| 906 | oop obj = RawAccess<>::oop_load(p); | |||
| 907 | if (!CompressedOops::is_null(obj)) { | |||
| 908 | assert(!HeapShared::is_archived_object_during_dumptime(obj),do { if (!(!HeapShared::is_archived_object_during_dumptime(obj ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 909, "assert(" "!HeapShared::is_archived_object_during_dumptime(obj)" ") failed", "original objects must not point to archived objects" ); ::breakpoint(); } } while (0) | |||
| 909 | "original objects must not point to archived objects")do { if (!(!HeapShared::is_archived_object_during_dumptime(obj ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 909, "assert(" "!HeapShared::is_archived_object_during_dumptime(obj)" ") failed", "original objects must not point to archived objects" ); ::breakpoint(); } } while (0); | |||
| 910 | ||||
| 911 | size_t field_delta = pointer_delta(p, _orig_referencing_obj, sizeof(char)); | |||
| 912 | T* new_p = (T*)(cast_from_oop<address>(_archived_referencing_obj) + field_delta); | |||
| 913 | ||||
| 914 | if (!_record_klasses_only && log_is_enabled(Debug, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) { | |||
| 915 | ResourceMark rm; | |||
| 916 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("(%d) %s[" SIZE_FORMAT"%" "l" "u" "] ==> " PTR_FORMAT"0x%016" "l" "x" " size " SIZE_FORMAT"%" "l" "u" " %s", _level, | |||
| 917 | _orig_referencing_obj->klass()->external_name(), field_delta, | |||
| 918 | p2i(obj), obj->size() * HeapWordSize, obj->klass()->external_name()); | |||
| 919 | LogTarget(Trace, cds, heap)LogTargetImpl<LogLevel::Trace, (LogTag::_cds), (LogTag::_heap ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) , (LogTag::__NO_TAG)> log; | |||
| 920 | LogStream out(log); | |||
| 921 | obj->print_on(&out); | |||
| 922 | } | |||
| 923 | ||||
| 924 | oop archived = HeapShared::archive_reachable_objects_from( | |||
| 925 | _level + 1, _subgraph_info, obj, _is_closed_archive); | |||
| 926 | assert(archived != NULL, "VM should have exited with unarchivable objects for _level > 1")do { if (!(archived != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 926, "assert(" "archived != __null" ") failed", "VM should have exited with unarchivable objects for _level > 1" ); ::breakpoint(); } } while (0); | |||
| 927 | assert(HeapShared::is_archived_object_during_dumptime(archived), "must be")do { if (!(HeapShared::is_archived_object_during_dumptime(archived ))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 927, "assert(" "HeapShared::is_archived_object_during_dumptime(archived)" ") failed", "must be"); ::breakpoint(); } } while (0); | |||
| 928 | ||||
| 929 | if (!_record_klasses_only) { | |||
| 930 | // Update the reference in the archived copy of the referencing object. | |||
| 931 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("(%d) updating oop @[" PTR_FORMAT"0x%016" "l" "x" "] " PTR_FORMAT"0x%016" "l" "x" " ==> " PTR_FORMAT"0x%016" "l" "x", | |||
| 932 | _level, p2i(new_p), p2i(obj), p2i(archived)); | |||
| 933 | RawAccess<IS_NOT_NULL>::oop_store(new_p, archived); | |||
| 934 | } | |||
| 935 | } | |||
| 936 | } | |||
| 937 | }; | |||
| 938 | ||||
| 939 | void HeapShared::check_closed_region_object(InstanceKlass* k) { | |||
| 940 | // Check fields in the object | |||
| 941 | for (JavaFieldStream fs(k); !fs.done(); fs.next()) { | |||
| 942 | if (!fs.access_flags().is_static()) { | |||
| 943 | BasicType ft = fs.field_descriptor().field_type(); | |||
| 944 | if (!fs.access_flags().is_final() && is_reference_type(ft)) { | |||
| 945 | ResourceMark rm; | |||
| 946 | log_warning(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Warning))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Warning>( | |||
| 947 | "Please check reference field in %s instance in closed archive heap region: %s %s", | |||
| 948 | k->external_name(), (fs.name())->as_C_string(), | |||
| 949 | (fs.signature())->as_C_string()); | |||
| 950 | } | |||
| 951 | } | |||
| 952 | } | |||
| 953 | } | |||
| 954 | ||||
| 955 | void HeapShared::check_module_oop(oop orig_module_obj) { | |||
| 956 | assert(DumpSharedSpaces, "must be")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 956, "assert(" "DumpSharedSpaces" ") failed", "must be"); :: breakpoint(); } } while (0); | |||
| 957 | assert(java_lang_Module::is_instance(orig_module_obj), "must be")do { if (!(java_lang_Module::is_instance(orig_module_obj))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 957, "assert(" "java_lang_Module::is_instance(orig_module_obj)" ") failed", "must be"); ::breakpoint(); } } while (0); | |||
| 958 | ModuleEntry* orig_module_ent = java_lang_Module::module_entry_raw(orig_module_obj); | |||
| 959 | if (orig_module_ent == NULL__null) { | |||
| 960 | // These special Module objects are created in Java code. They are not | |||
| 961 | // defined via Modules::define_module(), so they don't have a ModuleEntry: | |||
| 962 | // java.lang.Module::ALL_UNNAMED_MODULE | |||
| 963 | // java.lang.Module::EVERYONE_MODULE | |||
| 964 | // jdk.internal.loader.ClassLoaders$BootClassLoader::unnamedModule | |||
| 965 | assert(java_lang_Module::name(orig_module_obj) == NULL, "must be unnamed")do { if (!(java_lang_Module::name(orig_module_obj) == __null) ) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 965, "assert(" "java_lang_Module::name(orig_module_obj) == __null" ") failed", "must be unnamed"); ::breakpoint(); } } while (0 ); | |||
| 966 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Module oop with No ModuleEntry* @[" PTR_FORMAT"0x%016" "l" "x" "]", p2i(orig_module_obj)); | |||
| 967 | } else { | |||
| 968 | ClassLoaderData* loader_data = orig_module_ent->loader_data(); | |||
| 969 | assert(loader_data->is_builtin_class_loader_data(), "must be")do { if (!(loader_data->is_builtin_class_loader_data())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 969, "assert(" "loader_data->is_builtin_class_loader_data()" ") failed", "must be"); ::breakpoint(); } } while (0); | |||
| 970 | } | |||
| 971 | } | |||
| 972 | ||||
| 973 | ||||
| 974 | // (1) If orig_obj has not been archived yet, archive it. | |||
| 975 | // (2) If orig_obj has not been seen yet (since start_recording_subgraph() was called), | |||
| 976 | // trace all objects that are reachable from it, and make sure these objects are archived. | |||
| 977 | // (3) Record the klasses of all orig_obj and all reachable objects. | |||
| 978 | oop HeapShared::archive_reachable_objects_from(int level, | |||
| 979 | KlassSubGraphInfo* subgraph_info, | |||
| 980 | oop orig_obj, | |||
| 981 | bool is_closed_archive) { | |||
| 982 | assert(orig_obj != NULL, "must be")do { if (!(orig_obj != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 982, "assert(" "orig_obj != __null" ") failed", "must be"); ::breakpoint(); } } while (0); | |||
| 983 | assert(!is_archived_object_during_dumptime(orig_obj), "sanity")do { if (!(!is_archived_object_during_dumptime(orig_obj))) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 983, "assert(" "!is_archived_object_during_dumptime(orig_obj)" ") failed", "sanity"); ::breakpoint(); } } while (0); | |||
| 984 | ||||
| 985 | if (!JavaClasses::is_supported_for_archiving(orig_obj)) { | |||
| 986 | // This object has injected fields that cannot be supported easily, so we disallow them for now. | |||
| 987 | // If you get an error here, you probably made a change in the JDK library that has added | |||
| 988 | // these objects that are referenced (directly or indirectly) by static fields. | |||
| 989 | ResourceMark rm; | |||
| 990 | log_error(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Cannot archive object of class %s", orig_obj->klass()->external_name()); | |||
| 991 | vm_direct_exit(1); | |||
| 992 | } | |||
| 993 | ||||
| 994 | // java.lang.Class instances cannot be included in an archived object sub-graph. We only support | |||
| 995 | // them as Klass::_archived_mirror because they need to be specially restored at run time. | |||
| 996 | // | |||
| 997 | // If you get an error here, you probably made a change in the JDK library that has added a Class | |||
| 998 | // object that is referenced (directly or indirectly) by static fields. | |||
| 999 | if (java_lang_Class::is_instance(orig_obj)) { | |||
| 1000 | log_error(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("(%d) Unknown java.lang.Class object is in the archived sub-graph", level); | |||
| 1001 | vm_direct_exit(1); | |||
| 1002 | } | |||
| 1003 | ||||
| 1004 | oop archived_obj = find_archived_heap_object(orig_obj); | |||
| 1005 | if (java_lang_String::is_instance(orig_obj) && archived_obj != NULL__null) { | |||
| 1006 | // To save time, don't walk strings that are already archived. They just contain | |||
| 1007 | // pointers to a type array, whose klass doesn't need to be recorded. | |||
| 1008 | return archived_obj; | |||
| 1009 | } | |||
| 1010 | ||||
| 1011 | if (has_been_seen_during_subgraph_recording(orig_obj)) { | |||
| 1012 | // orig_obj has already been archived and traced. Nothing more to do. | |||
| 1013 | return archived_obj; | |||
| 1014 | } else { | |||
| 1015 | set_has_been_seen_during_subgraph_recording(orig_obj); | |||
| 1016 | } | |||
| 1017 | ||||
| 1018 | bool record_klasses_only = (archived_obj != NULL__null); | |||
| 1019 | if (archived_obj == NULL__null) { | |||
| 1020 | ++_num_new_archived_objs; | |||
| 1021 | archived_obj = archive_object(orig_obj); | |||
| 1022 | if (archived_obj == NULL__null) { | |||
| 1023 | // Skip archiving the sub-graph referenced from the current entry field. | |||
| 1024 | ResourceMark rm; | |||
| 1025 | log_error(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>( | |||
| 1026 | "Cannot archive the sub-graph referenced from %s object (" | |||
| 1027 | PTR_FORMAT"0x%016" "l" "x" ") size " SIZE_FORMAT"%" "l" "u" ", skipped.", | |||
| 1028 | orig_obj->klass()->external_name(), p2i(orig_obj), orig_obj->size() * HeapWordSize); | |||
| 1029 | if (level == 1) { | |||
| 1030 | // Don't archive a subgraph root that's too big. For archives static fields, that's OK | |||
| 1031 | // as the Java code will take care of initializing this field dynamically. | |||
| 1032 | return NULL__null; | |||
| 1033 | } else { | |||
| 1034 | // We don't know how to handle an object that has been archived, but some of its reachable | |||
| 1035 | // objects cannot be archived. Bail out for now. We might need to fix this in the future if | |||
| 1036 | // we have a real use case. | |||
| 1037 | vm_direct_exit(1); | |||
| 1038 | } | |||
| 1039 | } | |||
| 1040 | ||||
| 1041 | if (java_lang_Module::is_instance(orig_obj)) { | |||
| 1042 | check_module_oop(orig_obj); | |||
| 1043 | java_lang_Module::set_module_entry(archived_obj, NULL__null); | |||
| 1044 | java_lang_Module::set_loader(archived_obj, NULL__null); | |||
| 1045 | } else if (java_lang_ClassLoader::is_instance(orig_obj)) { | |||
| 1046 | // class_data will be restored explicitly at run time. | |||
| 1047 | guarantee(orig_obj == SystemDictionary::java_platform_loader() ||do { if (!(orig_obj == SystemDictionary::java_platform_loader () || orig_obj == SystemDictionary::java_system_loader() || java_lang_ClassLoader ::loader_data(orig_obj) == __null)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1049, "guarantee(" "orig_obj == SystemDictionary::java_platform_loader() || orig_obj == SystemDictionary::java_system_loader() || java_lang_ClassLoader::loader_data(orig_obj) == NULL" ") failed", "must be"); ::breakpoint(); } } while (0) | |||
| 1048 | orig_obj == SystemDictionary::java_system_loader() ||do { if (!(orig_obj == SystemDictionary::java_platform_loader () || orig_obj == SystemDictionary::java_system_loader() || java_lang_ClassLoader ::loader_data(orig_obj) == __null)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1049, "guarantee(" "orig_obj == SystemDictionary::java_platform_loader() || orig_obj == SystemDictionary::java_system_loader() || java_lang_ClassLoader::loader_data(orig_obj) == NULL" ") failed", "must be"); ::breakpoint(); } } while (0) | |||
| 1049 | java_lang_ClassLoader::loader_data(orig_obj) == NULL, "must be")do { if (!(orig_obj == SystemDictionary::java_platform_loader () || orig_obj == SystemDictionary::java_system_loader() || java_lang_ClassLoader ::loader_data(orig_obj) == __null)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1049, "guarantee(" "orig_obj == SystemDictionary::java_platform_loader() || orig_obj == SystemDictionary::java_system_loader() || java_lang_ClassLoader::loader_data(orig_obj) == NULL" ") failed", "must be"); ::breakpoint(); } } while (0); | |||
| 1050 | java_lang_ClassLoader::release_set_loader_data(archived_obj, NULL__null); | |||
| 1051 | } | |||
| 1052 | } | |||
| 1053 | ||||
| 1054 | assert(archived_obj != NULL, "must be")do { if (!(archived_obj != __null)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1054, "assert(" "archived_obj != __null" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 1055 | Klass *orig_k = orig_obj->klass(); | |||
| 1056 | subgraph_info->add_subgraph_object_klass(orig_k); | |||
| 1057 | ||||
| 1058 | WalkOopAndArchiveClosure walker(level, is_closed_archive, record_klasses_only, | |||
| 1059 | subgraph_info, orig_obj, archived_obj); | |||
| 1060 | orig_obj->oop_iterate(&walker); | |||
| 1061 | if (is_closed_archive && orig_k->is_instance_klass()) { | |||
| 1062 | check_closed_region_object(InstanceKlass::cast(orig_k)); | |||
| 1063 | } | |||
| 1064 | return archived_obj; | |||
| 1065 | } | |||
| 1066 | ||||
| 1067 | // | |||
| 1068 | // Start from the given static field in a java mirror and archive the | |||
| 1069 | // complete sub-graph of java heap objects that are reached directly | |||
| 1070 | // or indirectly from the starting object by following references. | |||
| 1071 | // Sub-graph archiving restrictions (current): | |||
| 1072 | // | |||
| 1073 | // - All classes of objects in the archived sub-graph (including the | |||
| 1074 | // entry class) must be boot class only. | |||
| 1075 | // - No java.lang.Class instance (java mirror) can be included inside | |||
| 1076 | // an archived sub-graph. Mirror can only be the sub-graph entry object. | |||
| 1077 | // | |||
| 1078 | // The Java heap object sub-graph archiving process (see | |||
| 1079 | // WalkOopAndArchiveClosure): | |||
| 1080 | // | |||
| 1081 | // 1) Java object sub-graph archiving starts from a given static field | |||
| 1082 | // within a Class instance (java mirror). If the static field is a | |||
| 1083 | // refererence field and points to a non-null java object, proceed to | |||
| 1084 | // the next step. | |||
| 1085 | // | |||
| 1086 | // 2) Archives the referenced java object. If an archived copy of the | |||
| 1087 | // current object already exists, updates the pointer in the archived | |||
| 1088 | // copy of the referencing object to point to the current archived object. | |||
| 1089 | // Otherwise, proceed to the next step. | |||
| 1090 | // | |||
| 1091 | // 3) Follows all references within the current java object and recursively | |||
| 1092 | // archive the sub-graph of objects starting from each reference. | |||
| 1093 | // | |||
| 1094 | // 4) Updates the pointer in the archived copy of referencing object to | |||
| 1095 | // point to the current archived object. | |||
| 1096 | // | |||
| 1097 | // 5) The Klass of the current java object is added to the list of Klasses | |||
| 1098 | // for loading and initialzing before any object in the archived graph can | |||
| 1099 | // be accessed at runtime. | |||
| 1100 | // | |||
| 1101 | void HeapShared::archive_reachable_objects_from_static_field(InstanceKlass *k, | |||
| 1102 | const char* klass_name, | |||
| 1103 | int field_offset, | |||
| 1104 | const char* field_name, | |||
| 1105 | bool is_closed_archive) { | |||
| 1106 | assert(DumpSharedSpaces, "dump time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1106, "assert(" "DumpSharedSpaces" ") failed", "dump time only" ); ::breakpoint(); } } while (0); | |||
| 1107 | assert(k->is_shared_boot_class(), "must be boot class")do { if (!(k->is_shared_boot_class())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1107, "assert(" "k->is_shared_boot_class()" ") failed", "must be boot class" ); ::breakpoint(); } } while (0); | |||
| 1108 | ||||
| 1109 | oop m = k->java_mirror(); | |||
| 1110 | ||||
| 1111 | KlassSubGraphInfo* subgraph_info = get_subgraph_info(k); | |||
| 1112 | oop f = m->obj_field(field_offset); | |||
| 1113 | ||||
| 1114 | log_debug(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Debug))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Debug>("Start archiving from: %s::%s (" PTR_FORMAT"0x%016" "l" "x" ")", klass_name, field_name, p2i(f)); | |||
| 1115 | ||||
| 1116 | if (!CompressedOops::is_null(f)) { | |||
| 1117 | if (log_is_enabled(Trace, cds, heap)(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Trace))) { | |||
| 1118 | LogTarget(Trace, cds, heap)LogTargetImpl<LogLevel::Trace, (LogTag::_cds), (LogTag::_heap ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) , (LogTag::__NO_TAG)> log; | |||
| 1119 | LogStream out(log); | |||
| 1120 | f->print_on(&out); | |||
| 1121 | } | |||
| 1122 | ||||
| 1123 | oop af = archive_reachable_objects_from(1, subgraph_info, f, is_closed_archive); | |||
| 1124 | ||||
| 1125 | if (af == NULL__null) { | |||
| 1126 | log_error(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Archiving failed %s::%s (some reachable objects cannot be archived)", | |||
| 1127 | klass_name, field_name); | |||
| 1128 | } else { | |||
| 1129 | // Note: the field value is not preserved in the archived mirror. | |||
| 1130 | // Record the field as a new subGraph entry point. The recorded | |||
| 1131 | // information is restored from the archive at runtime. | |||
| 1132 | subgraph_info->add_subgraph_entry_field(field_offset, af, is_closed_archive); | |||
| 1133 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Archived field %s::%s => " PTR_FORMAT"0x%016" "l" "x", klass_name, field_name, p2i(af)); | |||
| 1134 | } | |||
| 1135 | } else { | |||
| 1136 | // The field contains null, we still need to record the entry point, | |||
| 1137 | // so it can be restored at runtime. | |||
| 1138 | subgraph_info->add_subgraph_entry_field(field_offset, NULL__null, false); | |||
| 1139 | } | |||
| 1140 | } | |||
| 1141 | ||||
| 1142 | #ifndef PRODUCT | |||
| 1143 | class VerifySharedOopClosure: public BasicOopIterateClosure { | |||
| 1144 | private: | |||
| 1145 | bool _is_archived; | |||
| 1146 | ||||
| 1147 | public: | |||
| 1148 | VerifySharedOopClosure(bool is_archived) : _is_archived(is_archived) {} | |||
| 1149 | ||||
| 1150 | void do_oop(narrowOop *p) { VerifySharedOopClosure::do_oop_work(p); } | |||
| 1151 | void do_oop( oop *p) { VerifySharedOopClosure::do_oop_work(p); } | |||
| 1152 | ||||
| 1153 | protected: | |||
| 1154 | template <class T> void do_oop_work(T *p) { | |||
| 1155 | oop obj = RawAccess<>::oop_load(p); | |||
| 1156 | if (!CompressedOops::is_null(obj)) { | |||
| 1157 | HeapShared::verify_reachable_objects_from(obj, _is_archived); | |||
| 1158 | } | |||
| 1159 | } | |||
| 1160 | }; | |||
| 1161 | ||||
| 1162 | void HeapShared::verify_subgraph_from_static_field(InstanceKlass* k, int field_offset) { | |||
| 1163 | assert(DumpSharedSpaces, "dump time only")do { if (!(DumpSharedSpaces)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1163, "assert(" "DumpSharedSpaces" ") failed", "dump time only" ); ::breakpoint(); } } while (0); | |||
| 1164 | assert(k->is_shared_boot_class(), "must be boot class")do { if (!(k->is_shared_boot_class())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1164, "assert(" "k->is_shared_boot_class()" ") failed", "must be boot class" ); ::breakpoint(); } } while (0); | |||
| 1165 | ||||
| 1166 | oop m = k->java_mirror(); | |||
| 1167 | oop f = m->obj_field(field_offset); | |||
| 1168 | if (!CompressedOops::is_null(f)) { | |||
| 1169 | verify_subgraph_from(f); | |||
| 1170 | } | |||
| 1171 | } | |||
| 1172 | ||||
| 1173 | void HeapShared::verify_subgraph_from(oop orig_obj) { | |||
| 1174 | oop archived_obj = find_archived_heap_object(orig_obj); | |||
| 1175 | if (archived_obj == NULL__null) { | |||
| 1176 | // It's OK for the root of a subgraph to be not archived. See comments in | |||
| 1177 | // archive_reachable_objects_from(). | |||
| 1178 | return; | |||
| 1179 | } | |||
| 1180 | ||||
| 1181 | // Verify that all objects reachable from orig_obj are archived. | |||
| 1182 | init_seen_objects_table(); | |||
| 1183 | verify_reachable_objects_from(orig_obj, false); | |||
| 1184 | delete_seen_objects_table(); | |||
| 1185 | ||||
| 1186 | // Note: we could also verify that all objects reachable from the archived | |||
| 1187 | // copy of orig_obj can only point to archived objects, with: | |||
| 1188 | // init_seen_objects_table(); | |||
| 1189 | // verify_reachable_objects_from(archived_obj, true); | |||
| 1190 | // init_seen_objects_table(); | |||
| 1191 | // but that's already done in G1HeapVerifier::verify_archive_regions so we | |||
| 1192 | // won't do it here. | |||
| 1193 | } | |||
| 1194 | ||||
| 1195 | void HeapShared::verify_reachable_objects_from(oop obj, bool is_archived) { | |||
| 1196 | _num_total_verifications ++; | |||
| 1197 | if (!has_been_seen_during_subgraph_recording(obj)) { | |||
| 1198 | set_has_been_seen_during_subgraph_recording(obj); | |||
| 1199 | ||||
| 1200 | if (is_archived) { | |||
| 1201 | assert(is_archived_object_during_dumptime(obj), "must be")do { if (!(is_archived_object_during_dumptime(obj))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1201, "assert(" "is_archived_object_during_dumptime(obj)" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 1202 | assert(find_archived_heap_object(obj) == NULL, "must be")do { if (!(find_archived_heap_object(obj) == __null)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1202, "assert(" "find_archived_heap_object(obj) == __null" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 1203 | } else { | |||
| 1204 | assert(!is_archived_object_during_dumptime(obj), "must be")do { if (!(!is_archived_object_during_dumptime(obj))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1204, "assert(" "!is_archived_object_during_dumptime(obj)" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 1205 | assert(find_archived_heap_object(obj) != NULL, "must be")do { if (!(find_archived_heap_object(obj) != __null)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1205, "assert(" "find_archived_heap_object(obj) != __null" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 1206 | } | |||
| 1207 | ||||
| 1208 | VerifySharedOopClosure walker(is_archived); | |||
| 1209 | obj->oop_iterate(&walker); | |||
| 1210 | } | |||
| 1211 | } | |||
| 1212 | #endif | |||
| 1213 | ||||
| 1214 | HeapShared::SeenObjectsTable* HeapShared::_seen_objects_table = NULL__null; | |||
| 1215 | int HeapShared::_num_new_walked_objs; | |||
| 1216 | int HeapShared::_num_new_archived_objs; | |||
| 1217 | int HeapShared::_num_old_recorded_klasses; | |||
| 1218 | ||||
| 1219 | int HeapShared::_num_total_subgraph_recordings = 0; | |||
| 1220 | int HeapShared::_num_total_walked_objs = 0; | |||
| 1221 | int HeapShared::_num_total_archived_objs = 0; | |||
| 1222 | int HeapShared::_num_total_recorded_klasses = 0; | |||
| 1223 | int HeapShared::_num_total_verifications = 0; | |||
| 1224 | ||||
| 1225 | bool HeapShared::has_been_seen_during_subgraph_recording(oop obj) { | |||
| 1226 | return _seen_objects_table->get(obj) != NULL__null; | |||
| 1227 | } | |||
| 1228 | ||||
| 1229 | void HeapShared::set_has_been_seen_during_subgraph_recording(oop obj) { | |||
| 1230 | assert(!has_been_seen_during_subgraph_recording(obj), "sanity")do { if (!(!has_been_seen_during_subgraph_recording(obj))) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1230, "assert(" "!has_been_seen_during_subgraph_recording(obj)" ") failed", "sanity"); ::breakpoint(); } } while (0); | |||
| 1231 | _seen_objects_table->put(obj, true); | |||
| 1232 | ++ _num_new_walked_objs; | |||
| 1233 | } | |||
| 1234 | ||||
| 1235 | void HeapShared::start_recording_subgraph(InstanceKlass *k, const char* class_name, bool is_full_module_graph) { | |||
| 1236 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Start recording subgraph(s) for archived fields in %s", class_name); | |||
| 1237 | init_subgraph_info(k, is_full_module_graph); | |||
| 1238 | init_seen_objects_table(); | |||
| 1239 | _num_new_walked_objs = 0; | |||
| 1240 | _num_new_archived_objs = 0; | |||
| 1241 | _num_old_recorded_klasses = get_subgraph_info(k)->num_subgraph_object_klasses(); | |||
| 1242 | } | |||
| 1243 | ||||
| 1244 | void HeapShared::done_recording_subgraph(InstanceKlass *k, const char* class_name) { | |||
| 1245 | int num_new_recorded_klasses = get_subgraph_info(k)->num_subgraph_object_klasses() - | |||
| 1246 | _num_old_recorded_klasses; | |||
| 1247 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Done recording subgraph(s) for archived fields in %s: " | |||
| 1248 | "walked %d objs, archived %d new objs, recorded %d classes", | |||
| 1249 | class_name, _num_new_walked_objs, _num_new_archived_objs, | |||
| 1250 | num_new_recorded_klasses); | |||
| 1251 | ||||
| 1252 | delete_seen_objects_table(); | |||
| 1253 | ||||
| 1254 | _num_total_subgraph_recordings ++; | |||
| 1255 | _num_total_walked_objs += _num_new_walked_objs; | |||
| 1256 | _num_total_archived_objs += _num_new_archived_objs; | |||
| 1257 | _num_total_recorded_klasses += num_new_recorded_klasses; | |||
| 1258 | } | |||
| 1259 | ||||
| 1260 | class ArchivableStaticFieldFinder: public FieldClosure { | |||
| 1261 | InstanceKlass* _ik; | |||
| 1262 | Symbol* _field_name; | |||
| 1263 | bool _found; | |||
| 1264 | int _offset; | |||
| 1265 | public: | |||
| 1266 | ArchivableStaticFieldFinder(InstanceKlass* ik, Symbol* field_name) : | |||
| 1267 | _ik(ik), _field_name(field_name), _found(false), _offset(-1) {} | |||
| 1268 | ||||
| 1269 | virtual void do_field(fieldDescriptor* fd) { | |||
| 1270 | if (fd->name() == _field_name) { | |||
| 1271 | assert(!_found, "fields cannot be overloaded")do { if (!(!_found)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1271, "assert(" "!_found" ") failed", "fields cannot be overloaded" ); ::breakpoint(); } } while (0); | |||
| 1272 | assert(is_reference_type(fd->field_type()), "can archive only fields that are references")do { if (!(is_reference_type(fd->field_type()))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1272, "assert(" "is_reference_type(fd->field_type())" ") failed" , "can archive only fields that are references"); ::breakpoint (); } } while (0); | |||
| 1273 | _found = true; | |||
| 1274 | _offset = fd->offset(); | |||
| 1275 | } | |||
| 1276 | } | |||
| 1277 | bool found() { return _found; } | |||
| 1278 | int offset() { return _offset; } | |||
| 1279 | }; | |||
| 1280 | ||||
| 1281 | void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], | |||
| 1282 | int num, TRAPSJavaThread* __the_thread__) { | |||
| 1283 | for (int i = 0; i < num; i++) { | |||
| 1284 | ArchivableStaticFieldInfo* info = &fields[i]; | |||
| 1285 | TempNewSymbol klass_name = SymbolTable::new_symbol(info->klass_name); | |||
| 1286 | TempNewSymbol field_name = SymbolTable::new_symbol(info->field_name); | |||
| 1287 | ||||
| 1288 | Klass* k = SystemDictionary::resolve_or_fail(klass_name, true, CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 1289 | InstanceKlass* ik = InstanceKlass::cast(k); | |||
| 1290 | assert(InstanceKlass::cast(ik)->is_shared_boot_class(),do { if (!(InstanceKlass::cast(ik)->is_shared_boot_class() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1291, "assert(" "InstanceKlass::cast(ik)->is_shared_boot_class()" ") failed", "Only support boot classes"); ::breakpoint(); } } while (0) | |||
| 1291 | "Only support boot classes")do { if (!(InstanceKlass::cast(ik)->is_shared_boot_class() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1291, "assert(" "InstanceKlass::cast(ik)->is_shared_boot_class()" ") failed", "Only support boot classes"); ::breakpoint(); } } while (0); | |||
| 1292 | ik->initialize(CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 1293 | ||||
| 1294 | ArchivableStaticFieldFinder finder(ik, field_name); | |||
| 1295 | ik->do_local_static_fields(&finder); | |||
| 1296 | assert(finder.found(), "field must exist")do { if (!(finder.found())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1296, "assert(" "finder.found()" ") failed", "field must exist" ); ::breakpoint(); } } while (0); | |||
| 1297 | ||||
| 1298 | info->klass = ik; | |||
| 1299 | info->offset = finder.offset(); | |||
| 1300 | } | |||
| 1301 | } | |||
| 1302 | ||||
| 1303 | void HeapShared::init_subgraph_entry_fields(TRAPSJavaThread* __the_thread__) { | |||
| 1304 | assert(HeapShared::can_write(), "must be")do { if (!(HeapShared::can_write())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1304, "assert(" "HeapShared::can_write()" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 1305 | _dump_time_subgraph_info_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeKlassSubGraphInfoTable(); | |||
| 1306 | init_subgraph_entry_fields(closed_archive_subgraph_entry_fields, | |||
| 1307 | num_closed_archive_subgraph_entry_fields, | |||
| 1308 | CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 1309 | init_subgraph_entry_fields(open_archive_subgraph_entry_fields, | |||
| 1310 | num_open_archive_subgraph_entry_fields, | |||
| 1311 | CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 1312 | if (MetaspaceShared::use_full_module_graph()) { | |||
| 1313 | init_subgraph_entry_fields(fmg_open_archive_subgraph_entry_fields, | |||
| 1314 | num_fmg_open_archive_subgraph_entry_fields, | |||
| 1315 | CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 1316 | } | |||
| 1317 | } | |||
| 1318 | ||||
| 1319 | void HeapShared::init_for_dumping(TRAPSJavaThread* __the_thread__) { | |||
| 1320 | if (HeapShared::can_write()) { | |||
| 1321 | _dumped_interned_strings = new (ResourceObj::C_HEAP, mtClass)DumpedInternedStrings(); | |||
| 1322 | init_subgraph_entry_fields(CHECK__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return ; (void)(0); | |||
| 1323 | } | |||
| 1324 | } | |||
| 1325 | ||||
| 1326 | void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], | |||
| 1327 | int num, bool is_closed_archive, | |||
| 1328 | bool is_full_module_graph) { | |||
| 1329 | _num_total_subgraph_recordings = 0; | |||
| 1330 | _num_total_walked_objs = 0; | |||
| 1331 | _num_total_archived_objs = 0; | |||
| 1332 | _num_total_recorded_klasses = 0; | |||
| 1333 | _num_total_verifications = 0; | |||
| 1334 | ||||
| 1335 | // For each class X that has one or more archived fields: | |||
| 1336 | // [1] Dump the subgraph of each archived field | |||
| 1337 | // [2] Create a list of all the class of the objects that can be reached | |||
| 1338 | // by any of these static fields. | |||
| 1339 | // At runtime, these classes are initialized before X's archived fields | |||
| 1340 | // are restored by HeapShared::initialize_from_archived_subgraph(). | |||
| 1341 | int i; | |||
| 1342 | for (i = 0; i < num; ) { | |||
| 1343 | ArchivableStaticFieldInfo* info = &fields[i]; | |||
| 1344 | const char* klass_name = info->klass_name; | |||
| 1345 | start_recording_subgraph(info->klass, klass_name, is_full_module_graph); | |||
| 1346 | ||||
| 1347 | // If you have specified consecutive fields of the same klass in | |||
| 1348 | // fields[], these will be archived in the same | |||
| 1349 | // {start_recording_subgraph ... done_recording_subgraph} pass to | |||
| 1350 | // save time. | |||
| 1351 | for (; i < num; i++) { | |||
| 1352 | ArchivableStaticFieldInfo* f = &fields[i]; | |||
| 1353 | if (f->klass_name != klass_name) { | |||
| 1354 | break; | |||
| 1355 | } | |||
| 1356 | ||||
| 1357 | archive_reachable_objects_from_static_field(f->klass, f->klass_name, | |||
| 1358 | f->offset, f->field_name, | |||
| 1359 | is_closed_archive); | |||
| 1360 | } | |||
| 1361 | done_recording_subgraph(info->klass, klass_name); | |||
| 1362 | } | |||
| 1363 | ||||
| 1364 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Archived subgraph records in %s archive heap region = %d", | |||
| 1365 | is_closed_archive ? "closed" : "open", | |||
| 1366 | _num_total_subgraph_recordings); | |||
| 1367 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>(" Walked %d objects", _num_total_walked_objs); | |||
| 1368 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>(" Archived %d objects", _num_total_archived_objs); | |||
| 1369 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>(" Recorded %d klasses", _num_total_recorded_klasses); | |||
| 1370 | ||||
| 1371 | #ifndef PRODUCT | |||
| 1372 | for (int i = 0; i < num; i++) { | |||
| 1373 | ArchivableStaticFieldInfo* f = &fields[i]; | |||
| 1374 | verify_subgraph_from_static_field(f->klass, f->offset); | |||
| 1375 | } | |||
| 1376 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>(" Verified %d references", _num_total_verifications); | |||
| 1377 | #endif | |||
| 1378 | } | |||
| 1379 | ||||
| 1380 | // Not all the strings in the global StringTable are dumped into the archive, because | |||
| 1381 | // some of those strings may be only referenced by classes that are excluded from | |||
| 1382 | // the archive. We need to explicitly mark the strings that are: | |||
| 1383 | // [1] used by classes that WILL be archived; | |||
| 1384 | // [2] included in the SharedArchiveConfigFile. | |||
| 1385 | void HeapShared::add_to_dumped_interned_strings(oop string) { | |||
| 1386 | assert_at_safepoint()do { if (!(SafepointSynchronize::is_at_safepoint())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1386, "assert(" "SafepointSynchronize::is_at_safepoint()" ") failed" , "should be at a safepoint"); ::breakpoint(); } } while (0); // DumpedInternedStrings uses raw oops | |||
| 1387 | bool created; | |||
| 1388 | _dumped_interned_strings->put_if_absent(string, true, &created); | |||
| 1389 | } | |||
| 1390 | ||||
| 1391 | // At dump-time, find the location of all the non-null oop pointers in an archived heap | |||
| 1392 | // region. This way we can quickly relocate all the pointers without using | |||
| 1393 | // BasicOopIterateClosure at runtime. | |||
| 1394 | class FindEmbeddedNonNullPointers: public BasicOopIterateClosure { | |||
| 1395 | narrowOop* _start; | |||
| 1396 | BitMap *_oopmap; | |||
| 1397 | int _num_total_oops; | |||
| 1398 | int _num_null_oops; | |||
| 1399 | public: | |||
| 1400 | FindEmbeddedNonNullPointers(narrowOop* start, BitMap* oopmap) | |||
| 1401 | : _start(start), _oopmap(oopmap), _num_total_oops(0), _num_null_oops(0) {} | |||
| 1402 | ||||
| 1403 | virtual void do_oop(narrowOop* p) { | |||
| 1404 | _num_total_oops ++; | |||
| 1405 | narrowOop v = *p; | |||
| 1406 | if (!CompressedOops::is_null(v)) { | |||
| 1407 | size_t idx = p - _start; | |||
| 1408 | _oopmap->set_bit(idx); | |||
| 1409 | } else { | |||
| 1410 | _num_null_oops ++; | |||
| 1411 | } | |||
| 1412 | } | |||
| 1413 | virtual void do_oop(oop *p) { | |||
| 1414 | ShouldNotReachHere()do { (*g_assert_poison) = 'X';; report_should_not_reach_here( "/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1414); ::breakpoint(); } while (0); | |||
| 1415 | } | |||
| 1416 | int num_total_oops() const { return _num_total_oops; } | |||
| 1417 | int num_null_oops() const { return _num_null_oops; } | |||
| 1418 | }; | |||
| 1419 | ||||
| 1420 | ResourceBitMap HeapShared::calculate_oopmap(MemRegion region) { | |||
| 1421 | assert(UseCompressedOops, "must be")do { if (!(UseCompressedOops)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1421, "assert(" "UseCompressedOops" ") failed", "must be"); ::breakpoint(); } } while (0); | |||
| 1422 | size_t num_bits = region.byte_size() / sizeof(narrowOop); | |||
| 1423 | ResourceBitMap oopmap(num_bits); | |||
| 1424 | ||||
| 1425 | HeapWord* p = region.start(); | |||
| 1426 | HeapWord* end = region.end(); | |||
| 1427 | FindEmbeddedNonNullPointers finder((narrowOop*)p, &oopmap); | |||
| 1428 | ArchiveBuilder* builder = DumpSharedSpaces ? ArchiveBuilder::current() : NULL__null; | |||
| 1429 | ||||
| 1430 | int num_objs = 0; | |||
| 1431 | while (p < end) { | |||
| 1432 | oop o = cast_to_oop(p); | |||
| 1433 | o->oop_iterate(&finder); | |||
| 1434 | p += o->size(); | |||
| 1435 | if (DumpSharedSpaces) { | |||
| 1436 | builder->relocate_klass_ptr(o); | |||
| ||||
| 1437 | } | |||
| 1438 | ++ num_objs; | |||
| 1439 | } | |||
| 1440 | ||||
| 1441 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("calculate_oopmap: objects = %6d, embedded oops = %7d, nulls = %7d", | |||
| 1442 | num_objs, finder.num_total_oops(), finder.num_null_oops()); | |||
| 1443 | return oopmap; | |||
| 1444 | } | |||
| 1445 | ||||
| 1446 | // Patch all the embedded oop pointers inside an archived heap region, | |||
| 1447 | // to be consistent with the runtime oop encoding. | |||
| 1448 | class PatchEmbeddedPointers: public BitMapClosure { | |||
| 1449 | narrowOop* _start; | |||
| 1450 | ||||
| 1451 | public: | |||
| 1452 | PatchEmbeddedPointers(narrowOop* start) : _start(start) {} | |||
| 1453 | ||||
| 1454 | bool do_bit(size_t offset) { | |||
| 1455 | narrowOop* p = _start + offset; | |||
| 1456 | narrowOop v = *p; | |||
| 1457 | assert(!CompressedOops::is_null(v), "null oops should have been filtered out at dump time")do { if (!(!CompressedOops::is_null(v))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1457, "assert(" "!CompressedOops::is_null(v)" ") failed", "null oops should have been filtered out at dump time" ); ::breakpoint(); } } while (0); | |||
| 1458 | oop o = HeapShared::decode_from_archive(v); | |||
| 1459 | RawAccess<IS_NOT_NULL>::oop_store(p, o); | |||
| 1460 | return true; | |||
| 1461 | } | |||
| 1462 | }; | |||
| 1463 | ||||
| 1464 | // Patch all the non-null pointers that are embedded in the archived heap objects | |||
| 1465 | // in this region | |||
| 1466 | void HeapShared::patch_embedded_pointers(MemRegion region, address oopmap, | |||
| 1467 | size_t oopmap_size_in_bits) { | |||
| 1468 | BitMapView bm((BitMap::bm_word_t*)oopmap, oopmap_size_in_bits); | |||
| 1469 | ||||
| 1470 | #ifndef PRODUCT | |||
| 1471 | ResourceMark rm; | |||
| 1472 | ResourceBitMap checkBm = calculate_oopmap(region); | |||
| ||||
| 1473 | assert(bm.is_same(checkBm), "sanity")do { if (!(bm.is_same(checkBm))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1473, "assert(" "bm.is_same(checkBm)" ") failed", "sanity") ; ::breakpoint(); } } while (0); | |||
| 1474 | #endif | |||
| 1475 | ||||
| 1476 | PatchEmbeddedPointers patcher((narrowOop*)region.start()); | |||
| 1477 | bm.iterate(&patcher); | |||
| 1478 | } | |||
| 1479 | ||||
| 1480 | // The CDS archive remembers each heap object by its address at dump time, but | |||
| 1481 | // the heap object may be loaded at a different address at run time. This structure is used | |||
| 1482 | // to translate the dump time addresses for all objects in FileMapInfo::space_at(region_index) | |||
| 1483 | // to their runtime addresses. | |||
| 1484 | struct LoadedArchiveHeapRegion { | |||
| 1485 | int _region_index; // index for FileMapInfo::space_at(index) | |||
| 1486 | size_t _region_size; // number of bytes in this region | |||
| 1487 | uintptr_t _dumptime_base; // The dump-time (decoded) address of the first object in this region | |||
| 1488 | intx _runtime_offset; // If an object's dump time address P is within in this region, its | |||
| 1489 | // runtime address is P + _runtime_offset | |||
| 1490 | ||||
| 1491 | static int comparator(const void* a, const void* b) { | |||
| 1492 | LoadedArchiveHeapRegion* reg_a = (LoadedArchiveHeapRegion*)a; | |||
| 1493 | LoadedArchiveHeapRegion* reg_b = (LoadedArchiveHeapRegion*)b; | |||
| 1494 | if (reg_a->_dumptime_base < reg_b->_dumptime_base) { | |||
| 1495 | return -1; | |||
| 1496 | } else if (reg_a->_dumptime_base == reg_b->_dumptime_base) { | |||
| 1497 | return 0; | |||
| 1498 | } else { | |||
| 1499 | return 1; | |||
| 1500 | } | |||
| 1501 | } | |||
| 1502 | ||||
| 1503 | uintptr_t top() { | |||
| 1504 | return _dumptime_base + _region_size; | |||
| 1505 | } | |||
| 1506 | }; | |||
| 1507 | ||||
| 1508 | void HeapShared::init_loaded_heap_relocation(LoadedArchiveHeapRegion* loaded_regions, | |||
| 1509 | int num_loaded_regions) { | |||
| 1510 | _dumptime_base_0 = loaded_regions[0]._dumptime_base; | |||
| 1511 | _dumptime_base_1 = loaded_regions[1]._dumptime_base; | |||
| 1512 | _dumptime_base_2 = loaded_regions[2]._dumptime_base; | |||
| 1513 | _dumptime_base_3 = loaded_regions[3]._dumptime_base; | |||
| 1514 | _dumptime_top = loaded_regions[num_loaded_regions-1].top(); | |||
| 1515 | ||||
| 1516 | _runtime_offset_0 = loaded_regions[0]._runtime_offset; | |||
| 1517 | _runtime_offset_1 = loaded_regions[1]._runtime_offset; | |||
| 1518 | _runtime_offset_2 = loaded_regions[2]._runtime_offset; | |||
| 1519 | _runtime_offset_3 = loaded_regions[3]._runtime_offset; | |||
| 1520 | ||||
| 1521 | assert(2 <= num_loaded_regions && num_loaded_regions <= 4, "must be")do { if (!(2 <= num_loaded_regions && num_loaded_regions <= 4)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1521, "assert(" "2 <= num_loaded_regions && num_loaded_regions <= 4" ") failed", "must be"); ::breakpoint(); } } while (0); | |||
| 1522 | if (num_loaded_regions < 4) { | |||
| 1523 | _dumptime_base_3 = UINTPTR_MAX(18446744073709551615UL); | |||
| 1524 | } | |||
| 1525 | if (num_loaded_regions < 3) { | |||
| 1526 | _dumptime_base_2 = UINTPTR_MAX(18446744073709551615UL); | |||
| 1527 | } | |||
| 1528 | } | |||
| 1529 | ||||
| 1530 | bool HeapShared::can_load() { | |||
| 1531 | return Universe::heap()->can_load_archived_objects(); | |||
| 1532 | } | |||
| 1533 | ||||
| 1534 | template <int NUM_LOADED_REGIONS> | |||
| 1535 | class PatchLoadedRegionPointers: public BitMapClosure { | |||
| 1536 | narrowOop* _start; | |||
| 1537 | intx _offset_0; | |||
| 1538 | intx _offset_1; | |||
| 1539 | intx _offset_2; | |||
| 1540 | intx _offset_3; | |||
| 1541 | uintptr_t _base_0; | |||
| 1542 | uintptr_t _base_1; | |||
| 1543 | uintptr_t _base_2; | |||
| 1544 | uintptr_t _base_3; | |||
| 1545 | uintptr_t _top; | |||
| 1546 | ||||
| 1547 | static_assert(MetaspaceShared::max_num_heap_regions == 4, "can't handle more than 4 regions"); | |||
| 1548 | static_assert(NUM_LOADED_REGIONS >= 2, "we have at least 2 loaded regions"); | |||
| 1549 | static_assert(NUM_LOADED_REGIONS <= 4, "we have at most 4 loaded regions"); | |||
| 1550 | ||||
| 1551 | public: | |||
| 1552 | PatchLoadedRegionPointers(narrowOop* start, LoadedArchiveHeapRegion* loaded_regions) | |||
| 1553 | : _start(start), | |||
| 1554 | _offset_0(loaded_regions[0]._runtime_offset), | |||
| 1555 | _offset_1(loaded_regions[1]._runtime_offset), | |||
| 1556 | _offset_2(loaded_regions[2]._runtime_offset), | |||
| 1557 | _offset_3(loaded_regions[3]._runtime_offset), | |||
| 1558 | _base_0(loaded_regions[0]._dumptime_base), | |||
| 1559 | _base_1(loaded_regions[1]._dumptime_base), | |||
| 1560 | _base_2(loaded_regions[2]._dumptime_base), | |||
| 1561 | _base_3(loaded_regions[3]._dumptime_base) { | |||
| 1562 | _top = loaded_regions[NUM_LOADED_REGIONS-1].top(); | |||
| 1563 | } | |||
| 1564 | ||||
| 1565 | bool do_bit(size_t offset) { | |||
| 1566 | narrowOop* p = _start + offset; | |||
| 1567 | narrowOop v = *p; | |||
| 1568 | assert(!CompressedOops::is_null(v), "null oops should have been filtered out at dump time")do { if (!(!CompressedOops::is_null(v))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1568, "assert(" "!CompressedOops::is_null(v)" ") failed", "null oops should have been filtered out at dump time" ); ::breakpoint(); } } while (0); | |||
| 1569 | uintptr_t o = cast_from_oop<uintptr_t>(HeapShared::decode_from_archive(v)); | |||
| 1570 | assert(_base_0 <= o && o < _top, "must be")do { if (!(_base_0 <= o && o < _top)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1570, "assert(" "_base_0 <= o && o < _top" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 1571 | ||||
| 1572 | ||||
| 1573 | // We usually have only 2 regions for the default archive. Use template to avoid unnecessary comparisons. | |||
| 1574 | if (NUM_LOADED_REGIONS > 3 && o >= _base_3) { | |||
| 1575 | o += _offset_3; | |||
| 1576 | } else if (NUM_LOADED_REGIONS > 2 && o >= _base_2) { | |||
| 1577 | o += _offset_2; | |||
| 1578 | } else if (o >= _base_1) { | |||
| 1579 | o += _offset_1; | |||
| 1580 | } else { | |||
| 1581 | o += _offset_0; | |||
| 1582 | } | |||
| 1583 | HeapShared::assert_in_loaded_heap(o); | |||
| 1584 | RawAccess<IS_NOT_NULL>::oop_store(p, cast_to_oop(o)); | |||
| 1585 | return true; | |||
| 1586 | } | |||
| 1587 | }; | |||
| 1588 | ||||
| 1589 | int HeapShared::init_loaded_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, | |||
| 1590 | MemRegion& archive_space) { | |||
| 1591 | size_t total_bytes = 0; | |||
| 1592 | int num_loaded_regions = 0; | |||
| 1593 | for (int i = MetaspaceShared::first_archive_heap_region; | |||
| 1594 | i <= MetaspaceShared::last_archive_heap_region; i++) { | |||
| 1595 | FileMapRegion* r = mapinfo->space_at(i); | |||
| 1596 | r->assert_is_heap_region(); | |||
| 1597 | if (r->used() > 0) { | |||
| 1598 | assert(is_aligned(r->used(), HeapWordSize), "must be")do { if (!(is_aligned(r->used(), HeapWordSize))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1598, "assert(" "is_aligned(r->used(), HeapWordSize)" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 1599 | total_bytes += r->used(); | |||
| 1600 | LoadedArchiveHeapRegion* ri = &loaded_regions[num_loaded_regions++]; | |||
| 1601 | ri->_region_index = i; | |||
| 1602 | ri->_region_size = r->used(); | |||
| 1603 | ri->_dumptime_base = (uintptr_t)mapinfo->start_address_as_decoded_from_archive(r); | |||
| 1604 | } | |||
| 1605 | } | |||
| 1606 | ||||
| 1607 | assert(is_aligned(total_bytes, HeapWordSize), "must be")do { if (!(is_aligned(total_bytes, HeapWordSize))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1607, "assert(" "is_aligned(total_bytes, HeapWordSize)" ") failed" , "must be"); ::breakpoint(); } } while (0); | |||
| 1608 | size_t word_size = total_bytes / HeapWordSize; | |||
| 1609 | HeapWord* buffer = Universe::heap()->allocate_loaded_archive_space(word_size); | |||
| 1610 | if (buffer == nullptr) { | |||
| 1611 | return 0; | |||
| 1612 | } | |||
| 1613 | ||||
| 1614 | archive_space = MemRegion(buffer, word_size); | |||
| 1615 | _loaded_heap_bottom = (uintptr_t)archive_space.start(); | |||
| 1616 | _loaded_heap_top = _loaded_heap_bottom + total_bytes; | |||
| 1617 | ||||
| 1618 | return num_loaded_regions; | |||
| 1619 | } | |||
| 1620 | ||||
| 1621 | void HeapShared::sort_loaded_regions(LoadedArchiveHeapRegion* loaded_regions, int num_loaded_regions, | |||
| 1622 | uintptr_t buffer) { | |||
| 1623 | // Find the relocation offset of the pointers in each region | |||
| 1624 | qsort(loaded_regions, num_loaded_regions, sizeof(LoadedArchiveHeapRegion), | |||
| 1625 | LoadedArchiveHeapRegion::comparator); | |||
| 1626 | ||||
| 1627 | uintptr_t p = buffer; | |||
| 1628 | for (int i = 0; i < num_loaded_regions; i++) { | |||
| 1629 | // This region will be loaded at p, so all objects inside this | |||
| 1630 | // region will be shifted by ri->offset | |||
| 1631 | LoadedArchiveHeapRegion* ri = &loaded_regions[i]; | |||
| 1632 | ri->_runtime_offset = p - ri->_dumptime_base; | |||
| 1633 | p += ri->_region_size; | |||
| 1634 | } | |||
| 1635 | assert(p == _loaded_heap_top, "must be")do { if (!(p == _loaded_heap_top)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1635, "assert(" "p == _loaded_heap_top" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 1636 | } | |||
| 1637 | ||||
| 1638 | bool HeapShared::load_regions(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_regions, | |||
| 1639 | int num_loaded_regions, uintptr_t buffer) { | |||
| 1640 | uintptr_t bitmap_base = (uintptr_t)mapinfo->map_bitmap_region(); | |||
| 1641 | uintptr_t load_address = buffer; | |||
| 1642 | for (int i = 0; i < num_loaded_regions; i++) { | |||
| 1643 | LoadedArchiveHeapRegion* ri = &loaded_regions[i]; | |||
| 1644 | FileMapRegion* r = mapinfo->space_at(ri->_region_index); | |||
| 1645 | ||||
| 1646 | if (!mapinfo->read_region(ri->_region_index, (char*)load_address, r->used(), /* do_commit = */ false)) { | |||
| 1647 | // There's no easy way to free the buffer, so we will fill it with zero later | |||
| 1648 | // in fill_failed_loaded_region(), and it will eventually be GC'ed. | |||
| 1649 | log_warning(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Warning))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Warning>("Loading of heap region %d has failed. Archived objects are disabled", i); | |||
| 1650 | _loading_failed = true; | |||
| 1651 | return false; | |||
| 1652 | } | |||
| 1653 | log_info(cds)(!(LogImpl<(LogTag::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Loaded heap region #%d at base " INTPTR_FORMAT"0x%016" "l" "x" " top " INTPTR_FORMAT"0x%016" "l" "x" | |||
| 1654 | " size " SIZE_FORMAT_W(6)"%" "6" "l" "u" " delta " INTX_FORMAT"%" "l" "d", | |||
| 1655 | ri->_region_index, load_address, load_address + ri->_region_size, | |||
| 1656 | ri->_region_size, ri->_runtime_offset); | |||
| 1657 | ||||
| 1658 | uintptr_t oopmap = bitmap_base + r->oopmap_offset(); | |||
| 1659 | BitMapView bm((BitMap::bm_word_t*)oopmap, r->oopmap_size_in_bits()); | |||
| 1660 | ||||
| 1661 | if (num_loaded_regions == 4) { | |||
| 1662 | PatchLoadedRegionPointers<4> patcher((narrowOop*)load_address, loaded_regions); | |||
| 1663 | bm.iterate(&patcher); | |||
| 1664 | } else if (num_loaded_regions == 3) { | |||
| 1665 | PatchLoadedRegionPointers<3> patcher((narrowOop*)load_address, loaded_regions); | |||
| 1666 | bm.iterate(&patcher); | |||
| 1667 | } else { | |||
| 1668 | assert(num_loaded_regions == 2, "must be")do { if (!(num_loaded_regions == 2)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1668, "assert(" "num_loaded_regions == 2" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 1669 | PatchLoadedRegionPointers<2> patcher((narrowOop*)load_address, loaded_regions); | |||
| 1670 | bm.iterate(&patcher); | |||
| 1671 | } | |||
| 1672 | ||||
| 1673 | load_address += r->used(); | |||
| 1674 | } | |||
| 1675 | ||||
| 1676 | return true; | |||
| 1677 | } | |||
| 1678 | ||||
| 1679 | bool HeapShared::load_heap_regions(FileMapInfo* mapinfo) { | |||
| 1680 | init_narrow_oop_decoding(mapinfo->narrow_oop_base(), mapinfo->narrow_oop_shift()); | |||
| 1681 | ||||
| 1682 | LoadedArchiveHeapRegion loaded_regions[MetaspaceShared::max_num_heap_regions]; | |||
| 1683 | memset(loaded_regions, 0, sizeof(loaded_regions)); | |||
| 1684 | ||||
| 1685 | MemRegion archive_space; | |||
| 1686 | int num_loaded_regions = init_loaded_regions(mapinfo, loaded_regions, archive_space); | |||
| 1687 | if (num_loaded_regions <= 0) { | |||
| 1688 | return false; | |||
| 1689 | } | |||
| 1690 | sort_loaded_regions(loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start()); | |||
| 1691 | if (!load_regions(mapinfo, loaded_regions, num_loaded_regions, (uintptr_t)archive_space.start())) { | |||
| 1692 | assert(_loading_failed, "must be")do { if (!(_loading_failed)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1692, "assert(" "_loading_failed" ") failed", "must be"); :: breakpoint(); } } while (0); | |||
| 1693 | return false; | |||
| 1694 | } | |||
| 1695 | ||||
| 1696 | init_loaded_heap_relocation(loaded_regions, num_loaded_regions); | |||
| 1697 | _is_loaded = true; | |||
| 1698 | set_roots(mapinfo->heap_obj_roots()); | |||
| 1699 | ||||
| 1700 | return true; | |||
| 1701 | } | |||
| 1702 | ||||
| 1703 | class VerifyLoadedHeapEmbeddedPointers: public BasicOopIterateClosure { | |||
| 1704 | ResourceHashtable<uintptr_t, bool>* _table; | |||
| 1705 | ||||
| 1706 | public: | |||
| 1707 | VerifyLoadedHeapEmbeddedPointers(ResourceHashtable<uintptr_t, bool>* table) : _table(table) {} | |||
| 1708 | ||||
| 1709 | virtual void do_oop(narrowOop* p) { | |||
| 1710 | // This should be called before the loaded regions are modified, so all the embedded pointers | |||
| 1711 | // must be NULL, or must point to a valid object in the loaded regions. | |||
| 1712 | narrowOop v = *p; | |||
| 1713 | if (!CompressedOops::is_null(v)) { | |||
| 1714 | oop o = CompressedOops::decode_not_null(v); | |||
| 1715 | uintptr_t u = cast_from_oop<uintptr_t>(o); | |||
| 1716 | HeapShared::assert_in_loaded_heap(u); | |||
| 1717 | guarantee(_table->contains(u), "must point to beginning of object in loaded archived regions")do { if (!(_table->contains(u))) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1717, "guarantee(" "_table->contains(u)" ") failed", "must point to beginning of object in loaded archived regions" ); ::breakpoint(); } } while (0); | |||
| 1718 | } | |||
| 1719 | } | |||
| 1720 | virtual void do_oop(oop* p) { | |||
| 1721 | ShouldNotReachHere()do { (*g_assert_poison) = 'X';; report_should_not_reach_here( "/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1721); ::breakpoint(); } while (0); | |||
| 1722 | } | |||
| 1723 | }; | |||
| 1724 | ||||
| 1725 | void HeapShared::finish_initialization() { | |||
| 1726 | if (is_loaded()) { | |||
| 1727 | HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; | |||
| 1728 | HeapWord* top = (HeapWord*)_loaded_heap_top; | |||
| 1729 | ||||
| 1730 | MemRegion archive_space = MemRegion(bottom, top); | |||
| 1731 | Universe::heap()->complete_loaded_archive_space(archive_space); | |||
| 1732 | } | |||
| 1733 | ||||
| 1734 | if (VerifyArchivedFields <= 0 || !is_loaded()) { | |||
| 1735 | return; | |||
| 1736 | } | |||
| 1737 | ||||
| 1738 | log_info(cds, heap)(!(LogImpl<(LogTag::_cds), (LogTag::_heap), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_cds), (LogTag::_heap), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>("Verify all oops and pointers in loaded heap"); | |||
| 1739 | ||||
| 1740 | ResourceMark rm; | |||
| 1741 | ResourceHashtable<uintptr_t, bool> table; | |||
| 1742 | VerifyLoadedHeapEmbeddedPointers verifier(&table); | |||
| 1743 | HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; | |||
| 1744 | HeapWord* top = (HeapWord*)_loaded_heap_top; | |||
| 1745 | ||||
| 1746 | for (HeapWord* p = bottom; p < top; ) { | |||
| 1747 | oop o = cast_to_oop(p); | |||
| 1748 | table.put(cast_from_oop<uintptr_t>(o), true); | |||
| 1749 | p += o->size(); | |||
| 1750 | } | |||
| 1751 | ||||
| 1752 | for (HeapWord* p = bottom; p < top; ) { | |||
| 1753 | oop o = cast_to_oop(p); | |||
| 1754 | o->oop_iterate(&verifier); | |||
| 1755 | p += o->size(); | |||
| 1756 | } | |||
| 1757 | } | |||
| 1758 | ||||
| 1759 | void HeapShared::fill_failed_loaded_region() { | |||
| 1760 | assert(_loading_failed, "must be")do { if (!(_loading_failed)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1760, "assert(" "_loading_failed" ") failed", "must be"); :: breakpoint(); } } while (0); | |||
| 1761 | if (_loaded_heap_bottom != 0) { | |||
| 1762 | assert(_loaded_heap_top != 0, "must be")do { if (!(_loaded_heap_top != 0)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/cds/heapShared.cpp" , 1762, "assert(" "_loaded_heap_top != 0" ") failed", "must be" ); ::breakpoint(); } } while (0); | |||
| 1763 | HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; | |||
| 1764 | HeapWord* top = (HeapWord*)_loaded_heap_top; | |||
| 1765 | Universe::heap()->fill_with_objects(bottom, top - bottom); | |||
| 1766 | } | |||
| 1767 | } | |||
| 1768 | ||||
| 1769 | #endif // INCLUDE_CDS_JAVA_HEAP |
| 1 | /* |
| 2 | * Copyright (c) 1997, 2021, 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_OOPS_OOP_INLINE_HPP |
| 26 | #define SHARE_OOPS_OOP_INLINE_HPP |
| 27 | |
| 28 | #include "oops/oop.hpp" |
| 29 | |
| 30 | #include "memory/universe.hpp" |
| 31 | #include "oops/access.inline.hpp" |
| 32 | #include "oops/arrayKlass.hpp" |
| 33 | #include "oops/arrayOop.hpp" |
| 34 | #include "oops/compressedOops.inline.hpp" |
| 35 | #include "oops/markWord.hpp" |
| 36 | #include "oops/oopsHierarchy.hpp" |
| 37 | #include "runtime/atomic.hpp" |
| 38 | #include "runtime/globals.hpp" |
| 39 | #include "utilities/align.hpp" |
| 40 | #include "utilities/debug.hpp" |
| 41 | #include "utilities/macros.hpp" |
| 42 | #include "utilities/globalDefinitions.hpp" |
| 43 | |
| 44 | // Implementation of all inlined member functions defined in oop.hpp |
| 45 | // We need a separate file to avoid circular references |
| 46 | |
| 47 | markWord oopDesc::mark() const { |
| 48 | return Atomic::load(&_mark); |
| 49 | } |
| 50 | |
| 51 | markWord oopDesc::mark_acquire() const { |
| 52 | return Atomic::load_acquire(&_mark); |
| 53 | } |
| 54 | |
| 55 | markWord* oopDesc::mark_addr() const { |
| 56 | return (markWord*) &_mark; |
| 57 | } |
| 58 | |
| 59 | void oopDesc::set_mark(markWord m) { |
| 60 | Atomic::store(&_mark, m); |
| 61 | } |
| 62 | |
| 63 | void oopDesc::set_mark(HeapWord* mem, markWord m) { |
| 64 | *(markWord*)(((char*)mem) + mark_offset_in_bytes()) = m; |
| 65 | } |
| 66 | |
| 67 | void oopDesc::release_set_mark(markWord m) { |
| 68 | Atomic::release_store(&_mark, m); |
| 69 | } |
| 70 | |
| 71 | markWord oopDesc::cas_set_mark(markWord new_mark, markWord old_mark) { |
| 72 | return Atomic::cmpxchg(&_mark, old_mark, new_mark); |
| 73 | } |
| 74 | |
| 75 | markWord oopDesc::cas_set_mark(markWord new_mark, markWord old_mark, atomic_memory_order order) { |
| 76 | return Atomic::cmpxchg(&_mark, old_mark, new_mark, order); |
| 77 | } |
| 78 | |
| 79 | void oopDesc::init_mark() { |
| 80 | set_mark(markWord::prototype()); |
| 81 | } |
| 82 | |
| 83 | Klass* oopDesc::klass() const { |
| 84 | if (UseCompressedClassPointers) { |
| 85 | return CompressedKlassPointers::decode_not_null(_metadata._compressed_klass); |
| 86 | } else { |
| 87 | return _metadata._klass; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | Klass* oopDesc::klass_or_null() const { |
| 92 | if (UseCompressedClassPointers) { |
| 93 | return CompressedKlassPointers::decode(_metadata._compressed_klass); |
| 94 | } else { |
| 95 | return _metadata._klass; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | Klass* oopDesc::klass_or_null_acquire() const { |
| 100 | if (UseCompressedClassPointers) { |
| 101 | narrowKlass nklass = Atomic::load_acquire(&_metadata._compressed_klass); |
| 102 | return CompressedKlassPointers::decode(nklass); |
| 103 | } else { |
| 104 | return Atomic::load_acquire(&_metadata._klass); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | void oopDesc::set_klass(Klass* k) { |
| 109 | assert(Universe::is_bootstrapping() || (k != NULL && k->is_klass()), "incorrect Klass")do { if (!(Universe::is_bootstrapping() || (k != __null && k->is_klass()))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 109, "assert(" "Universe::is_bootstrapping() || (k != __null && k->is_klass())" ") failed", "incorrect Klass"); ::breakpoint(); } } while (0 ); |
| 110 | if (UseCompressedClassPointers) { |
| 111 | _metadata._compressed_klass = CompressedKlassPointers::encode_not_null(k); |
| 112 | } else { |
| 113 | _metadata._klass = k; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | void oopDesc::release_set_klass(HeapWord* mem, Klass* k) { |
| 118 | assert(Universe::is_bootstrapping() || (k != NULL && k->is_klass()), "incorrect Klass")do { if (!(Universe::is_bootstrapping() || (k != __null && k->is_klass()))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 118, "assert(" "Universe::is_bootstrapping() || (k != __null && k->is_klass())" ") failed", "incorrect Klass"); ::breakpoint(); } } while (0 ); |
| 119 | char* raw_mem = ((char*)mem + klass_offset_in_bytes()); |
| 120 | if (UseCompressedClassPointers) { |
| 121 | Atomic::release_store((narrowKlass*)raw_mem, |
| 122 | CompressedKlassPointers::encode_not_null(k)); |
| 123 | } else { |
| 124 | Atomic::release_store((Klass**)raw_mem, k); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | int oopDesc::klass_gap() const { |
| 129 | return *(int*)(((intptr_t)this) + klass_gap_offset_in_bytes()); |
| 130 | } |
| 131 | |
| 132 | void oopDesc::set_klass_gap(HeapWord* mem, int v) { |
| 133 | if (UseCompressedClassPointers) { |
| 134 | *(int*)(((char*)mem) + klass_gap_offset_in_bytes()) = v; |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | void oopDesc::set_klass_gap(int v) { |
| 139 | set_klass_gap((HeapWord*)this, v); |
| 140 | } |
| 141 | |
| 142 | bool oopDesc::is_a(Klass* k) const { |
| 143 | return klass()->is_subtype_of(k); |
| 144 | } |
| 145 | |
| 146 | size_t oopDesc::size() { |
| 147 | return size_given_klass(klass()); |
| 148 | } |
| 149 | |
| 150 | size_t oopDesc::size_given_klass(Klass* klass) { |
| 151 | int lh = klass->layout_helper(); |
| 152 | size_t s; |
| 153 | |
| 154 | // lh is now a value computed at class initialization that may hint |
| 155 | // at the size. For instances, this is positive and equal to the |
| 156 | // size. For arrays, this is negative and provides log2 of the |
| 157 | // array element size. For other oops, it is zero and thus requires |
| 158 | // a virtual call. |
| 159 | // |
| 160 | // We go to all this trouble because the size computation is at the |
| 161 | // heart of phase 2 of mark-compaction, and called for every object, |
| 162 | // alive or dead. So the speed here is equal in importance to the |
| 163 | // speed of allocation. |
| 164 | |
| 165 | if (lh > Klass::_lh_neutral_value) { |
| 166 | if (!Klass::layout_helper_needs_slow_path(lh)) { |
| 167 | s = lh >> LogHeapWordSize; // deliver size scaled by wordSize |
| 168 | } else { |
| 169 | s = klass->oop_size(this); |
| 170 | } |
| 171 | } else if (lh <= Klass::_lh_neutral_value) { |
| 172 | // The most common case is instances; fall through if so. |
| 173 | if (lh < Klass::_lh_neutral_value) { |
| 174 | // Second most common case is arrays. We have to fetch the |
| 175 | // length of the array, shift (multiply) it appropriately, |
| 176 | // up to wordSize, add the header, and align to object size. |
| 177 | size_t size_in_bytes; |
| 178 | size_t array_length = (size_t) ((arrayOop)this)->length(); |
| 179 | size_in_bytes = array_length << Klass::layout_helper_log2_element_size(lh); |
| 180 | size_in_bytes += Klass::layout_helper_header_size(lh); |
| 181 | |
| 182 | // This code could be simplified, but by keeping array_header_in_bytes |
| 183 | // in units of bytes and doing it this way we can round up just once, |
| 184 | // skipping the intermediate round to HeapWordSize. |
| 185 | s = align_up(size_in_bytes, MinObjAlignmentInBytes) / HeapWordSize; |
| 186 | |
| 187 | // UseParallelGC and UseG1GC can change the length field |
| 188 | // of an "old copy" of an object array in the young gen so it indicates |
| 189 | // the grey portion of an already copied array. This will cause the first |
| 190 | // disjunct below to fail if the two comparands are computed across such |
| 191 | // a concurrent change. |
| 192 | assert((s == klass->oop_size(this)) ||do { if (!((s == klass->oop_size(this)) || (Universe::is_gc_active () && is_objArray() && is_forwarded() && (get_UseParallelGC() || get_UseG1GC())))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 194, "assert(" "(s == klass->oop_size(this)) || (Universe::is_gc_active() && is_objArray() && is_forwarded() && (get_UseParallelGC() || get_UseG1GC()))" ") failed", "wrong array object size"); ::breakpoint(); } } while (0) |
| 193 | (Universe::is_gc_active() && is_objArray() && is_forwarded() && (get_UseParallelGC() || get_UseG1GC())),do { if (!((s == klass->oop_size(this)) || (Universe::is_gc_active () && is_objArray() && is_forwarded() && (get_UseParallelGC() || get_UseG1GC())))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 194, "assert(" "(s == klass->oop_size(this)) || (Universe::is_gc_active() && is_objArray() && is_forwarded() && (get_UseParallelGC() || get_UseG1GC()))" ") failed", "wrong array object size"); ::breakpoint(); } } while (0) |
| 194 | "wrong array object size")do { if (!((s == klass->oop_size(this)) || (Universe::is_gc_active () && is_objArray() && is_forwarded() && (get_UseParallelGC() || get_UseG1GC())))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 194, "assert(" "(s == klass->oop_size(this)) || (Universe::is_gc_active() && is_objArray() && is_forwarded() && (get_UseParallelGC() || get_UseG1GC()))" ") failed", "wrong array object size"); ::breakpoint(); } } while (0); |
| 195 | } else { |
| 196 | // Must be zero, so bite the bullet and take the virtual call. |
| 197 | s = klass->oop_size(this); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | assert(s > 0, "Oop size must be greater than zero, not " SIZE_FORMAT, s)do { if (!(s > 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 201, "assert(" "s > 0" ") failed", "Oop size must be greater than zero, not " "%" "l" "u", s); ::breakpoint(); } } while (0); |
| 202 | assert(is_object_aligned(s), "Oop size is not properly aligned: " SIZE_FORMAT, s)do { if (!(is_object_aligned(s))) { (*g_assert_poison) = 'X'; ; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 202, "assert(" "is_object_aligned(s)" ") failed", "Oop size is not properly aligned: " "%" "l" "u", s); ::breakpoint(); } } while (0); |
| 203 | return s; |
| 204 | } |
| 205 | |
| 206 | bool oopDesc::is_instance() const { return klass()->is_instance_klass(); } |
| 207 | bool oopDesc::is_array() const { return klass()->is_array_klass(); } |
| 208 | bool oopDesc::is_objArray() const { return klass()->is_objArray_klass(); } |
| 209 | bool oopDesc::is_typeArray() const { return klass()->is_typeArray_klass(); } |
| 210 | |
| 211 | template<typename T> |
| 212 | T* oopDesc::field_addr(int offset) const { return reinterpret_cast<T*>(cast_from_oop<intptr_t>(as_oop()) + offset); } |
| 213 | |
| 214 | template <typename T> |
| 215 | size_t oopDesc::field_offset(T* p) const { return pointer_delta((void*)p, (void*)this, 1); } |
| 216 | |
| 217 | template <DecoratorSet decorators> |
| 218 | inline oop oopDesc::obj_field_access(int offset) const { return HeapAccess<decorators>::oop_load_at(as_oop(), offset); } |
| 219 | inline oop oopDesc::obj_field(int offset) const { return HeapAccess<>::oop_load_at(as_oop(), offset); } |
| 220 | |
| 221 | inline void oopDesc::obj_field_put(int offset, oop value) { HeapAccess<>::oop_store_at(as_oop(), offset, value); } |
| 222 | |
| 223 | inline jbyte oopDesc::byte_field(int offset) const { return *field_addr<jbyte>(offset); } |
| 224 | inline void oopDesc::byte_field_put(int offset, jbyte value) { *field_addr<jbyte>(offset) = value; } |
| 225 | |
| 226 | inline jchar oopDesc::char_field(int offset) const { return *field_addr<jchar>(offset); } |
| 227 | inline void oopDesc::char_field_put(int offset, jchar value) { *field_addr<jchar>(offset) = value; } |
| 228 | |
| 229 | inline jboolean oopDesc::bool_field(int offset) const { return *field_addr<jboolean>(offset); } |
| 230 | inline void oopDesc::bool_field_put(int offset, jboolean value) { *field_addr<jboolean>(offset) = jboolean(value & 1); } |
| 231 | inline jboolean oopDesc::bool_field_volatile(int offset) const { return RawAccess<MO_SEQ_CST>::load(field_addr<jboolean>(offset)); } |
| 232 | inline void oopDesc::bool_field_put_volatile(int offset, jboolean value) { RawAccess<MO_SEQ_CST>::store(field_addr<jboolean>(offset), jboolean(value & 1)); } |
| 233 | inline jshort oopDesc::short_field(int offset) const { return *field_addr<jshort>(offset); } |
| 234 | inline void oopDesc::short_field_put(int offset, jshort value) { *field_addr<jshort>(offset) = value; } |
| 235 | |
| 236 | inline jint oopDesc::int_field(int offset) const { return *field_addr<jint>(offset); } |
| 237 | inline void oopDesc::int_field_put(int offset, jint value) { *field_addr<jint>(offset) = value; } |
| 238 | |
| 239 | inline jlong oopDesc::long_field(int offset) const { return *field_addr<jlong>(offset); } |
| 240 | inline void oopDesc::long_field_put(int offset, jlong value) { *field_addr<jlong>(offset) = value; } |
| 241 | |
| 242 | inline jfloat oopDesc::float_field(int offset) const { return *field_addr<jfloat>(offset); } |
| 243 | inline void oopDesc::float_field_put(int offset, jfloat value) { *field_addr<jfloat>(offset) = value; } |
| 244 | |
| 245 | inline jdouble oopDesc::double_field(int offset) const { return *field_addr<jdouble>(offset); } |
| 246 | inline void oopDesc::double_field_put(int offset, jdouble value) { *field_addr<jdouble>(offset) = value; } |
| 247 | |
| 248 | bool oopDesc::is_locked() const { |
| 249 | return mark().is_locked(); |
| 250 | } |
| 251 | |
| 252 | bool oopDesc::is_unlocked() const { |
| 253 | return mark().is_unlocked(); |
| 254 | } |
| 255 | |
| 256 | // Used only for markSweep, scavenging |
| 257 | bool oopDesc::is_gc_marked() const { |
| 258 | return mark().is_marked(); |
| 259 | } |
| 260 | |
| 261 | // Used by scavengers |
| 262 | bool oopDesc::is_forwarded() const { |
| 263 | // The extra heap check is needed since the obj might be locked, in which case the |
| 264 | // mark would point to a stack location and have the sentinel bit cleared |
| 265 | return mark().is_marked(); |
| 266 | } |
| 267 | |
| 268 | // Used by scavengers |
| 269 | void oopDesc::forward_to(oop p) { |
| 270 | verify_forwardee(p); |
| 271 | markWord m = markWord::encode_pointer_as_mark(p); |
| 272 | assert(m.decode_pointer() == p, "encoding must be reversable")do { if (!(m.decode_pointer() == p)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 272, "assert(" "m.decode_pointer() == p" ") failed", "encoding must be reversable" ); ::breakpoint(); } } while (0); |
| 273 | set_mark(m); |
| 274 | } |
| 275 | |
| 276 | oop oopDesc::forward_to_atomic(oop p, markWord compare, atomic_memory_order order) { |
| 277 | verify_forwardee(p); |
| 278 | markWord m = markWord::encode_pointer_as_mark(p); |
| 279 | assert(m.decode_pointer() == p, "encoding must be reversable")do { if (!(m.decode_pointer() == p)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 279, "assert(" "m.decode_pointer() == p" ") failed", "encoding must be reversable" ); ::breakpoint(); } } while (0); |
| 280 | markWord old_mark = cas_set_mark(m, compare, order); |
| 281 | if (old_mark == compare) { |
| 282 | return NULL__null; |
| 283 | } else { |
| 284 | return cast_to_oop(old_mark.decode_pointer()); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | // Note that the forwardee is not the same thing as the displaced_mark. |
| 289 | // The forwardee is used when copying during scavenge and mark-sweep. |
| 290 | // It does need to clear the low two locking- and GC-related bits. |
| 291 | oop oopDesc::forwardee() const { |
| 292 | assert(is_forwarded(), "only decode when actually forwarded")do { if (!(is_forwarded())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 292, "assert(" "is_forwarded()" ") failed", "only decode when actually forwarded" ); ::breakpoint(); } } while (0); |
| 293 | return cast_to_oop(mark().decode_pointer()); |
| 294 | } |
| 295 | |
| 296 | // The following method needs to be MT safe. |
| 297 | uint oopDesc::age() const { |
| 298 | assert(!mark().is_marked(), "Attempt to read age from forwarded mark")do { if (!(!mark().is_marked())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 298, "assert(" "!mark().is_marked()" ") failed", "Attempt to read age from forwarded mark" ); ::breakpoint(); } } while (0); |
| 299 | if (has_displaced_mark()) { |
| 300 | return displaced_mark().age(); |
| 301 | } else { |
| 302 | return mark().age(); |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | void oopDesc::incr_age() { |
| 307 | assert(!mark().is_marked(), "Attempt to increment age of forwarded mark")do { if (!(!mark().is_marked())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 307, "assert(" "!mark().is_marked()" ") failed", "Attempt to increment age of forwarded mark" ); ::breakpoint(); } } while (0); |
| 308 | if (has_displaced_mark()) { |
| 309 | set_displaced_mark(displaced_mark().incr_age()); |
| 310 | } else { |
| 311 | set_mark(mark().incr_age()); |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | template <typename OopClosureType> |
| 316 | void oopDesc::oop_iterate(OopClosureType* cl) { |
| 317 | OopIteratorClosureDispatch::oop_oop_iterate(cl, this, klass()); |
| 318 | } |
| 319 | |
| 320 | template <typename OopClosureType> |
| 321 | void oopDesc::oop_iterate(OopClosureType* cl, MemRegion mr) { |
| 322 | OopIteratorClosureDispatch::oop_oop_iterate(cl, this, klass(), mr); |
| 323 | } |
| 324 | |
| 325 | template <typename OopClosureType> |
| 326 | size_t oopDesc::oop_iterate_size(OopClosureType* cl) { |
| 327 | Klass* k = klass(); |
| 328 | size_t size = size_given_klass(k); |
| 329 | OopIteratorClosureDispatch::oop_oop_iterate(cl, this, k); |
| 330 | return size; |
| 331 | } |
| 332 | |
| 333 | template <typename OopClosureType> |
| 334 | size_t oopDesc::oop_iterate_size(OopClosureType* cl, MemRegion mr) { |
| 335 | Klass* k = klass(); |
| 336 | size_t size = size_given_klass(k); |
| 337 | OopIteratorClosureDispatch::oop_oop_iterate(cl, this, k, mr); |
| 338 | return size; |
| 339 | } |
| 340 | |
| 341 | template <typename OopClosureType> |
| 342 | void oopDesc::oop_iterate_backwards(OopClosureType* cl) { |
| 343 | oop_iterate_backwards(cl, klass()); |
| 344 | } |
| 345 | |
| 346 | template <typename OopClosureType> |
| 347 | void oopDesc::oop_iterate_backwards(OopClosureType* cl, Klass* k) { |
| 348 | assert(k == klass(), "wrong klass")do { if (!(k == klass())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/oops/oop.inline.hpp" , 348, "assert(" "k == klass()" ") failed", "wrong klass"); :: breakpoint(); } } while (0); |
| 349 | OopIteratorClosureDispatch::oop_oop_iterate_backwards(cl, this, k); |
| 350 | } |
| 351 | |
| 352 | bool oopDesc::is_instanceof_or_null(oop obj, Klass* klass) { |
| 353 | return obj == NULL__null || obj->klass()->is_subtype_of(klass); |
| 354 | } |
| 355 | |
| 356 | intptr_t oopDesc::identity_hash() { |
| 357 | // Fast case; if the object is unlocked and the hash value is set, no locking is needed |
| 358 | // Note: The mark must be read into local variable to avoid concurrent updates. |
| 359 | markWord mrk = mark(); |
| 360 | if (mrk.is_unlocked() && !mrk.has_no_hash()) { |
| 361 | return mrk.hash(); |
| 362 | } else if (mrk.is_marked()) { |
| 363 | return mrk.hash(); |
| 364 | } else { |
| 365 | return slow_identity_hash(); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | bool oopDesc::has_displaced_mark() const { |
| 370 | return mark().has_displaced_mark_helper(); |
| 371 | } |
| 372 | |
| 373 | markWord oopDesc::displaced_mark() const { |
| 374 | return mark().displaced_mark_helper(); |
| 375 | } |
| 376 | |
| 377 | void oopDesc::set_displaced_mark(markWord m) { |
| 378 | mark().set_displaced_mark_helper(m); |
| 379 | } |
| 380 | |
| 381 | bool oopDesc::mark_must_be_preserved() const { |
| 382 | return mark_must_be_preserved(mark()); |
| 383 | } |
| 384 | |
| 385 | bool oopDesc::mark_must_be_preserved(markWord m) const { |
| 386 | return m.must_be_preserved(this); |
| 387 | } |
| 388 | |
| 389 | #endif // SHARE_OOPS_OOP_INLINE_HPP |