1 /*
  2  * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "cds/aotReferenceObjSupport.hpp"
 26 #include "cds/heapShared.hpp"
 27 #include "classfile/javaClasses.hpp"
 28 #include "classfile/symbolTable.hpp"
 29 #include "classfile/systemDictionary.hpp"
 30 #include "classfile/vmSymbols.hpp"
 31 #include "logging/log.hpp"
 32 #include "memory/resourceArea.hpp"
 33 #include "memory/universe.hpp"
 34 #include "oops/oop.inline.hpp"
 35 #include "oops/oopHandle.inline.hpp"
 36 #include "runtime/fieldDescriptor.inline.hpp"
 37 #include "runtime/javaCalls.hpp"
 38 #include "utilities/hashTable.hpp"
 39 
 40 // Handling of java.lang.ref.Reference objects in the AOT cache
 41 // ============================================================
 42 //
 43 // When AOTArtifactFinder finds an oop which is a instance of java.lang.ref.Reference:
 44 //
 45 // - We check if the oop is eligible to be stored in the AOT cache. If not, the AOT cache
 46 //   creation fails -- see AOTReferenceObjSupport::check_if_ref_obj()
 47 //
 48 // - Otherwise, we store the oop into the AOT cache, but we unconditionally reset its
 49 //   "next" and "discovered" fields to null. Otherwise, if AOTArtifactFinder follows these
 50 //   fields, it may found unrelated objects that we don't intend to cache.
 51 //
 52 // Eligibility
 53 // ===========
 54 //
 55 // [1] A reference that does not require special clean up (i.e., Reference::queue == ReferenceQueue.NULL_QUEUE)
 56 //     is eligible.
 57 //
 58 // [2] A reference that REQUIRE specials clean up (i.e., Reference::queue != ReferenceQueue.NULL_QUEUE)
 59 //     is eligible ONLY if its referent is not null.
 60 //
 61 // As of this version, the only oops in group [2] that can be found by AOTArtifactFinder are
 62 // the keys used by ReferencedKeyMap in the implementation of MethodType::internTable.
 63 // stabilize_cached_reference_objects() ensures that all keys found by AOTArtifactFinder are eligible.
 64 //
 65 // The purpose of the error check in check_if_ref_obj() is to guard against changes in the JDK core
 66 // libs that might introduce new types of oops in group [2] into the AOT cache.
 67 //
 68 // Reasons for the eligibility restrictions
 69 // ========================================
 70 //
 71 // Reference handling is complex. In this version, we implement only enough functionality to support
 72 // the use of Weak/Soft references used by java.lang.invoke.
 73 //
 74 // We intend to evolve the implementation in the future by
 75 // -- implementing more assemblySetup() operations for other use cases, and/or
 76 // -- relaxing the eligibility restrictions.
 77 //
 78 //
 79 // null referents for group [1]
 80 // ============================
 81 //
 82 // Any cached reference R1 of group [1] is allowed to have a null referent.
 83 // This can happen in the following situations:
 84 //    (a) R1.clear() was called by Java code during the assembly phase.
 85 //    (b) The referent has been collected, and R1 is in the "pending" state.
 86 // In case (b), the "next" and "discovered" fields of the cached copy of R1 will
 87 // be set to null. During the production run:
 88 //    - It would appear to the Java program as if immediately during VM start-up, the referent
 89 //      was collected and ReferenceThread completed processing of R1.
 90 //    - It would appear to the GC as if immediately during VM start-up, the Java program called
 91 //      R1.clear().
 92 
 93 #if INCLUDE_CDS_JAVA_HEAP
 94 
 95 class KeepAliveObjectsTable : public HashTable<oop, bool,
 96     36137, // prime number
 97     AnyObj::C_HEAP,
 98     mtClassShared,
 99     HeapShared::oop_hash> {};
100 
101 static KeepAliveObjectsTable* _keep_alive_objs_table;
102 static OopHandle _keep_alive_objs_array;
103 static OopHandle _null_queue;
104 
105 bool AOTReferenceObjSupport::is_enabled() {
106   // For simplicity, AOTReferenceObjSupport is enabled only when dumping method handles.
107   // Otherwise we won't see Reference objects in the AOT cache. Let's be conservative now.
108   return CDSConfig::is_dumping_method_handles();
109 }
110 
111 void AOTReferenceObjSupport::initialize(TRAPS) {
112   if (!AOTReferenceObjSupport::is_enabled()) {
113     return;
114   }
115 
116   TempNewSymbol class_name = SymbolTable::new_symbol("java/lang/ref/ReferenceQueue");
117   Klass* k = SystemDictionary::resolve_or_fail(class_name, true, CHECK);
118   InstanceKlass* ik = InstanceKlass::cast(k);
119   ik->initialize(CHECK);
120 
121   TempNewSymbol field_name = SymbolTable::new_symbol("NULL_QUEUE");
122   fieldDescriptor fd;
123   bool found = ik->find_local_field(field_name, vmSymbols::referencequeue_signature(), &fd);
124   precond(found);
125   precond(fd.is_static());
126 
127   _null_queue = OopHandle(Universe::vm_global(), ik->java_mirror()->obj_field(fd.offset()));
128 }
129 
130 // Ensure that all group [2] references found by AOTArtifactFinder are eligible.
131 void AOTReferenceObjSupport::stabilize_cached_reference_objects(TRAPS) {
132   if (AOTReferenceObjSupport::is_enabled()) {
133     // This assert means that the MethodType and MethodTypeForm tables won't be
134     // updated concurrently, so we can remove GC'ed entries ...
135     assert(CDSConfig::allow_only_single_java_thread(), "Required");
136 
137     {
138       TempNewSymbol method_name = SymbolTable::new_symbol("assemblySetup");
139       JavaValue result(T_VOID);
140       JavaCalls::call_static(&result, vmClasses::MethodType_klass(),
141                            method_name,
142                            vmSymbols::void_method_signature(),
143                            CHECK);
144     }
145 
146     {
147       Symbol* cds_name  = vmSymbols::jdk_internal_misc_CDS();
148       Klass* cds_klass = SystemDictionary::resolve_or_fail(cds_name, true /*throw error*/,  CHECK);
149       TempNewSymbol method_name = SymbolTable::new_symbol("getKeepAliveObjects");
150       TempNewSymbol method_sig = SymbolTable::new_symbol("()[Ljava/lang/Object;");
151       JavaValue result(T_OBJECT);
152       JavaCalls::call_static(&result, cds_klass, method_name, method_sig, CHECK);
153 
154       _keep_alive_objs_array = OopHandle(Universe::vm_global(), result.get_oop());
155     }
156 
157     // Trigger a GC to prune eligible referents that were not kept alive
158     Universe::heap()->collect(GCCause::_java_lang_system_gc);
159   }
160 }
161 
162 void AOTReferenceObjSupport::init_keep_alive_objs_table() {
163   assert_at_safepoint(); // _keep_alive_objs_table uses raw oops
164   oop a = _keep_alive_objs_array.resolve();
165   if (a != nullptr) {
166     precond(a->is_objArray());
167     precond(AOTReferenceObjSupport::is_enabled());
168     objArrayOop array = objArrayOop(a);
169 
170     _keep_alive_objs_table = new (mtClass)KeepAliveObjectsTable();
171     for (int i = 0; i < array->length(); i++) {
172       oop obj = array->obj_at(i);
173       _keep_alive_objs_table->put(obj, true); // The array may have duplicated entries but that's OK.
174     }
175   }
176 }
177 
178 // Returns true IFF obj is an instance of java.lang.ref.Reference. If so, perform extra eligibility checks.
179 bool AOTReferenceObjSupport::check_if_ref_obj(oop obj) {
180   assert_at_safepoint(); // _keep_alive_objs_table uses raw oops
181 
182   if (obj->klass()->is_subclass_of(vmClasses::Reference_klass())) {
183     // The following check works only if the java.lang.ref.Reference$ReferenceHandler thread
184     // is not running.
185     //
186     // This code is called on every object found by AOTArtifactFinder. When dumping the
187     // preimage archive, AOTArtifactFinder should not find any Reference objects.
188     precond(!CDSConfig::is_dumping_preimage_static_archive());
189     precond(CDSConfig::allow_only_single_java_thread());
190 
191     precond(AOTReferenceObjSupport::is_enabled());
192     precond(JavaClasses::is_supported_for_archiving(obj));
193     precond(_keep_alive_objs_table != nullptr);
194 
195     // GC needs to know about this load, It will keep referent alive until the current safepoint ends.
196     oop referent = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at(obj, java_lang_ref_Reference::referent_offset());
197 
198     oop queue = obj->obj_field(java_lang_ref_Reference::queue_offset());
199     oop next = java_lang_ref_Reference::next(obj);
200     oop discovered = java_lang_ref_Reference::discovered(obj);
201     bool needs_special_cleanup = (queue != _null_queue.resolve());
202 
203     // If you see the errors below, you probably modified the implementation of java.lang.invoke.
204     // Please check the comments at the top of this file.
205     if (needs_special_cleanup && (referent == nullptr || !_keep_alive_objs_table->contains(referent))) {
206       ResourceMark rm;
207 
208       log_error(aot, heap)("Cannot archive reference object " PTR_FORMAT " of class %s",
209                            p2i(obj), obj->klass()->external_name());
210       log_error(aot, heap)("referent = " PTR_FORMAT
211                            ", queue = " PTR_FORMAT
212                            ", next = " PTR_FORMAT
213                            ", discovered = " PTR_FORMAT,
214                            p2i(referent), p2i(queue), p2i(next), p2i(discovered));
215       log_error(aot, heap)("This object requires special clean up as its queue is not ReferenceQueue::N" "ULL ("
216                            PTR_FORMAT ")", p2i(_null_queue.resolve()));
217       log_error(aot, heap)("%s", (referent == nullptr) ?
218                            "referent cannot be null" : "referent is not registered with CDS.keepAlive()");
219       HeapShared::debug_trace();
220       AOTMetaspace::unrecoverable_writing_error();
221     }
222 
223     if (log_is_enabled(Info, aot, ref)) {
224       ResourceMark rm;
225       log_info(aot, ref)("Reference obj:"
226                          " r=" PTR_FORMAT
227                          " q=" PTR_FORMAT
228                          " n=" PTR_FORMAT
229                          " d=" PTR_FORMAT
230                          " %s",
231                          p2i(referent),
232                          p2i(queue),
233                          p2i(next),
234                          p2i(discovered),
235                          obj->klass()->external_name());
236     }
237     return true;
238   } else {
239     return false;
240   }
241 }
242 
243 bool AOTReferenceObjSupport::skip_field(int field_offset) {
244   return (field_offset == java_lang_ref_Reference::next_offset() ||
245           field_offset == java_lang_ref_Reference::discovered_offset());
246 }
247 
248 #endif // INCLUDE_CDS_JAVA_HEAP