/* * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// CDSHeapVerifier is used to check for problems where an archived object references a // static field that may be reinitialized at runtime. In the following example, // Foo.get.test() // correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled. // // class Foo { // final Foo archivedFoo; // this field is archived by CDS // Bar bar; // static { // CDS.initializeFromArchive(Foo.class); // if (archivedFoo == null) { // archivedFoo = new Foo(); // archivedFoo.bar = Bar.bar; // } // } // static Foo get() { return archivedFoo; } // boolean test() { // return bar == Bar.bar; // } // } // // class Bar { // // this field is initialized in both CDS dump time and runtime. // static final Bar bar = new Bar(); // } // // The check itself is simple: // [1] CDSHeapVerifier::do_klass() collects all static fields // [2] CDSHeapVerifier::do_entry() checks all the archived objects. None of them // should be in [1] // // However, it's legal for *some* static fields to be references. This leads to the // table of ADD_EXCL below. // // [A] In most of the cases, the module bootstrap code will update the static field // to point to part of the archived module graph. E.g., // - java/lang/System::bootLayer // - jdk/internal/loader/ClassLoaders::BOOT_LOADER // [B] A final static String that's explicitly initialized inside <clinit>, but // its value is deterministic and is always the same string literal. // [C] A non-final static string that is assigned a string literal during class // initialization; this string is never changed during -Xshare:dump. // [D] Simple caches whose value doesn't matter. // [E] Other cases (see comments in-line below).
// Unfortunately this needs to be manually maintained. If // test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedEnumTest.java fails, // you might need to fix the core library code, or fix the ADD_EXCL entries below. // // class field type
ADD_EXCL("java/lang/ClassLoader", "scl"); // A
ADD_EXCL("java/lang/invoke/InvokerBytecodeGenerator", "DONTINLINE_SIG", // B "FORCEINLINE_SIG", // B "HIDDEN_SIG", // B "INJECTEDPROFILE_SIG", // B "LF_COMPILED_SIG"); // B
ADD_EXCL("java/lang/Module", "ALL_UNNAMED_MODULE", // A "ALL_UNNAMED_MODULE_SET", // A "EVERYONE_MODULE", // A "EVERYONE_SET"); // A
// This is the same as java/util/ImmutableCollections::EMPTY_SET, which is archived
ADD_EXCL("java/lang/reflect/AccessFlag$Location", "EMPTY_SET"); // E
ADD_EXCL("java/lang/System", "bootLayer"); // A
ADD_EXCL("java/lang/VersionProps", "VENDOR_URL_BUG", // C "VENDOR_URL_VM_BUG", // C "VENDOR_VERSION"); // C
ADD_EXCL("java/net/URL$DefaultFactory", "PREFIX"); // B FIXME: JDK-8276561
// A dummy object used by HashSet. The value doesn't matter and it's never // tested for equality.
ADD_EXCL("java/util/HashSet", "PRESENT"); // E
ADD_EXCL("jdk/internal/loader/BuiltinClassLoader", "packageToModule"); // A
ADD_EXCL("jdk/internal/loader/ClassLoaders", "BOOT_LOADER", // A "APP_LOADER", // A "PLATFORM_LOADER"); // A
ADD_EXCL("jdk/internal/loader/URLClassPath", "JAVA_VERSION"); // B
ADD_EXCL("jdk/internal/module/Builder", "cachedVersion"); // D
ADD_EXCL("jdk/internal/module/ModuleLoaderMap$Mapper", "APP_CLASSLOADER", // A "APP_LOADER_INDEX", // A "PLATFORM_CLASSLOADER", // A "PLATFORM_LOADER_INDEX"); // A
ADD_EXCL("jdk/internal/module/ServicesCatalog", "CLV"); // A
// This just points to an empty Map
ADD_EXCL("jdk/internal/reflect/Reflection", "methodFilterMap"); // E
ADD_EXCL("jdk/internal/util/StaticProperty", "FILE_ENCODING", // C "JAVA_LOCALE_USE_OLD_ISO_CODES"); // C
// Integer for 0 and 1 are in java/lang/Integer$IntegerCache and are archived
ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E "ZERO_INT"); // E
ADD_EXCL("sun/security/util/SecurityConstants", "PROVIDER_VER"); // C
# undef ADD_EXCL
ClassLoaderDataGraph::classes_do(this);
}
CDSHeapVerifier::~CDSHeapVerifier() { if (_problems > 0) {
log_warning(cds, heap)("Scanned %d objects. Found %d case(s) where " "an object points to a static field that may be " "reinitialized at runtime.", _archived_objs, _problems);
}
}
oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset()); if (static_obj_field != NULL) {
Klass* klass = static_obj_field->klass(); if (_exclusions != NULL) { for (constchar** p = _exclusions; *p != NULL; p++) { if (fd->name()->equals(*p)) { return;
}
}
}
if (fd->is_final() && java_lang_String::is_instance(static_obj_field) && fd->has_initial_value()) { // This field looks like like this in the Java source: // static final SOME_STRING = "a string literal"; // This string literal has been stored in the shared string table, so it's OK // for the archived objects to refer to it. return;
} if (fd->is_final() && java_lang_Class::is_instance(static_obj_field)) { // This field points to an archived mirror. return;
} if (klass->has_archived_enum_objs()) { // This klass is a subclass of java.lang.Enum. If any instance of this klass // has been archived, we will archive all static fields of this klass. // See HeapShared::initialize_enum_klass(). return;
}
// This field *may* be initialized to a different value at runtime. Remember it // and check later if it appears in the archived object graph.
_verifier->add_static_obj_field(_ik, static_obj_field, fd->name());
}
}
};
// Remember all the static object fields of every class that are currently // loaded. void CDSHeapVerifier::do_klass(Klass* k) { if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
if (HeapShared::is_subgraph_root_class(ik)) { // ik is inside one of the ArchivableStaticFieldInfo tables // in heapShared.cpp. We assume such classes are programmed to // update their static fields correctly at runtime. return;
}
StaticFieldInfo* info = _table.get(orig_obj); if (info != NULL) {
ResourceMark rm;
LogStream ls(Log(cds, heap)::warning());
ls.print_cr("Archive heap points to a static field that may be reinitialized at runtime:");
ls.print_cr("Field: %s::%s", info->_holder->name()->as_C_string(), info->_name->as_C_string());
ls.print("Value: ");
orig_obj->print_on(&ls);
ls.print_cr("--- trace begin ---");
trace_to_root(orig_obj, NULL, &value);
ls.print_cr("--- trace end ---");
ls.cr();
_problems ++;
}
returntrue; /* keep on iterating */
}
class CDSHeapVerifier::TraceFields : public FieldClosure {
oop _orig_obj;
oop _orig_field;
LogStream* _ls;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.