8331497: Implement JEP 483: Ahead-of-Time Class Loading & Linking
Reviewed-by: jrose, kvn, heidinga, asmehra, vlivanov
This commit is contained in:
parent
276251c44a
commit
41a2d49f0a
@ -45,7 +45,7 @@ ifneq ($(TEST_VM_OPTS), )
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
$(eval $(call ParseKeywordVariable, TEST_OPTS, \
|
$(eval $(call ParseKeywordVariable, TEST_OPTS, \
|
||||||
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR JCOV JCOV_DIFF_CHANGESET, \
|
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR JCOV JCOV_DIFF_CHANGESET AOT_JDK, \
|
||||||
STRING_KEYWORDS := VM_OPTIONS JAVA_OPTIONS, \
|
STRING_KEYWORDS := VM_OPTIONS JAVA_OPTIONS, \
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -202,11 +202,12 @@ $(eval $(call SetTestOpt,JOBS,JTREG))
|
|||||||
$(eval $(call SetTestOpt,TIMEOUT_FACTOR,JTREG))
|
$(eval $(call SetTestOpt,TIMEOUT_FACTOR,JTREG))
|
||||||
$(eval $(call SetTestOpt,FAILURE_HANDLER_TIMEOUT,JTREG))
|
$(eval $(call SetTestOpt,FAILURE_HANDLER_TIMEOUT,JTREG))
|
||||||
$(eval $(call SetTestOpt,REPORT,JTREG))
|
$(eval $(call SetTestOpt,REPORT,JTREG))
|
||||||
|
$(eval $(call SetTestOpt,AOT_JDK,JTREG))
|
||||||
|
|
||||||
$(eval $(call ParseKeywordVariable, JTREG, \
|
$(eval $(call ParseKeywordVariable, JTREG, \
|
||||||
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \
|
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \
|
||||||
TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY MAX_MEM RUN_PROBLEM_LISTS \
|
TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY MAX_MEM RUN_PROBLEM_LISTS \
|
||||||
RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
|
RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
|
||||||
STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \
|
STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \
|
||||||
EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \
|
EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \
|
||||||
$(CUSTOM_JTREG_STRING_KEYWORDS), \
|
$(CUSTOM_JTREG_STRING_KEYWORDS), \
|
||||||
@ -702,6 +703,58 @@ define SetJtregValue
|
|||||||
endif
|
endif
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
|
||||||
|
# Parameter 1 is the name of the rule.
|
||||||
|
#
|
||||||
|
# Remaining parameters are named arguments.
|
||||||
|
# VM_OPTIONS List of JVM arguments to use when creating AOT cache
|
||||||
|
#
|
||||||
|
# After calling this, the following variables are defined
|
||||||
|
# $1_AOT_TARGETS List of all targets that the test rule will need to depend on
|
||||||
|
# $1_AOT_JDK_CACHE The AOT cache file to be used to run the test with
|
||||||
|
#
|
||||||
|
SetupAot = $(NamedParamsMacroTemplate)
|
||||||
|
define SetupAotBody
|
||||||
|
$1_AOT_JDK_CONF := $$($1_TEST_SUPPORT_DIR)/aot/jdk.aotconf
|
||||||
|
$1_AOT_JDK_CACHE := $$($1_TEST_SUPPORT_DIR)/aot/jdk.aotcache
|
||||||
|
|
||||||
|
$1_JAVA_TOOL_OPTS := $$(addprefix -J, $$($1_VM_OPTIONS))
|
||||||
|
|
||||||
|
$$($1_AOT_JDK_CACHE): $$(JDK_IMAGE_DIR)/release
|
||||||
|
$$(call MakeDir, $$($1_TEST_SUPPORT_DIR)/aot)
|
||||||
|
|
||||||
|
$(foreach jtool, javac javap jlink jar, \
|
||||||
|
$(info AOT: Create cache configuration for $(jtool)) \
|
||||||
|
$$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/aot.$(jtool), ( \
|
||||||
|
$$(FIXPATH) $(JDK_UNDER_TEST)/bin/$(jtool) $$($1_JAVA_TOOL_OPTS) \
|
||||||
|
-J-XX:AOTMode=record -J-XX:AOTConfiguration=$$($1_AOT_JDK_CONF).$(jtool) --help \
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
$$(info AOT: Copy $(JDK_UNDER_TEST)/lib/classlist to $$($1_AOT_JDK_CONF).jdk )
|
||||||
|
$$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/aot, ( \
|
||||||
|
$$(FIXPATH) $(CP) $(JDK_UNDER_TEST)/lib/classlist $$($1_AOT_JDK_CONF).jdk \
|
||||||
|
))
|
||||||
|
|
||||||
|
$$(FIXPATH) $$(CAT) $$($1_AOT_JDK_CONF).* > $$($1_AOT_JDK_CONF).temp
|
||||||
|
$$(FIXPATH) $$(CAT) $$($1_AOT_JDK_CONF).temp | $(GREP) -v '#' | $(GREP) -v '@' | $(SORT) | \
|
||||||
|
$(SED) -e 's/id:.*//g' | uniq \
|
||||||
|
> $$($1_AOT_JDK_CONF)
|
||||||
|
$$(FIXPATH) $$(CAT) $$($1_AOT_JDK_CONF).temp | $(GREP) '@cp' | $(SORT) \
|
||||||
|
>> $$($1_AOT_JDK_CONF)
|
||||||
|
|
||||||
|
$$(info AOT: Generate AOT cache $$($1_AOT_JDK_CACHE) with flags: $$($1_VM_OPTIONS))
|
||||||
|
$$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/aot, ( \
|
||||||
|
$$(FIXPATH) $(JDK_UNDER_TEST)/bin/java \
|
||||||
|
$$($1_VM_OPTIONS) -Xlog:cds,cds+class=debug:file=$$($1_AOT_JDK_CACHE).log \
|
||||||
|
-XX:AOTMode=create -XX:AOTConfiguration=$$($1_AOT_JDK_CONF) -XX:AOTCache=$$($1_AOT_JDK_CACHE) \
|
||||||
|
))
|
||||||
|
|
||||||
|
$1_AOT_TARGETS += $$($1_AOT_JDK_CACHE)
|
||||||
|
|
||||||
|
endef
|
||||||
|
|
||||||
|
|
||||||
SetupRunJtregTest = $(NamedParamsMacroTemplate)
|
SetupRunJtregTest = $(NamedParamsMacroTemplate)
|
||||||
define SetupRunJtregTestBody
|
define SetupRunJtregTestBody
|
||||||
$1_TEST_RESULTS_DIR := $$(TEST_RESULTS_DIR)/$1
|
$1_TEST_RESULTS_DIR := $$(TEST_RESULTS_DIR)/$1
|
||||||
@ -762,6 +815,7 @@ define SetupRunJtregTestBody
|
|||||||
JTREG_RETRY_COUNT ?= 0
|
JTREG_RETRY_COUNT ?= 0
|
||||||
JTREG_REPEAT_COUNT ?= 0
|
JTREG_REPEAT_COUNT ?= 0
|
||||||
JTREG_REPORT ?= files
|
JTREG_REPORT ?= files
|
||||||
|
JTREG_AOT_JDK ?= false
|
||||||
|
|
||||||
ifneq ($$(JTREG_RETRY_COUNT), 0)
|
ifneq ($$(JTREG_RETRY_COUNT), 0)
|
||||||
ifneq ($$(JTREG_REPEAT_COUNT), 0)
|
ifneq ($$(JTREG_REPEAT_COUNT), 0)
|
||||||
@ -891,6 +945,17 @@ define SetupRunJtregTestBody
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($$(JTREG_AOT_JDK), true)
|
||||||
|
$$(info Add AOT target for $1)
|
||||||
|
$$(eval $$(call SetupAot, $1, VM_OPTIONS := $$(JTREG_ALL_OPTIONS) ))
|
||||||
|
|
||||||
|
$$(info AOT_TARGETS=$$($1_AOT_TARGETS))
|
||||||
|
$$(info AOT_JDK_CACHE=$$($1_AOT_JDK_CACHE))
|
||||||
|
|
||||||
|
$1_JTREG_BASIC_OPTIONS += -vmoption:-XX:AOTCache="$$($1_AOT_JDK_CACHE)"
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
$$(eval $$(call SetupRunJtregTestCustom, $1))
|
$$(eval $$(call SetupRunJtregTestCustom, $1))
|
||||||
|
|
||||||
# SetupRunJtregTestCustom might also adjust JTREG_AUTO_ variables
|
# SetupRunJtregTestCustom might also adjust JTREG_AUTO_ variables
|
||||||
@ -906,6 +971,7 @@ define SetupRunJtregTestBody
|
|||||||
JTREG_TIMEOUT_FACTOR ?= $$(JTREG_AUTO_TIMEOUT_FACTOR)
|
JTREG_TIMEOUT_FACTOR ?= $$(JTREG_AUTO_TIMEOUT_FACTOR)
|
||||||
|
|
||||||
clean-outputdirs-$1:
|
clean-outputdirs-$1:
|
||||||
|
$$(call LogWarn, Clean up dirs for $1)
|
||||||
$$(RM) -r $$($1_TEST_SUPPORT_DIR)
|
$$(RM) -r $$($1_TEST_SUPPORT_DIR)
|
||||||
$$(RM) -r $$($1_TEST_RESULTS_DIR)
|
$$(RM) -r $$($1_TEST_RESULTS_DIR)
|
||||||
|
|
||||||
@ -953,7 +1019,7 @@ define SetupRunJtregTestBody
|
|||||||
done
|
done
|
||||||
endif
|
endif
|
||||||
|
|
||||||
run-test-$1: pre-run-test clean-outputdirs-$1
|
run-test-$1: clean-outputdirs-$1 pre-run-test $$($1_AOT_TARGETS)
|
||||||
$$(call LogWarn)
|
$$(call LogWarn)
|
||||||
$$(call LogWarn, Running test '$$($1_TEST)')
|
$$(call LogWarn, Running test '$$($1_TEST)')
|
||||||
$$(call MakeDir, $$($1_TEST_RESULTS_DIR) $$($1_TEST_SUPPORT_DIR) \
|
$$(call MakeDir, $$($1_TEST_RESULTS_DIR) $$($1_TEST_SUPPORT_DIR) \
|
||||||
|
359
src/hotspot/share/cds/aotClassInitializer.cpp
Normal file
359
src/hotspot/share/cds/aotClassInitializer.cpp
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassInitializer.hpp"
|
||||||
|
#include "cds/archiveBuilder.hpp"
|
||||||
|
#include "cds/cdsConfig.hpp"
|
||||||
|
#include "cds/heapShared.hpp"
|
||||||
|
#include "classfile/vmSymbols.hpp"
|
||||||
|
#include "oops/instanceKlass.inline.hpp"
|
||||||
|
#include "oops/symbol.hpp"
|
||||||
|
#include "runtime/javaCalls.hpp"
|
||||||
|
|
||||||
|
// Detector for class names we wish to handle specially.
|
||||||
|
// It is either an exact string match or a string prefix match.
|
||||||
|
class AOTClassInitializer::AllowedSpec {
|
||||||
|
const char* _class_name;
|
||||||
|
bool _is_prefix;
|
||||||
|
int _len;
|
||||||
|
public:
|
||||||
|
AllowedSpec(const char* class_name, bool is_prefix = false)
|
||||||
|
: _class_name(class_name), _is_prefix(is_prefix)
|
||||||
|
{
|
||||||
|
_len = (class_name == nullptr) ? 0 : (int)strlen(class_name);
|
||||||
|
}
|
||||||
|
const char* class_name() { return _class_name; }
|
||||||
|
|
||||||
|
bool matches(Symbol* name, int len) {
|
||||||
|
assert(_class_name != nullptr, "caller resp.");
|
||||||
|
if (_is_prefix) {
|
||||||
|
return len >= _len && name->starts_with(_class_name);
|
||||||
|
} else {
|
||||||
|
return len == _len && name->equals(_class_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Tell if ik has a name that matches one of the given specs.
|
||||||
|
bool AOTClassInitializer::is_allowed(AllowedSpec* specs, InstanceKlass* ik) {
|
||||||
|
Symbol* name = ik->name();
|
||||||
|
int len = name->utf8_length();
|
||||||
|
for (AllowedSpec* s = specs; s->class_name() != nullptr; s++) {
|
||||||
|
if (s->matches(name, len)) {
|
||||||
|
// If a type is included in the tables inside can_archive_initialized_mirror(), we require that
|
||||||
|
// - all super classes must be included
|
||||||
|
// - all super interfaces that have <clinit> must be included.
|
||||||
|
// This ensures that in the production run, we don't run the <clinit> of a supertype but skips
|
||||||
|
// ik's <clinit>.
|
||||||
|
if (ik->java_super() != nullptr) {
|
||||||
|
DEBUG_ONLY(ResourceMark rm);
|
||||||
|
assert(AOTClassInitializer::can_archive_initialized_mirror(ik->java_super()),
|
||||||
|
"super class %s of %s must be aot-initialized", ik->java_super()->external_name(),
|
||||||
|
ik->external_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<InstanceKlass*>* interfaces = ik->local_interfaces();
|
||||||
|
int len = interfaces->length();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
InstanceKlass* intf = interfaces->at(i);
|
||||||
|
if (intf->class_initializer() != nullptr) {
|
||||||
|
assert(AOTClassInitializer::can_archive_initialized_mirror(intf),
|
||||||
|
"super interface %s (which has <clinit>) of %s must be aot-initialized", intf->external_name(),
|
||||||
|
ik->external_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
|
||||||
|
assert(!ArchiveBuilder::current()->is_in_buffer_space(ik), "must be source klass");
|
||||||
|
if (!CDSConfig::is_initing_classes_at_dump_time()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ik->is_initialized()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ik->is_hidden()) {
|
||||||
|
return HeapShared::is_archivable_hidden_klass(ik);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ik->is_enum_subclass()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// About "static field that may hold a different value" errors:
|
||||||
|
//
|
||||||
|
// Automatic selection for aot-inited classes
|
||||||
|
// ==========================================
|
||||||
|
//
|
||||||
|
// When CDSConfig::is_initing_classes_at_dump_time() is enabled,
|
||||||
|
// HeapShared::find_all_aot_initialized_classes() finds the classes of all
|
||||||
|
// heap objects that are reachable from HeapShared::_run_time_special_subgraph,
|
||||||
|
// and mark these classes as aot-inited. This preserves the initialized
|
||||||
|
// mirrors of these classes, and their <clinit> methods are NOT executed
|
||||||
|
// at runtime.
|
||||||
|
//
|
||||||
|
// For example, with -XX:+AOTInvokeDynamicLinking, _run_time_special_subgraph
|
||||||
|
// will contain some DirectMethodHandle objects. As a result, the DirectMethodHandle
|
||||||
|
// class is automatically marked as aot-inited.
|
||||||
|
//
|
||||||
|
// When a class is aot-inited, its static fields are already set up
|
||||||
|
// by executing the <clinit> method at AOT assembly time. Later on
|
||||||
|
// in the production run, when the class would normally be
|
||||||
|
// initialized, the VM performs guarding and synchronization as if
|
||||||
|
// it were going to run the <clinit> again, but instead it simply
|
||||||
|
// observes that that class was aot-inited. The VM assumes that, if
|
||||||
|
// it were to run <clinit> again, it would get a semantically
|
||||||
|
// equivalent set of final field values, so it just adopts the
|
||||||
|
// existing field values (from AOT assembly) and skips the call to
|
||||||
|
// <clinit>. There may at that point be fixups performed by ad hoc
|
||||||
|
// code, if the VM recognizes a request in the library.
|
||||||
|
//
|
||||||
|
// It is true that this is not generally correct for all possible
|
||||||
|
// Java code. A <clinit> method might have a side effect beyond
|
||||||
|
// initializing the static fields. It might send an email somewhere
|
||||||
|
// noting the current time of day. In that case, such an email
|
||||||
|
// would have been sent during the AOT assembly phase, and the email
|
||||||
|
// would NOT be sent again during production. This is clearly NOT
|
||||||
|
// what a user would want, if this were a general purpose facility.
|
||||||
|
// But in fact it is only for certain well-behaved classes, which
|
||||||
|
// are known NOT to have such side effects. We know this because
|
||||||
|
// the optimization (of skipping <clinit> for aot-init classes) is
|
||||||
|
// only applied to classes fully defined by the JDK.
|
||||||
|
//
|
||||||
|
// (A day may come when we figure out how to gracefully extend this
|
||||||
|
// optimization to untrusted third parties, but it is not this day.)
|
||||||
|
//
|
||||||
|
// Manual selection
|
||||||
|
// ================
|
||||||
|
//
|
||||||
|
// There are important cases where one aot-init class has a side
|
||||||
|
// effect on another aot-class, a side effect which is not captured
|
||||||
|
// in any static field value in either class. The simplest example
|
||||||
|
// is class A forces the initialization of class B. In that case,
|
||||||
|
// we need to aot-init either both classes or neither. From looking
|
||||||
|
// at the JDK state after AOT assembly is done, it is hard to tell
|
||||||
|
// that A "touched" B and B might escape our notice. Another common
|
||||||
|
// example is A copying a field value from B. We don't know where A
|
||||||
|
// got the value, but it would be wrong to re-initialize B at
|
||||||
|
// startup, while keeping the snapshot of the old B value in A. In
|
||||||
|
// general, if we aot-init A, we need to aot-init every class B that
|
||||||
|
// somehow contributed to A's initial state, and every class C that
|
||||||
|
// was somehow side-effected by A's initialization. We say that the
|
||||||
|
// aot-init of A is "init-coupled" to those of B and C.
|
||||||
|
//
|
||||||
|
// So there are init-coupled classes that cannot be automatically discovered. For
|
||||||
|
// example, DirectMethodHandle::IMPL_NAMES points to MethodHandles::IMPL_NAMES,
|
||||||
|
// but the MethodHandles class is not automatically marked because there are
|
||||||
|
// no archived instances of the MethodHandles type.
|
||||||
|
//
|
||||||
|
// If we aot-initialize DirectMethodHandle, but allow MethodHandles to be
|
||||||
|
// initialized at runtime, MethodHandles::IMPL_NAMES will get a different
|
||||||
|
// value than DirectMethodHandle::IMPL_NAMES. This *may or may not* be a problem,
|
||||||
|
// but to ensure compatibility, we should try to preserve the identity equality
|
||||||
|
// of these two fields.
|
||||||
|
//
|
||||||
|
// To do that, we add MethodHandles to the indy_specs[] table below.
|
||||||
|
//
|
||||||
|
// Luckily we do not need to be all-knowing in order to choose which
|
||||||
|
// items to add to that table. We have tools to help detect couplings.
|
||||||
|
//
|
||||||
|
// Automatic validation
|
||||||
|
// ====================
|
||||||
|
//
|
||||||
|
// CDSHeapVerifier is used to detect potential problems with identity equality.
|
||||||
|
//
|
||||||
|
// A class B is assumed to be init-coupled to some aot-init class if
|
||||||
|
// B has a field which points to a live object X in the AOT heap.
|
||||||
|
// The live object X was created by some other class A which somehow
|
||||||
|
// used B's reference to X, perhaps with the help of an intermediate
|
||||||
|
// class Z. Or, B pulled the reference to X from some other class
|
||||||
|
// Y, and B obtained that reference from Y (or an intermediate Z).
|
||||||
|
// It is not certain how X got into the heap, nor whether B
|
||||||
|
// contributed it, but it is a good heuristic that B is init-coupled
|
||||||
|
// to X's class or some other aot-init class. In any case, B should
|
||||||
|
// be made an aot-init class as well, unless a manual inspection
|
||||||
|
// shows that would be a problem. If there is a problem, then the
|
||||||
|
// JDK code for B and/or X probably needs refactoring. If there is
|
||||||
|
// no problem, we add B to the list. Typically the same scan will
|
||||||
|
// find any other accomplices Y, Z, etc. One failure would be a
|
||||||
|
// class Q whose only initialization action is to scribble a special
|
||||||
|
// value into B, from which the value X is derived and then makes
|
||||||
|
// its way into the heap. In that case, the heuristic does not
|
||||||
|
// identify Q. It is (currently) a human responsibility, of JDK
|
||||||
|
// engineers, not to write such dirty JDK code, or to repair it if
|
||||||
|
// it crops up. Eventually we may have tools, or even a user mode
|
||||||
|
// with design rules and checks, that will vet our code base more
|
||||||
|
// automatically.
|
||||||
|
//
|
||||||
|
// To see how the tool detects the problem with MethodHandles::IMPL_NAMES:
|
||||||
|
//
|
||||||
|
// - Comment out all the lines in indy_specs[] except the {nullptr} line.
|
||||||
|
// - Rebuild the JDK
|
||||||
|
//
|
||||||
|
// Then run the following:
|
||||||
|
// java -XX:AOTMode=record -XX:AOTConfiguration=jc.aotconfig com.sun.tools.javac.Main
|
||||||
|
// java -XX:AOTMode=create -Xlog:cds -XX:AOTCache=jc.aot -XX:AOTConfiguration=jc.aotconfig
|
||||||
|
//
|
||||||
|
// You will see an error like this:
|
||||||
|
//
|
||||||
|
// Archive heap points to a static field that may hold a different value at runtime:
|
||||||
|
// Field: java/lang/invoke/MethodHandles::IMPL_NAMES
|
||||||
|
// Value: java.lang.invoke.MemberName$Factory
|
||||||
|
// {0x000000060e906ae8} - klass: 'java/lang/invoke/MemberName$Factory' - flags:
|
||||||
|
//
|
||||||
|
// - ---- fields (total size 2 words):
|
||||||
|
// --- trace begin ---
|
||||||
|
// [ 0] {0x000000060e8deeb0} java.lang.Class (java.lang.invoke.DirectMethodHandle::IMPL_NAMES)
|
||||||
|
// [ 1] {0x000000060e906ae8} java.lang.invoke.MemberName$Factory
|
||||||
|
// --- trace end ---
|
||||||
|
//
|
||||||
|
// Trouble-shooting
|
||||||
|
// ================
|
||||||
|
//
|
||||||
|
// If you see a "static field that may hold a different value" error, it's probably
|
||||||
|
// because you've made some changes in the JDK core libraries (most likely
|
||||||
|
// java.lang.invoke).
|
||||||
|
//
|
||||||
|
// - Did you add a new static field to a class that could be referenced by
|
||||||
|
// cached object instances of MethodType, MethodHandle, etc? You may need
|
||||||
|
// to add that class to indy_specs[].
|
||||||
|
// - Did you modify the <clinit> of the classes in java.lang.invoke such that
|
||||||
|
// a static field now points to an object that should not be cached (e.g.,
|
||||||
|
// a native resource such as a file descriptior, or a Thread)?
|
||||||
|
//
|
||||||
|
// Note that these potential problems only occur when one class gets
|
||||||
|
// the aot-init treatment, AND another class is init-coupled to it,
|
||||||
|
// AND the coupling is not detected. Currently there are a number
|
||||||
|
// classes that get the aot-init treatment, in java.lang.invoke
|
||||||
|
// because of invokedynamic. They are few enough for now to be
|
||||||
|
// manually tracked. There may be more in the future.
|
||||||
|
|
||||||
|
// IS_PREFIX means that we match all class names that start with a
|
||||||
|
// prefix. Otherwise, it is an exact match, of just one class name.
|
||||||
|
const bool IS_PREFIX = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
static AllowedSpec specs[] = {
|
||||||
|
// everybody's favorite super
|
||||||
|
{"java/lang/Object"},
|
||||||
|
|
||||||
|
// above we selected all enums; we must include their super as well
|
||||||
|
{"java/lang/Enum"},
|
||||||
|
{nullptr}
|
||||||
|
};
|
||||||
|
if (is_allowed(specs, ik)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
// This table was created with the help of CDSHeapVerifier.
|
||||||
|
// Also, some $Holder classes are needed. E.g., Invokers.<clinit> explicitly
|
||||||
|
// initializes Invokers$Holder. Since Invokers.<clinit> won't be executed
|
||||||
|
// at runtime, we need to make sure Invokers$Holder is also aot-inited.
|
||||||
|
//
|
||||||
|
// We hope we can reduce the size of this list over time, and move
|
||||||
|
// the responsibility for identifying such classes into the JDK
|
||||||
|
// code itself. See tracking RFE JDK-8342481.
|
||||||
|
static AllowedSpec indy_specs[] = {
|
||||||
|
{"java/lang/constant/ConstantDescs"},
|
||||||
|
{"java/lang/constant/DynamicConstantDesc"},
|
||||||
|
{"java/lang/invoke/BoundMethodHandle"},
|
||||||
|
{"java/lang/invoke/BoundMethodHandle$Specializer"},
|
||||||
|
{"java/lang/invoke/BoundMethodHandle$Species_", IS_PREFIX},
|
||||||
|
{"java/lang/invoke/ClassSpecializer"},
|
||||||
|
{"java/lang/invoke/ClassSpecializer$", IS_PREFIX},
|
||||||
|
{"java/lang/invoke/DelegatingMethodHandle"},
|
||||||
|
{"java/lang/invoke/DelegatingMethodHandle$Holder"}, // UNSAFE.ensureClassInitialized()
|
||||||
|
{"java/lang/invoke/DirectMethodHandle"},
|
||||||
|
{"java/lang/invoke/DirectMethodHandle$Constructor"},
|
||||||
|
{"java/lang/invoke/DirectMethodHandle$Holder"}, // UNSAFE.ensureClassInitialized()
|
||||||
|
{"java/lang/invoke/Invokers"},
|
||||||
|
{"java/lang/invoke/Invokers$Holder"}, // UNSAFE.ensureClassInitialized()
|
||||||
|
{"java/lang/invoke/LambdaForm"},
|
||||||
|
{"java/lang/invoke/LambdaForm$Holder"}, // UNSAFE.ensureClassInitialized()
|
||||||
|
{"java/lang/invoke/LambdaForm$NamedFunction"},
|
||||||
|
{"java/lang/invoke/MethodHandle"},
|
||||||
|
{"java/lang/invoke/MethodHandles"},
|
||||||
|
{"java/lang/invoke/SimpleMethodHandle"},
|
||||||
|
{"java/util/Collections"},
|
||||||
|
{"java/util/stream/Collectors"},
|
||||||
|
{"jdk/internal/constant/ConstantUtils"},
|
||||||
|
{"jdk/internal/constant/PrimitiveClassDescImpl"},
|
||||||
|
{"jdk/internal/constant/ReferenceClassDescImpl"},
|
||||||
|
|
||||||
|
// Can't include this, as it will pull in MethodHandleStatics which has many environment
|
||||||
|
// dependencies (on system properties, etc).
|
||||||
|
// MethodHandleStatics is an example of a class that must NOT get the aot-init treatment,
|
||||||
|
// because of its strong reliance on (a) final fields which are (b) environmentally determined.
|
||||||
|
//{"java/lang/invoke/InvokerBytecodeGenerator"},
|
||||||
|
|
||||||
|
{nullptr}
|
||||||
|
};
|
||||||
|
if (is_allowed(indy_specs, ik)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: currently we have a hard-coded list. We should turn this into
|
||||||
|
// an annotation: @jdk.internal.vm.annotation.RuntimeSetupRequired
|
||||||
|
// See JDK-8342481.
|
||||||
|
bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) {
|
||||||
|
return ik == vmClasses::Class_klass() ||
|
||||||
|
ik == vmClasses::internal_Unsafe_klass() ||
|
||||||
|
ik == vmClasses::ConcurrentHashMap_klass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) {
|
||||||
|
assert(ik->has_aot_initialized_mirror(), "sanity");
|
||||||
|
if (ik->is_runtime_setup_required()) {
|
||||||
|
if (log_is_enabled(Info, cds, init)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_info(cds, init)("Calling %s::runtimeSetup()", ik->external_name());
|
||||||
|
}
|
||||||
|
JavaValue result(T_VOID);
|
||||||
|
JavaCalls::call_static(&result, ik,
|
||||||
|
vmSymbols::runtimeSetup(),
|
||||||
|
vmSymbols::void_method_signature(), current);
|
||||||
|
if (current->has_pending_exception()) {
|
||||||
|
// We cannot continue, as we might have cached instances of ik in the heap, but propagating the
|
||||||
|
// exception would cause ik to be in an error state.
|
||||||
|
AOTLinkedClassBulkLoader::exit_on_exception(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
46
src/hotspot/share/cds/aotClassInitializer.hpp
Normal file
46
src/hotspot/share/cds/aotClassInitializer.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARE_CDS_AOTCLASSINITIALIZER_HPP
|
||||||
|
#define SHARE_CDS_AOTCLASSINITIALIZER_HPP
|
||||||
|
|
||||||
|
#include "memory/allStatic.hpp"
|
||||||
|
#include "utilities/exceptions.hpp"
|
||||||
|
|
||||||
|
class InstanceKlass;
|
||||||
|
|
||||||
|
class AOTClassInitializer : AllStatic {
|
||||||
|
class AllowedSpec;
|
||||||
|
static bool is_allowed(AllowedSpec* specs, InstanceKlass* ik);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Called by heapShared.cpp to see if src_ik->java_mirror() can be archived in
|
||||||
|
// the initialized state.
|
||||||
|
static bool can_archive_initialized_mirror(InstanceKlass* src_ik);
|
||||||
|
|
||||||
|
static bool is_runtime_setup_required(InstanceKlass* ik);
|
||||||
|
static void call_runtime_setup(JavaThread* current, InstanceKlass* ik);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARE_CDS_AOTCLASSINITIALIZER_HPP
|
319
src/hotspot/share/cds/aotClassLinker.cpp
Normal file
319
src/hotspot/share/cds/aotClassLinker.cpp
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassLinker.hpp"
|
||||||
|
#include "cds/aotConstantPoolResolver.hpp"
|
||||||
|
#include "cds/aotLinkedClassTable.hpp"
|
||||||
|
#include "cds/archiveBuilder.hpp"
|
||||||
|
#include "cds/archiveUtils.inline.hpp"
|
||||||
|
#include "cds/cdsConfig.hpp"
|
||||||
|
#include "cds/heapShared.hpp"
|
||||||
|
#include "cds/lambdaFormInvokers.inline.hpp"
|
||||||
|
#include "classfile/classLoader.hpp"
|
||||||
|
#include "classfile/dictionary.hpp"
|
||||||
|
#include "classfile/systemDictionary.hpp"
|
||||||
|
#include "classfile/systemDictionaryShared.hpp"
|
||||||
|
#include "classfile/vmClasses.hpp"
|
||||||
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "oops/constantPool.inline.hpp"
|
||||||
|
#include "oops/instanceKlass.hpp"
|
||||||
|
#include "oops/klass.inline.hpp"
|
||||||
|
#include "runtime/handles.inline.hpp"
|
||||||
|
|
||||||
|
AOTClassLinker::ClassesTable* AOTClassLinker::_vm_classes = nullptr;
|
||||||
|
AOTClassLinker::ClassesTable* AOTClassLinker::_candidates = nullptr;
|
||||||
|
GrowableArrayCHeap<InstanceKlass*, mtClassShared>* AOTClassLinker::_sorted_candidates = nullptr;
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
bool AOTClassLinker::is_initialized() {
|
||||||
|
assert(CDSConfig::is_dumping_archive(), "AOTClassLinker is for CDS dumping only");
|
||||||
|
return _vm_classes != nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void AOTClassLinker::initialize() {
|
||||||
|
assert(!is_initialized(), "sanity");
|
||||||
|
|
||||||
|
_vm_classes = new (mtClass)ClassesTable();
|
||||||
|
_candidates = new (mtClass)ClassesTable();
|
||||||
|
_sorted_candidates = new GrowableArrayCHeap<InstanceKlass*, mtClassShared>(1000);
|
||||||
|
|
||||||
|
for (auto id : EnumRange<vmClassID>{}) {
|
||||||
|
add_vm_class(vmClasses::klass_at(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(is_initialized(), "sanity");
|
||||||
|
|
||||||
|
AOTConstantPoolResolver::initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTClassLinker::dispose() {
|
||||||
|
assert(is_initialized(), "sanity");
|
||||||
|
|
||||||
|
delete _vm_classes;
|
||||||
|
delete _candidates;
|
||||||
|
delete _sorted_candidates;
|
||||||
|
_vm_classes = nullptr;
|
||||||
|
_candidates = nullptr;
|
||||||
|
_sorted_candidates = nullptr;
|
||||||
|
|
||||||
|
assert(!is_initialized(), "sanity");
|
||||||
|
|
||||||
|
AOTConstantPoolResolver::dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTClassLinker::is_vm_class(InstanceKlass* ik) {
|
||||||
|
assert(is_initialized(), "sanity");
|
||||||
|
return (_vm_classes->get(ik) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTClassLinker::add_vm_class(InstanceKlass* ik) {
|
||||||
|
assert(is_initialized(), "sanity");
|
||||||
|
bool created;
|
||||||
|
_vm_classes->put_if_absent(ik, &created);
|
||||||
|
if (created) {
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
bool v = try_add_candidate(ik);
|
||||||
|
assert(v, "must succeed for VM class");
|
||||||
|
}
|
||||||
|
InstanceKlass* super = ik->java_super();
|
||||||
|
if (super != nullptr) {
|
||||||
|
add_vm_class(super);
|
||||||
|
}
|
||||||
|
Array<InstanceKlass*>* ifs = ik->local_interfaces();
|
||||||
|
for (int i = 0; i < ifs->length(); i++) {
|
||||||
|
add_vm_class(ifs->at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTClassLinker::is_candidate(InstanceKlass* ik) {
|
||||||
|
return (_candidates->get(ik) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTClassLinker::add_new_candidate(InstanceKlass* ik) {
|
||||||
|
assert(!is_candidate(ik), "caller need to check");
|
||||||
|
_candidates->put_when_absent(ik, true);
|
||||||
|
_sorted_candidates->append(ik);
|
||||||
|
|
||||||
|
if (log_is_enabled(Info, cds, aot, link)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_info(cds, aot, link)("%s %s %p", class_category_name(ik), ik->external_name(), ik);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ik is a candidate for aot-linking; see if it can really work
|
||||||
|
// that way, and return success or failure. Not only must ik itself
|
||||||
|
// look like a class that can be aot-linked but its supers must also be
|
||||||
|
// aot-linkable.
|
||||||
|
bool AOTClassLinker::try_add_candidate(InstanceKlass* ik) {
|
||||||
|
assert(is_initialized(), "sanity");
|
||||||
|
assert(CDSConfig::is_dumping_aot_linked_classes(), "sanity");
|
||||||
|
|
||||||
|
if (!SystemDictionaryShared::is_builtin(ik)) {
|
||||||
|
// not loaded by a class loader which we know about
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_candidate(ik)) { // already checked.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ik->is_hidden()) {
|
||||||
|
assert(ik->shared_class_loader_type() != ClassLoader::OTHER, "must have been set");
|
||||||
|
if (!CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!SystemDictionaryShared::should_hidden_class_be_archived(ik)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (HeapShared::is_lambda_proxy_klass(ik)) {
|
||||||
|
InstanceKlass* nest_host = ik->nest_host_not_null();
|
||||||
|
if (!try_add_candidate(nest_host)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_warning(cds, aot, link)("%s cannot be aot-linked because it nest host is not aot-linked", ik->external_name());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceKlass* s = ik->java_super();
|
||||||
|
if (s != nullptr && !try_add_candidate(s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<InstanceKlass*>* interfaces = ik->local_interfaces();
|
||||||
|
int num_interfaces = interfaces->length();
|
||||||
|
for (int index = 0; index < num_interfaces; index++) {
|
||||||
|
InstanceKlass* intf = interfaces->at(index);
|
||||||
|
if (!try_add_candidate(intf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are no loops in the class hierarchy, and this function is always called single-threaded, so
|
||||||
|
// we know ik has not been added yet.
|
||||||
|
assert(CDSConfig::current_thread_is_vm_or_dumper(), "that's why we don't need locks");
|
||||||
|
add_new_candidate(ik);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTClassLinker::add_candidates() {
|
||||||
|
assert_at_safepoint();
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses();
|
||||||
|
for (GrowableArrayIterator<Klass*> it = klasses->begin(); it != klasses->end(); ++it) {
|
||||||
|
Klass* k = *it;
|
||||||
|
if (k->is_instance_klass()) {
|
||||||
|
try_add_candidate(InstanceKlass::cast(k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTClassLinker::write_to_archive() {
|
||||||
|
assert(is_initialized(), "sanity");
|
||||||
|
assert_at_safepoint();
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
AOTLinkedClassTable* table = AOTLinkedClassTable::get(CDSConfig::is_dumping_static_archive());
|
||||||
|
table->set_boot(write_classes(nullptr, true));
|
||||||
|
table->set_boot2(write_classes(nullptr, false));
|
||||||
|
table->set_platform(write_classes(SystemDictionary::java_platform_loader(), false));
|
||||||
|
table->set_app(write_classes(SystemDictionary::java_system_loader(), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<InstanceKlass*>* AOTClassLinker::write_classes(oop class_loader, bool is_javabase) {
|
||||||
|
ResourceMark rm;
|
||||||
|
GrowableArray<InstanceKlass*> list;
|
||||||
|
|
||||||
|
for (int i = 0; i < _sorted_candidates->length(); i++) {
|
||||||
|
InstanceKlass* ik = _sorted_candidates->at(i);
|
||||||
|
if (ik->class_loader() != class_loader) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((ik->module() == ModuleEntryTable::javabase_moduleEntry()) != is_javabase) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ik->is_shared() && CDSConfig::is_dumping_dynamic_archive()) {
|
||||||
|
if (CDSConfig::is_using_aot_linked_classes()) {
|
||||||
|
// This class was recorded as AOT-linked for the base archive,
|
||||||
|
// so there's no need to do so again for the dynamic archive.
|
||||||
|
} else {
|
||||||
|
list.append(ik);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.append(ArchiveBuilder::current()->get_buffered_addr(ik));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.length() == 0) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
const char* category = class_category_name(list.at(0));
|
||||||
|
log_info(cds, aot, link)("wrote %d class(es) for category %s", list.length(), category);
|
||||||
|
return ArchiveUtils::archive_array(&list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AOTClassLinker::num_platform_initiated_classes() {
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
// AOTLinkedClassBulkLoader will initiate loading of all public boot classes in the platform loader.
|
||||||
|
return count_public_classes(nullptr);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AOTClassLinker::num_app_initiated_classes() {
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
// AOTLinkedClassBulkLoader will initiate loading of all public boot/platform classes in the app loader.
|
||||||
|
return count_public_classes(nullptr) + count_public_classes(SystemDictionary::java_platform_loader());
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AOTClassLinker::count_public_classes(oop loader) {
|
||||||
|
int n = 0;
|
||||||
|
for (int i = 0; i < _sorted_candidates->length(); i++) {
|
||||||
|
InstanceKlass* ik = _sorted_candidates->at(i);
|
||||||
|
if (ik->is_public() && !ik->is_hidden() && ik->class_loader() == loader) {
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used in logging: "boot1", "boot2", "plat", "app" and "unreg", or "array"
|
||||||
|
const char* AOTClassLinker::class_category_name(Klass* k) {
|
||||||
|
if (ArchiveBuilder::is_active() && ArchiveBuilder::current()->is_in_buffer_space(k)) {
|
||||||
|
k = ArchiveBuilder::current()->get_source_addr(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k->is_array_klass()) {
|
||||||
|
return "array";
|
||||||
|
} else {
|
||||||
|
oop loader = k->class_loader();
|
||||||
|
if (loader == nullptr) {
|
||||||
|
if (k->module() != nullptr &&
|
||||||
|
k->module()->name() != nullptr &&
|
||||||
|
k->module()->name()->equals("java.base")) {
|
||||||
|
return "boot1"; // boot classes in java.base are loaded in the 1st phase
|
||||||
|
} else {
|
||||||
|
return "boot2"; // boot classes outside of java.base are loaded in the 2nd phase phase
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (loader == SystemDictionary::java_platform_loader()) {
|
||||||
|
return "plat";
|
||||||
|
} else if (loader == SystemDictionary::java_system_loader()) {
|
||||||
|
return "app";
|
||||||
|
} else {
|
||||||
|
return "unreg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* AOTClassLinker::class_category_name(AOTLinkedClassCategory category) {
|
||||||
|
switch (category) {
|
||||||
|
case AOTLinkedClassCategory::BOOT1:
|
||||||
|
return "boot1";
|
||||||
|
case AOTLinkedClassCategory::BOOT2:
|
||||||
|
return "boot2";
|
||||||
|
case AOTLinkedClassCategory::PLATFORM:
|
||||||
|
return "plat";
|
||||||
|
case AOTLinkedClassCategory::APP:
|
||||||
|
return "app";
|
||||||
|
case AOTLinkedClassCategory::UNREGISTERED:
|
||||||
|
default:
|
||||||
|
return "unreg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
129
src/hotspot/share/cds/aotClassLinker.hpp
Normal file
129
src/hotspot/share/cds/aotClassLinker.hpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARE_CDS_AOTCLASSLINKER_HPP
|
||||||
|
#define SHARE_CDS_AOTCLASSLINKER_HPP
|
||||||
|
|
||||||
|
#include "interpreter/bytecodes.hpp"
|
||||||
|
#include "memory/allocation.hpp"
|
||||||
|
#include "memory/allStatic.hpp"
|
||||||
|
#include "oops/oopsHierarchy.hpp"
|
||||||
|
#include "utilities/exceptions.hpp"
|
||||||
|
#include "utilities/growableArray.hpp"
|
||||||
|
#include "utilities/macros.hpp"
|
||||||
|
#include "utilities/resourceHash.hpp"
|
||||||
|
|
||||||
|
class AOTLinkedClassTable;
|
||||||
|
class InstanceKlass;
|
||||||
|
class SerializeClosure;
|
||||||
|
template <typename T> class Array;
|
||||||
|
enum class AOTLinkedClassCategory : int;
|
||||||
|
|
||||||
|
// AOTClassLinker is used during the AOTCache Assembly Phase.
|
||||||
|
// It links eligible classes before they are written into the AOTCache
|
||||||
|
//
|
||||||
|
// The classes linked by AOTClassLinker are recorded in an AOTLinkedClassTable,
|
||||||
|
// which is also written into the AOTCache.
|
||||||
|
//
|
||||||
|
// AOTClassLinker is enabled by the -XX:+AOTClassLinking option. If this option
|
||||||
|
// is disabled, an empty AOTLinkedClassTable will be included in the AOTCache.
|
||||||
|
//
|
||||||
|
// For each class C in the AOTLinkedClassTable, the following properties for C
|
||||||
|
// are assigned by AOTClassLinker and cannot be changed thereafter.
|
||||||
|
// - The CodeSource for C
|
||||||
|
// - The bytecodes in C
|
||||||
|
// - The supertypes of C
|
||||||
|
// - The ClassLoader, Package and Module of C
|
||||||
|
// - The visibility of C
|
||||||
|
//
|
||||||
|
// During a production run, the JVM can use an AOTCache with an AOTLinkedClassTable
|
||||||
|
// only if it's guaranteed to produce the same results for the above set of properties
|
||||||
|
// for each class C in the AOTLinkedClassTable.
|
||||||
|
//
|
||||||
|
// For example,
|
||||||
|
// - C may be loaded from a different CodeSource when the CLASSPATH is changed.
|
||||||
|
// - Some JVMTI agent may allow the bytecodes of C to be modified.
|
||||||
|
// - C may be made invisible by module options such as --add-modules
|
||||||
|
// In such situations, the JVM will refuse to load the AOTCache.
|
||||||
|
//
|
||||||
|
class AOTClassLinker : AllStatic {
|
||||||
|
static const int TABLE_SIZE = 15889; // prime number
|
||||||
|
using ClassesTable = ResourceHashtable<InstanceKlass*, bool, TABLE_SIZE, AnyObj::C_HEAP, mtClassShared>;
|
||||||
|
|
||||||
|
// Classes loaded inside vmClasses::resolve_all()
|
||||||
|
static ClassesTable* _vm_classes;
|
||||||
|
|
||||||
|
// Classes that should be automatically loaded into system dictionary at VM start-up
|
||||||
|
static ClassesTable* _candidates;
|
||||||
|
|
||||||
|
// Sorted list such that super types come first.
|
||||||
|
static GrowableArrayCHeap<InstanceKlass*, mtClassShared>* _sorted_candidates;
|
||||||
|
|
||||||
|
DEBUG_ONLY(static bool is_initialized());
|
||||||
|
|
||||||
|
static void add_vm_class(InstanceKlass* ik);
|
||||||
|
static void add_new_candidate(InstanceKlass* ik);
|
||||||
|
|
||||||
|
static Array<InstanceKlass*>* write_classes(oop class_loader, bool is_javabase);
|
||||||
|
static int count_public_classes(oop loader);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void initialize();
|
||||||
|
static void add_candidates();
|
||||||
|
static void write_to_archive();
|
||||||
|
static void dispose();
|
||||||
|
|
||||||
|
// Is this class resolved as part of vmClasses::resolve_all()?
|
||||||
|
static bool is_vm_class(InstanceKlass* ik);
|
||||||
|
|
||||||
|
// When CDS is enabled, is ik guaranteed to be linked at deployment time (and
|
||||||
|
// cannot be replaced by JVMTI, etc)?
|
||||||
|
// This is a necessary (but not sufficient) condition for keeping a direct pointer
|
||||||
|
// to ik in AOT-computed data (such as ConstantPool entries in archived classes,
|
||||||
|
// or in AOT-compiled code).
|
||||||
|
static bool is_candidate(InstanceKlass* ik);
|
||||||
|
|
||||||
|
// Request that ik be added to the candidates table. This will return true only if
|
||||||
|
// ik is allowed to be aot-linked.
|
||||||
|
static bool try_add_candidate(InstanceKlass* ik);
|
||||||
|
|
||||||
|
static int num_app_initiated_classes();
|
||||||
|
static int num_platform_initiated_classes();
|
||||||
|
|
||||||
|
// Used in logging: "boot1", "boot2", "plat", "app" and "unreg";
|
||||||
|
static const char* class_category_name(AOTLinkedClassCategory category);
|
||||||
|
static const char* class_category_name(Klass* k);
|
||||||
|
};
|
||||||
|
|
||||||
|
// AOT-linked classes are divided into different categories and are loaded
|
||||||
|
// in two phases during the production run.
|
||||||
|
enum class AOTLinkedClassCategory : int {
|
||||||
|
BOOT1, // Only java.base classes are loaded in the 1st phase
|
||||||
|
BOOT2, // All boot classes that not in java.base are loaded in the 2nd phase
|
||||||
|
PLATFORM, // Classes for platform loader, loaded in the 2nd phase
|
||||||
|
APP, // Classes for the app loader, loaded in the 2nd phase
|
||||||
|
UNREGISTERED // classes loaded outside of the boot/platform/app loaders; currently not supported by AOTClassLinker
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARE_CDS_AOTCLASSLINKER_HPP
|
573
src/hotspot/share/cds/aotConstantPoolResolver.cpp
Normal file
573
src/hotspot/share/cds/aotConstantPoolResolver.cpp
Normal file
@ -0,0 +1,573 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassLinker.hpp"
|
||||||
|
#include "cds/aotConstantPoolResolver.hpp"
|
||||||
|
#include "cds/archiveBuilder.hpp"
|
||||||
|
#include "cds/cdsConfig.hpp"
|
||||||
|
#include "classfile/systemDictionary.hpp"
|
||||||
|
#include "classfile/systemDictionaryShared.hpp"
|
||||||
|
#include "classfile/vmClasses.hpp"
|
||||||
|
#include "interpreter/bytecodeStream.hpp"
|
||||||
|
#include "interpreter/interpreterRuntime.hpp"
|
||||||
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "oops/constantPool.inline.hpp"
|
||||||
|
#include "oops/instanceKlass.hpp"
|
||||||
|
#include "oops/klass.inline.hpp"
|
||||||
|
#include "runtime/handles.inline.hpp"
|
||||||
|
|
||||||
|
AOTConstantPoolResolver::ClassesTable* AOTConstantPoolResolver::_processed_classes = nullptr;
|
||||||
|
|
||||||
|
void AOTConstantPoolResolver::initialize() {
|
||||||
|
assert(_processed_classes == nullptr, "must be");
|
||||||
|
_processed_classes = new (mtClass)ClassesTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTConstantPoolResolver::dispose() {
|
||||||
|
assert(_processed_classes != nullptr, "must be");
|
||||||
|
delete _processed_classes;
|
||||||
|
_processed_classes = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if we CAN PROVE that cp_index will always resolve to
|
||||||
|
// the same information at both dump time and run time. This is a
|
||||||
|
// necessary (but not sufficient) condition for pre-resolving cp_index
|
||||||
|
// during CDS archive assembly.
|
||||||
|
bool AOTConstantPoolResolver::is_resolution_deterministic(ConstantPool* cp, int cp_index) {
|
||||||
|
assert(!is_in_archivebuilder_buffer(cp), "sanity");
|
||||||
|
|
||||||
|
if (cp->tag_at(cp_index).is_klass()) {
|
||||||
|
// We require cp_index to be already resolved. This is fine for now, are we
|
||||||
|
// currently archive only CP entries that are already resolved.
|
||||||
|
Klass* resolved_klass = cp->resolved_klass_at(cp_index);
|
||||||
|
return resolved_klass != nullptr && is_class_resolution_deterministic(cp->pool_holder(), resolved_klass);
|
||||||
|
} else if (cp->tag_at(cp_index).is_invoke_dynamic()) {
|
||||||
|
return is_indy_resolution_deterministic(cp, cp_index);
|
||||||
|
} else if (cp->tag_at(cp_index).is_field() ||
|
||||||
|
cp->tag_at(cp_index).is_method() ||
|
||||||
|
cp->tag_at(cp_index).is_interface_method()) {
|
||||||
|
int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index);
|
||||||
|
if (!cp->tag_at(klass_cp_index).is_klass()) {
|
||||||
|
// Not yet resolved
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Klass* k = cp->resolved_klass_at(klass_cp_index);
|
||||||
|
if (!is_class_resolution_deterministic(cp->pool_holder(), k)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!k->is_instance_klass()) {
|
||||||
|
// TODO: support non instance klasses as well.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here, We don't check if this entry can actually be resolved to a valid Field/Method.
|
||||||
|
// This method should be called by the ConstantPool to check Fields/Methods that
|
||||||
|
// have already been successfully resolved.
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class) {
|
||||||
|
assert(!is_in_archivebuilder_buffer(cp_holder), "sanity");
|
||||||
|
assert(!is_in_archivebuilder_buffer(resolved_class), "sanity");
|
||||||
|
|
||||||
|
if (resolved_class->is_instance_klass()) {
|
||||||
|
InstanceKlass* ik = InstanceKlass::cast(resolved_class);
|
||||||
|
|
||||||
|
if (!ik->is_shared() && SystemDictionaryShared::is_excluded_class(ik)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cp_holder->is_subtype_of(ik)) {
|
||||||
|
// All super types of ik will be resolved in ik->class_loader() before
|
||||||
|
// ik is defined in this loader, so it's safe to archive the resolved klass reference.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
// Need to call try_add_candidate instead of is_candidate, as this may be called
|
||||||
|
// before AOTClassLinker::add_candidates().
|
||||||
|
if (AOTClassLinker::try_add_candidate(ik)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (AOTClassLinker::is_vm_class(ik)) {
|
||||||
|
if (ik->class_loader() != cp_holder->class_loader()) {
|
||||||
|
// At runtime, cp_holder() may not be able to resolve to the same
|
||||||
|
// ik. For example, a different version of ik may be defined in
|
||||||
|
// cp->pool_holder()'s loader using MethodHandles.Lookup.defineClass().
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (resolved_class->is_objArray_klass()) {
|
||||||
|
Klass* elem = ObjArrayKlass::cast(resolved_class)->bottom_klass();
|
||||||
|
if (elem->is_instance_klass()) {
|
||||||
|
return is_class_resolution_deterministic(cp_holder, InstanceKlass::cast(elem));
|
||||||
|
} else if (elem->is_typeArray_klass()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (resolved_class->is_typeArray_klass()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTConstantPoolResolver::dumptime_resolve_constants(InstanceKlass* ik, TRAPS) {
|
||||||
|
if (!ik->is_linked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool first_time;
|
||||||
|
_processed_classes->put_if_absent(ik, &first_time);
|
||||||
|
if (!first_time) {
|
||||||
|
// We have already resolved the constants in class, so no need to do it again.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constantPoolHandle cp(THREAD, ik->constants());
|
||||||
|
for (int cp_index = 1; cp_index < cp->length(); cp_index++) { // Index 0 is unused
|
||||||
|
switch (cp->tag_at(cp_index).value()) {
|
||||||
|
case JVM_CONSTANT_String:
|
||||||
|
resolve_string(cp, cp_index, CHECK); // may throw OOM when interning strings.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This works only for the boot/platform/app loaders
|
||||||
|
Klass* AOTConstantPoolResolver::find_loaded_class(Thread* current, oop class_loader, Symbol* name) {
|
||||||
|
HandleMark hm(current);
|
||||||
|
Handle h_loader(current, class_loader);
|
||||||
|
Klass* k = SystemDictionary::find_instance_or_array_klass(current, name,
|
||||||
|
h_loader,
|
||||||
|
Handle());
|
||||||
|
if (k != nullptr) {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
if (h_loader() == SystemDictionary::java_system_loader()) {
|
||||||
|
return find_loaded_class(current, SystemDictionary::java_platform_loader(), name);
|
||||||
|
} else if (h_loader() == SystemDictionary::java_platform_loader()) {
|
||||||
|
return find_loaded_class(current, nullptr, name);
|
||||||
|
} else {
|
||||||
|
assert(h_loader() == nullptr, "This function only works for boot/platform/app loaders %p %p %p",
|
||||||
|
cast_from_oop<address>(h_loader()),
|
||||||
|
cast_from_oop<address>(SystemDictionary::java_system_loader()),
|
||||||
|
cast_from_oop<address>(SystemDictionary::java_platform_loader()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Klass* AOTConstantPoolResolver::find_loaded_class(Thread* current, ConstantPool* cp, int class_cp_index) {
|
||||||
|
Symbol* name = cp->klass_name_at(class_cp_index);
|
||||||
|
return find_loaded_class(current, cp->pool_holder()->class_loader(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if INCLUDE_CDS_JAVA_HEAP
|
||||||
|
void AOTConstantPoolResolver::resolve_string(constantPoolHandle cp, int cp_index, TRAPS) {
|
||||||
|
if (CDSConfig::is_dumping_heap()) {
|
||||||
|
int cache_index = cp->cp_to_object_index(cp_index);
|
||||||
|
ConstantPool::string_at_impl(cp, cp_index, cache_index, CHECK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void AOTConstantPoolResolver::preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list) {
|
||||||
|
if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JavaThread* THREAD = current;
|
||||||
|
constantPoolHandle cp(THREAD, ik->constants());
|
||||||
|
for (int cp_index = 1; cp_index < cp->length(); cp_index++) {
|
||||||
|
if (cp->tag_at(cp_index).value() == JVM_CONSTANT_UnresolvedClass) {
|
||||||
|
if (preresolve_list != nullptr && preresolve_list->at(cp_index) == false) {
|
||||||
|
// This class was not resolved during trial run. Don't attempt to resolve it. Otherwise
|
||||||
|
// the compiler may generate less efficient code.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (find_loaded_class(current, cp(), cp_index) == nullptr) {
|
||||||
|
// Do not resolve any class that has not been loaded yet
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Klass* resolved_klass = cp->klass_at(cp_index, THREAD);
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
CLEAR_PENDING_EXCEPTION; // just ignore
|
||||||
|
} else {
|
||||||
|
log_trace(cds, resolve)("Resolved class [%3d] %s -> %s", cp_index, ik->external_name(),
|
||||||
|
resolved_klass->external_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list) {
|
||||||
|
JavaThread* THREAD = current;
|
||||||
|
constantPoolHandle cp(THREAD, ik->constants());
|
||||||
|
if (cp->cache() == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ik->methods()->length(); i++) {
|
||||||
|
Method* m = ik->methods()->at(i);
|
||||||
|
BytecodeStream bcs(methodHandle(THREAD, m));
|
||||||
|
while (!bcs.is_last_bytecode()) {
|
||||||
|
bcs.next();
|
||||||
|
Bytecodes::Code raw_bc = bcs.raw_code();
|
||||||
|
switch (raw_bc) {
|
||||||
|
case Bytecodes::_getfield:
|
||||||
|
case Bytecodes::_putfield:
|
||||||
|
maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
CLEAR_PENDING_EXCEPTION; // just ignore
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Bytecodes::_invokehandle:
|
||||||
|
case Bytecodes::_invokespecial:
|
||||||
|
case Bytecodes::_invokevirtual:
|
||||||
|
case Bytecodes::_invokeinterface:
|
||||||
|
maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
CLEAR_PENDING_EXCEPTION; // just ignore
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index,
|
||||||
|
GrowableArray<bool>* preresolve_list, TRAPS) {
|
||||||
|
methodHandle mh(THREAD, m);
|
||||||
|
constantPoolHandle cp(THREAD, ik->constants());
|
||||||
|
HandleMark hm(THREAD);
|
||||||
|
int cp_index = cp->to_cp_index(raw_index, bc);
|
||||||
|
|
||||||
|
if (cp->is_resolved(raw_index, bc)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preresolve_list != nullptr && preresolve_list->at(cp_index) == false) {
|
||||||
|
// This field wasn't resolved during the trial run. Don't attempt to resolve it. Otherwise
|
||||||
|
// the compiler may generate less efficient code.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index);
|
||||||
|
if (find_loaded_class(THREAD, cp(), klass_cp_index) == nullptr) {
|
||||||
|
// Do not resolve any field/methods from a class that has not been loaded yet.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Klass* resolved_klass = cp->klass_ref_at(raw_index, bc, CHECK);
|
||||||
|
|
||||||
|
switch (bc) {
|
||||||
|
case Bytecodes::_getfield:
|
||||||
|
case Bytecodes::_putfield:
|
||||||
|
InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bytecodes::_invokevirtual:
|
||||||
|
case Bytecodes::_invokespecial:
|
||||||
|
case Bytecodes::_invokeinterface:
|
||||||
|
InterpreterRuntime::cds_resolve_invoke(bc, raw_index, cp, CHECK);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bytecodes::_invokehandle:
|
||||||
|
InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ShouldNotReachHere();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_is_enabled(Trace, cds, resolve)) {
|
||||||
|
ResourceMark rm(THREAD);
|
||||||
|
bool resolved = cp->is_resolved(raw_index, bc);
|
||||||
|
Symbol* name = cp->name_ref_at(raw_index, bc);
|
||||||
|
Symbol* signature = cp->signature_ref_at(raw_index, bc);
|
||||||
|
log_trace(cds, resolve)("%s %s [%3d] %s -> %s.%s:%s",
|
||||||
|
(resolved ? "Resolved" : "Failed to resolve"),
|
||||||
|
Bytecodes::name(bc), cp_index, ik->external_name(),
|
||||||
|
resolved_klass->external_name(),
|
||||||
|
name->as_C_string(), signature->as_C_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTConstantPoolResolver::preresolve_indy_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list) {
|
||||||
|
JavaThread* THREAD = current;
|
||||||
|
constantPoolHandle cp(THREAD, ik->constants());
|
||||||
|
if (!CDSConfig::is_dumping_invokedynamic() || cp->cache() == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(preresolve_list != nullptr, "preresolve_indy_cp_entries() should not be called for "
|
||||||
|
"regenerated LambdaForm Invoker classes, which should not have indys anyway.");
|
||||||
|
|
||||||
|
Array<ResolvedIndyEntry>* indy_entries = cp->cache()->resolved_indy_entries();
|
||||||
|
for (int i = 0; i < indy_entries->length(); i++) {
|
||||||
|
ResolvedIndyEntry* rie = indy_entries->adr_at(i);
|
||||||
|
int cp_index = rie->constant_pool_index();
|
||||||
|
if (preresolve_list->at(cp_index) == true) {
|
||||||
|
if (!rie->is_resolved() && is_indy_resolution_deterministic(cp(), cp_index)) {
|
||||||
|
InterpreterRuntime::cds_resolve_invokedynamic(i, cp, THREAD);
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
CLEAR_PENDING_EXCEPTION; // just ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (log_is_enabled(Trace, cds, resolve)) {
|
||||||
|
ResourceMark rm(THREAD);
|
||||||
|
log_trace(cds, resolve)("%s indy [%3d] %s",
|
||||||
|
rie->is_resolved() ? "Resolved" : "Failed to resolve",
|
||||||
|
cp_index, ik->external_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the MethodType signatures used by parameters to the indy BSMs. Make sure we don't
|
||||||
|
// use types that have been excluded, or else we might end up creating MethodTypes that cannot be stored
|
||||||
|
// in the AOT cache.
|
||||||
|
bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret) {
|
||||||
|
ResourceMark rm;
|
||||||
|
for (SignatureStream ss(sig); !ss.is_done(); ss.next()) {
|
||||||
|
if (ss.is_reference()) {
|
||||||
|
Symbol* type = ss.as_symbol();
|
||||||
|
Klass* k = find_loaded_class(Thread::current(), cp->pool_holder()->class_loader(), type);
|
||||||
|
if (k == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SystemDictionaryShared::should_be_excluded(k)) {
|
||||||
|
if (log_is_enabled(Warning, cds, resolve)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_warning(cds, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ss.at_return_type() && return_type_ret != nullptr) {
|
||||||
|
*return_type_ret = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTConstantPoolResolver::check_lambda_metafactory_signature(ConstantPool* cp, Symbol* sig) {
|
||||||
|
Klass* k;
|
||||||
|
if (!check_methodtype_signature(cp, sig, &k)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <k> is the interface type implemented by the lambda proxy
|
||||||
|
if (!k->is_interface()) {
|
||||||
|
// cp->pool_holder() doesn't look like a valid class generated by javac
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The linked lambda callsite has an instance of the interface implemented by this lambda. If this
|
||||||
|
// interface requires its <clinit> to be executed, then we must delay the execution to the production run
|
||||||
|
// as <clinit> can have side effects ==> exclude such cases.
|
||||||
|
InstanceKlass* intf = InstanceKlass::cast(k);
|
||||||
|
bool exclude = intf->interface_needs_clinit_execution_as_super();
|
||||||
|
if (log_is_enabled(Debug, cds, resolve)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_debug(cds, resolve)("%s aot-resolve Lambda proxy of interface type %s",
|
||||||
|
exclude ? "Cannot" : "Can", k->external_name());
|
||||||
|
}
|
||||||
|
return !exclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
|
||||||
|
int mt_index = cp->operand_argument_index_at(bsms_attribute_index, arg_i);
|
||||||
|
if (!cp->tag_at(mt_index).is_method_type()) {
|
||||||
|
// malformed class?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol* sig = cp->method_type_signature_at(mt_index);
|
||||||
|
if (log_is_enabled(Debug, cds, resolve)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_debug(cds, resolve)("Checking MethodType for LambdaMetafactory BSM arg %d: %s", arg_i, sig->as_C_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
return check_methodtype_signature(cp, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
|
||||||
|
int mh_index = cp->operand_argument_index_at(bsms_attribute_index, arg_i);
|
||||||
|
if (!cp->tag_at(mh_index).is_method_handle()) {
|
||||||
|
// malformed class?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol* sig = cp->method_handle_signature_ref_at(mh_index);
|
||||||
|
if (log_is_enabled(Debug, cds, resolve)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_debug(cds, resolve)("Checking MethodType of MethodHandle for LambdaMetafactory BSM arg %d: %s", arg_i, sig->as_C_string());
|
||||||
|
}
|
||||||
|
return check_methodtype_signature(cp, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTConstantPoolResolver::is_indy_resolution_deterministic(ConstantPool* cp, int cp_index) {
|
||||||
|
assert(cp->tag_at(cp_index).is_invoke_dynamic(), "sanity");
|
||||||
|
if (!CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceKlass* pool_holder = cp->pool_holder();
|
||||||
|
if (!SystemDictionaryShared::is_builtin(pool_holder)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bsm = cp->bootstrap_method_ref_index_at(cp_index);
|
||||||
|
int bsm_ref = cp->method_handle_index_at(bsm);
|
||||||
|
Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref);
|
||||||
|
Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref);
|
||||||
|
Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref));
|
||||||
|
|
||||||
|
// We currently support only StringConcatFactory::makeConcatWithConstants() and LambdaMetafactory::metafactory()
|
||||||
|
// We should mark the allowed BSMs in the JDK code using a private annotation.
|
||||||
|
// See notes on RFE JDK-8342481.
|
||||||
|
|
||||||
|
if (bsm_klass->equals("java/lang/invoke/StringConcatFactory") &&
|
||||||
|
bsm_name->equals("makeConcatWithConstants") &&
|
||||||
|
bsm_signature->equals("(Ljava/lang/invoke/MethodHandles$Lookup;"
|
||||||
|
"Ljava/lang/String;"
|
||||||
|
"Ljava/lang/invoke/MethodType;"
|
||||||
|
"Ljava/lang/String;"
|
||||||
|
"[Ljava/lang/Object;"
|
||||||
|
")Ljava/lang/invoke/CallSite;")) {
|
||||||
|
Symbol* factory_type_sig = cp->uncached_signature_ref_at(cp_index);
|
||||||
|
if (log_is_enabled(Debug, cds, resolve)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_debug(cds, resolve)("Checking StringConcatFactory callsite signature [%d]: %s", cp_index, factory_type_sig->as_C_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Klass* k;
|
||||||
|
if (!check_methodtype_signature(cp, factory_type_sig, &k)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (k != vmClasses::String_klass()) {
|
||||||
|
// bad class file?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bsm_klass->equals("java/lang/invoke/LambdaMetafactory") &&
|
||||||
|
bsm_name->equals("metafactory") &&
|
||||||
|
bsm_signature->equals("(Ljava/lang/invoke/MethodHandles$Lookup;"
|
||||||
|
"Ljava/lang/String;"
|
||||||
|
"Ljava/lang/invoke/MethodType;"
|
||||||
|
"Ljava/lang/invoke/MethodType;"
|
||||||
|
"Ljava/lang/invoke/MethodHandle;"
|
||||||
|
"Ljava/lang/invoke/MethodType;"
|
||||||
|
")Ljava/lang/invoke/CallSite;")) {
|
||||||
|
/*
|
||||||
|
* An indy callsite is associated with the following MethodType and MethodHandles:
|
||||||
|
*
|
||||||
|
* https://github.com/openjdk/jdk/blob/580eb62dc097efeb51c76b095c1404106859b673/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java#L293-L309
|
||||||
|
*
|
||||||
|
* MethodType factoryType The expected signature of the {@code CallSite}. The
|
||||||
|
* parameter types represent the types of capture variables;
|
||||||
|
* the return type is the interface to implement. When
|
||||||
|
* used with {@code invokedynamic}, this is provided by
|
||||||
|
* the {@code NameAndType} of the {@code InvokeDynamic}
|
||||||
|
*
|
||||||
|
* MethodType interfaceMethodType Signature and return type of method to be
|
||||||
|
* implemented by the function object.
|
||||||
|
*
|
||||||
|
* MethodHandle implementation A direct method handle describing the implementation
|
||||||
|
* method which should be called (with suitable adaptation
|
||||||
|
* of argument types and return types, and with captured
|
||||||
|
* arguments prepended to the invocation arguments) at
|
||||||
|
* invocation time.
|
||||||
|
*
|
||||||
|
* MethodType dynamicMethodType The signature and return type that should
|
||||||
|
* be enforced dynamically at invocation time.
|
||||||
|
* In simple use cases this is the same as
|
||||||
|
* {@code interfaceMethodType}.
|
||||||
|
*/
|
||||||
|
Symbol* factory_type_sig = cp->uncached_signature_ref_at(cp_index);
|
||||||
|
if (log_is_enabled(Debug, cds, resolve)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_debug(cds, resolve)("Checking indy callsite signature [%d]: %s", cp_index, factory_type_sig->as_C_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_lambda_metafactory_signature(cp, factory_type_sig)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bsms_attribute_index = cp->bootstrap_methods_attribute_index(cp_index);
|
||||||
|
int arg_count = cp->operand_argument_count_at(bsms_attribute_index);
|
||||||
|
if (arg_count != 3) {
|
||||||
|
// Malformed class?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interfaceMethodType
|
||||||
|
if (!check_lambda_metafactory_methodtype_arg(cp, bsms_attribute_index, 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// implementation
|
||||||
|
if (!check_lambda_metafactory_methodhandle_arg(cp, bsms_attribute_index, 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dynamicMethodType
|
||||||
|
if (!check_lambda_metafactory_methodtype_arg(cp, bsms_attribute_index, 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef ASSERT
|
||||||
|
bool AOTConstantPoolResolver::is_in_archivebuilder_buffer(address p) {
|
||||||
|
if (!Thread::current()->is_VM_thread() || ArchiveBuilder::current() == nullptr) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return ArchiveBuilder::current()->is_in_buffer_space(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -22,13 +22,13 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SHARE_CDS_CLASSPRELINKER_HPP
|
#ifndef SHARE_CDS_AOTCONSTANTPOOLRESOLVER_HPP
|
||||||
#define SHARE_CDS_CLASSPRELINKER_HPP
|
#define SHARE_CDS_AOTCONSTANTPOOLRESOLVER_HPP
|
||||||
|
|
||||||
#include "interpreter/bytecodes.hpp"
|
#include "interpreter/bytecodes.hpp"
|
||||||
#include "oops/oopsHierarchy.hpp"
|
|
||||||
#include "memory/allStatic.hpp"
|
#include "memory/allStatic.hpp"
|
||||||
#include "memory/allocation.hpp"
|
#include "memory/allocation.hpp"
|
||||||
|
#include "oops/oopsHierarchy.hpp"
|
||||||
#include "runtime/handles.hpp"
|
#include "runtime/handles.hpp"
|
||||||
#include "utilities/exceptions.hpp"
|
#include "utilities/exceptions.hpp"
|
||||||
#include "utilities/macros.hpp"
|
#include "utilities/macros.hpp"
|
||||||
@ -39,7 +39,9 @@ class constantPoolHandle;
|
|||||||
class InstanceKlass;
|
class InstanceKlass;
|
||||||
class Klass;
|
class Klass;
|
||||||
|
|
||||||
// ClassPrelinker is used to perform ahead-of-time linking of ConstantPool entries
|
template <typename T> class GrowableArray;
|
||||||
|
|
||||||
|
// AOTConstantPoolResolver is used to perform ahead-of-time linking of ConstantPool entries
|
||||||
// for archived InstanceKlasses.
|
// for archived InstanceKlasses.
|
||||||
//
|
//
|
||||||
// At run time, Java classes are loaded dynamically and may be replaced with JVMTI.
|
// At run time, Java classes are loaded dynamically and may be replaced with JVMTI.
|
||||||
@ -49,23 +51,21 @@ class Klass;
|
|||||||
// For example, a JVM_CONSTANT_Class reference to a supertype can be safely resolved
|
// For example, a JVM_CONSTANT_Class reference to a supertype can be safely resolved
|
||||||
// at dump time, because at run time we will load a class from the CDS archive only
|
// at dump time, because at run time we will load a class from the CDS archive only
|
||||||
// if all of its supertypes are loaded from the CDS archive.
|
// if all of its supertypes are loaded from the CDS archive.
|
||||||
class ClassPrelinker : AllStatic {
|
class AOTConstantPoolResolver : AllStatic {
|
||||||
using ClassesTable = ResourceHashtable<InstanceKlass*, bool, 15889, AnyObj::C_HEAP, mtClassShared> ;
|
static const int TABLE_SIZE = 15889; // prime number
|
||||||
|
using ClassesTable = ResourceHashtable<InstanceKlass*, bool, TABLE_SIZE, AnyObj::C_HEAP, mtClassShared> ;
|
||||||
static ClassesTable* _processed_classes;
|
static ClassesTable* _processed_classes;
|
||||||
static ClassesTable* _vm_classes;
|
|
||||||
|
|
||||||
static void add_one_vm_class(InstanceKlass* ik);
|
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
|
template <typename T> static bool is_in_archivebuilder_buffer(T p) {
|
||||||
|
return is_in_archivebuilder_buffer((address)(p));
|
||||||
|
}
|
||||||
static bool is_in_archivebuilder_buffer(address p);
|
static bool is_in_archivebuilder_buffer(address p);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static bool is_in_archivebuilder_buffer(T p) {
|
|
||||||
return is_in_archivebuilder_buffer((address)(p));
|
|
||||||
}
|
|
||||||
static void resolve_string(constantPoolHandle cp, int cp_index, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
static void resolve_string(constantPoolHandle cp, int cp_index, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
static bool is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class);
|
static bool is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class);
|
||||||
|
static bool is_indy_resolution_deterministic(ConstantPool* cp, int cp_index);
|
||||||
|
|
||||||
static Klass* find_loaded_class(Thread* current, oop class_loader, Symbol* name);
|
static Klass* find_loaded_class(Thread* current, oop class_loader, Symbol* name);
|
||||||
static Klass* find_loaded_class(Thread* current, ConstantPool* cp, int class_cp_index);
|
static Klass* find_loaded_class(Thread* current, ConstantPool* cp, int class_cp_index);
|
||||||
@ -73,18 +73,20 @@ class ClassPrelinker : AllStatic {
|
|||||||
// fmi = FieldRef/MethodRef/InterfaceMethodRef
|
// fmi = FieldRef/MethodRef/InterfaceMethodRef
|
||||||
static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index,
|
static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index,
|
||||||
GrowableArray<bool>* resolve_fmi_list, TRAPS);
|
GrowableArray<bool>* resolve_fmi_list, TRAPS);
|
||||||
|
|
||||||
|
static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr);
|
||||||
|
static bool check_lambda_metafactory_signature(ConstantPool* cp, Symbol* sig);
|
||||||
|
static bool check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i);
|
||||||
|
static bool check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void initialize();
|
static void initialize();
|
||||||
static void dispose();
|
static void dispose();
|
||||||
|
|
||||||
static void preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
|
static void preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
|
||||||
static void preresolve_field_and_method_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
|
static void preresolve_field_and_method_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
|
||||||
|
static void preresolve_indy_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list);
|
||||||
|
|
||||||
// Is this class resolved as part of vmClasses::resolve_all()? If so, these
|
|
||||||
// classes are guatanteed to be loaded at runtime (and cannot be replaced by JVMTI)
|
|
||||||
// when CDS is enabled. Therefore, we can safely keep a direct reference to these
|
|
||||||
// classes.
|
|
||||||
static bool is_vm_class(InstanceKlass* ik);
|
|
||||||
|
|
||||||
// Resolve all constant pool entries that are safe to be stored in the
|
// Resolve all constant pool entries that are safe to be stored in the
|
||||||
// CDS archive.
|
// CDS archive.
|
||||||
@ -93,4 +95,4 @@ public:
|
|||||||
static bool is_resolution_deterministic(ConstantPool* cp, int cp_index);
|
static bool is_resolution_deterministic(ConstantPool* cp, int cp_index);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_CDS_CLASSPRELINKER_HPP
|
#endif // SHARE_CDS_AOTCONSTANTPOOLRESOLVER_HPP
|
397
src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
Normal file
397
src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassInitializer.hpp"
|
||||||
|
#include "cds/aotClassLinker.hpp"
|
||||||
|
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||||
|
#include "cds/aotLinkedClassTable.hpp"
|
||||||
|
#include "cds/cdsConfig.hpp"
|
||||||
|
#include "cds/heapShared.hpp"
|
||||||
|
#include "classfile/classLoaderData.hpp"
|
||||||
|
#include "classfile/systemDictionary.hpp"
|
||||||
|
#include "classfile/systemDictionaryShared.hpp"
|
||||||
|
#include "classfile/vmClasses.hpp"
|
||||||
|
#include "gc/shared/gcVMOperations.hpp"
|
||||||
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "oops/instanceKlass.hpp"
|
||||||
|
#include "oops/klass.inline.hpp"
|
||||||
|
#include "runtime/handles.inline.hpp"
|
||||||
|
#include "runtime/java.hpp"
|
||||||
|
|
||||||
|
bool AOTLinkedClassBulkLoader::_boot2_completed = false;
|
||||||
|
bool AOTLinkedClassBulkLoader::_platform_completed = false;
|
||||||
|
bool AOTLinkedClassBulkLoader::_app_completed = false;
|
||||||
|
bool AOTLinkedClassBulkLoader::_all_completed = false;
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::serialize(SerializeClosure* soc, bool is_static_archive) {
|
||||||
|
AOTLinkedClassTable::get(is_static_archive)->serialize(soc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::load_javabase_classes(JavaThread* current) {
|
||||||
|
assert(CDSConfig::is_using_aot_linked_classes(), "sanity");
|
||||||
|
load_classes_in_loader(current, AOTLinkedClassCategory::BOOT1, nullptr); // only java.base classes
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::load_non_javabase_classes(JavaThread* current) {
|
||||||
|
assert(CDSConfig::is_using_aot_linked_classes(), "sanity");
|
||||||
|
|
||||||
|
// is_using_aot_linked_classes() requires is_using_full_module_graph(). As a result,
|
||||||
|
// the platform/system class loader should already have been initialized as part
|
||||||
|
// of the FMG support.
|
||||||
|
assert(CDSConfig::is_using_full_module_graph(), "must be");
|
||||||
|
assert(SystemDictionary::java_platform_loader() != nullptr, "must be");
|
||||||
|
assert(SystemDictionary::java_system_loader() != nullptr, "must be");
|
||||||
|
|
||||||
|
load_classes_in_loader(current, AOTLinkedClassCategory::BOOT2, nullptr); // all boot classes outside of java.base
|
||||||
|
_boot2_completed = true;
|
||||||
|
|
||||||
|
load_classes_in_loader(current, AOTLinkedClassCategory::PLATFORM, SystemDictionary::java_platform_loader());
|
||||||
|
_platform_completed = true;
|
||||||
|
|
||||||
|
load_classes_in_loader(current, AOTLinkedClassCategory::APP, SystemDictionary::java_system_loader());
|
||||||
|
_app_completed = true;
|
||||||
|
_all_completed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::load_classes_in_loader(JavaThread* current, AOTLinkedClassCategory class_category, oop class_loader_oop) {
|
||||||
|
load_classes_in_loader_impl(class_category, class_loader_oop, current);
|
||||||
|
if (current->has_pending_exception()) {
|
||||||
|
// We cannot continue, as we might have loaded some of the aot-linked classes, which
|
||||||
|
// may have dangling C++ pointers to other aot-linked classes that we have failed to load.
|
||||||
|
exit_on_exception(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::exit_on_exception(JavaThread* current) {
|
||||||
|
assert(current->has_pending_exception(), "precondition");
|
||||||
|
ResourceMark rm(current);
|
||||||
|
if (current->pending_exception()->is_a(vmClasses::OutOfMemoryError_klass())) {
|
||||||
|
log_error(cds)("Out of memory. Please run with a larger Java heap, current MaxHeapSize = "
|
||||||
|
SIZE_FORMAT "M", MaxHeapSize/M);
|
||||||
|
} else {
|
||||||
|
log_error(cds)("%s: %s", current->pending_exception()->klass()->external_name(),
|
||||||
|
java_lang_String::as_utf8_string(java_lang_Throwable::message(current->pending_exception())));
|
||||||
|
}
|
||||||
|
vm_exit_during_initialization("Unexpected exception when loading aot-linked classes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::load_classes_in_loader_impl(AOTLinkedClassCategory class_category, oop class_loader_oop, TRAPS) {
|
||||||
|
Handle h_loader(THREAD, class_loader_oop);
|
||||||
|
load_table(AOTLinkedClassTable::for_static_archive(), class_category, h_loader, CHECK);
|
||||||
|
load_table(AOTLinkedClassTable::for_dynamic_archive(), class_category, h_loader, CHECK);
|
||||||
|
|
||||||
|
// Initialize the InstanceKlasses of all archived heap objects that are reachable from the
|
||||||
|
// archived java class mirrors.
|
||||||
|
//
|
||||||
|
// Only the classes in the static archive can have archived mirrors.
|
||||||
|
AOTLinkedClassTable* static_table = AOTLinkedClassTable::for_static_archive();
|
||||||
|
switch (class_category) {
|
||||||
|
case AOTLinkedClassCategory::BOOT1:
|
||||||
|
// Delayed until finish_loading_javabase_classes(), as the VM is not ready to
|
||||||
|
// execute some of the <clinit> methods.
|
||||||
|
break;
|
||||||
|
case AOTLinkedClassCategory::BOOT2:
|
||||||
|
init_required_classes_for_loader(h_loader, static_table->boot2(), CHECK);
|
||||||
|
break;
|
||||||
|
case AOTLinkedClassCategory::PLATFORM:
|
||||||
|
init_required_classes_for_loader(h_loader, static_table->platform(), CHECK);
|
||||||
|
break;
|
||||||
|
case AOTLinkedClassCategory::APP:
|
||||||
|
init_required_classes_for_loader(h_loader, static_table->app(), CHECK);
|
||||||
|
break;
|
||||||
|
case AOTLinkedClassCategory::UNREGISTERED:
|
||||||
|
ShouldNotReachHere();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Universe::is_fully_initialized() && VerifyDuringStartup) {
|
||||||
|
// Make sure we're still in a clean state.
|
||||||
|
VM_Verify verify_op;
|
||||||
|
VMThread::execute(&verify_op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::load_table(AOTLinkedClassTable* table, AOTLinkedClassCategory class_category, Handle loader, TRAPS) {
|
||||||
|
if (class_category != AOTLinkedClassCategory::BOOT1) {
|
||||||
|
assert(Universe::is_module_initialized(), "sanity");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* category_name = AOTClassLinker::class_category_name(class_category);
|
||||||
|
switch (class_category) {
|
||||||
|
case AOTLinkedClassCategory::BOOT1:
|
||||||
|
load_classes_impl(class_category, table->boot(), category_name, loader, CHECK);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AOTLinkedClassCategory::BOOT2:
|
||||||
|
load_classes_impl(class_category, table->boot2(), category_name, loader, CHECK);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AOTLinkedClassCategory::PLATFORM:
|
||||||
|
{
|
||||||
|
initiate_loading(THREAD, category_name, loader, table->boot());
|
||||||
|
initiate_loading(THREAD, category_name, loader, table->boot2());
|
||||||
|
load_classes_impl(class_category, table->platform(), category_name, loader, CHECK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AOTLinkedClassCategory::APP:
|
||||||
|
{
|
||||||
|
initiate_loading(THREAD, category_name, loader, table->boot());
|
||||||
|
initiate_loading(THREAD, category_name, loader, table->boot2());
|
||||||
|
initiate_loading(THREAD, category_name, loader, table->platform());
|
||||||
|
load_classes_impl(class_category, table->app(), category_name, loader, CHECK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AOTLinkedClassCategory::UNREGISTERED:
|
||||||
|
default:
|
||||||
|
ShouldNotReachHere(); // Currently aot-linked classes are not supported for this category.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::load_classes_impl(AOTLinkedClassCategory class_category, Array<InstanceKlass*>* classes,
|
||||||
|
const char* category_name, Handle loader, TRAPS) {
|
||||||
|
if (classes == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(loader());
|
||||||
|
|
||||||
|
for (int i = 0; i < classes->length(); i++) {
|
||||||
|
InstanceKlass* ik = classes->at(i);
|
||||||
|
if (log_is_enabled(Info, cds, aot, load)) {
|
||||||
|
ResourceMark rm(THREAD);
|
||||||
|
log_info(cds, aot, load)("%-5s %s%s%s", category_name, ik->external_name(),
|
||||||
|
ik->is_loaded() ? " (already loaded)" : "",
|
||||||
|
ik->is_hidden() ? " (hidden)" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ik->is_loaded()) {
|
||||||
|
if (ik->is_hidden()) {
|
||||||
|
load_hidden_class(loader_data, ik, CHECK);
|
||||||
|
} else {
|
||||||
|
InstanceKlass* actual;
|
||||||
|
if (loader_data == ClassLoaderData::the_null_class_loader_data()) {
|
||||||
|
actual = SystemDictionary::load_instance_class(ik->name(), loader, CHECK);
|
||||||
|
} else {
|
||||||
|
actual = SystemDictionaryShared::find_or_load_shared_class(ik->name(), loader, CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actual != ik) {
|
||||||
|
ResourceMark rm(THREAD);
|
||||||
|
log_error(cds)("Unable to resolve %s class from CDS archive: %s", category_name, ik->external_name());
|
||||||
|
log_error(cds)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual));
|
||||||
|
log_error(cds)("JVMTI class retransformation is not supported when archive was generated with -XX:+AOTClassLinking.");
|
||||||
|
MetaspaceShared::unrecoverable_loading_error();
|
||||||
|
}
|
||||||
|
assert(actual->is_loaded(), "must be");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate loading of the <classes> in the <initiating_loader>. The <classes> should have already been loaded
|
||||||
|
// by a parent loader of the <initiating_loader>. This is necessary for handling pre-resolved CP entries.
|
||||||
|
//
|
||||||
|
// For example, we initiate the loading of java/lang/String in the AppClassLoader. This will allow
|
||||||
|
// any App classes to have a pre-resolved ConstantPool entry that references java/lang/String.
|
||||||
|
//
|
||||||
|
// TODO: we can limit the number of initiated classes to only those that are actually referenced by
|
||||||
|
// AOT-linked classes loaded by <initiating_loader>.
|
||||||
|
void AOTLinkedClassBulkLoader::initiate_loading(JavaThread* current, const char* category_name,
|
||||||
|
Handle initiating_loader, Array<InstanceKlass*>* classes) {
|
||||||
|
if (classes == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(initiating_loader() == SystemDictionary::java_platform_loader() ||
|
||||||
|
initiating_loader() == SystemDictionary::java_system_loader(), "must be");
|
||||||
|
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(initiating_loader());
|
||||||
|
MonitorLocker mu1(SystemDictionary_lock);
|
||||||
|
|
||||||
|
for (int i = 0; i < classes->length(); i++) {
|
||||||
|
InstanceKlass* ik = classes->at(i);
|
||||||
|
assert(ik->is_loaded(), "must have already been loaded by a parent loader");
|
||||||
|
assert(ik->class_loader() != initiating_loader(), "must be a parent loader");
|
||||||
|
assert(ik->class_loader() == nullptr ||
|
||||||
|
ik->class_loader() == SystemDictionary::java_platform_loader(), "must be");
|
||||||
|
if (ik->is_public() && !ik->is_hidden()) {
|
||||||
|
if (log_is_enabled(Info, cds, aot, load)) {
|
||||||
|
ResourceMark rm(current);
|
||||||
|
const char* defining_loader = (ik->class_loader() == nullptr ? "boot" : "plat");
|
||||||
|
log_info(cds, aot, load)("%s %s (initiated, defined by %s)", category_name, ik->external_name(),
|
||||||
|
defining_loader);
|
||||||
|
}
|
||||||
|
SystemDictionary::add_to_initiating_loader(current, ik, loader_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently, we archive only three types of hidden classes:
|
||||||
|
// - LambdaForms
|
||||||
|
// - lambda proxy classes
|
||||||
|
// - StringConcat classes
|
||||||
|
// See HeapShared::is_archivable_hidden_klass().
|
||||||
|
//
|
||||||
|
// LambdaForm classes (with names like java/lang/invoke/LambdaForm$MH+0x800000015) logically
|
||||||
|
// belong to the boot loader, but they are usually stored in their own special ClassLoaderData to
|
||||||
|
// facilitate class unloading, as a LambdaForm may refer to a class loaded by a custom loader
|
||||||
|
// that may be unloaded.
|
||||||
|
//
|
||||||
|
// We only support AOT-resolution of indys in the boot/platform/app loader, so there's no need
|
||||||
|
// to support class unloading. For simplicity, we put all archived LambdaForm classes in the
|
||||||
|
// "main" ClassLoaderData of the boot loader.
|
||||||
|
//
|
||||||
|
// (Even if we were to support other loaders, we would still feel free to ignore any requirement
|
||||||
|
// of class unloading, for any class asset in the AOT cache. Anything that makes it into the AOT
|
||||||
|
// cache has a lifetime dispensation from unloading. After all, the AOT cache never grows, and
|
||||||
|
// we can assume that the user is content with its size, and doesn't need its footprint to shrink.)
|
||||||
|
//
|
||||||
|
// Lambda proxy classes are normally stored in the same ClassLoaderData as their nest hosts, and
|
||||||
|
// StringConcat are normally stored in the main ClassLoaderData of the boot class loader. We
|
||||||
|
// do the same for the archived copies of such classes.
|
||||||
|
void AOTLinkedClassBulkLoader::load_hidden_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS) {
|
||||||
|
assert(HeapShared::is_lambda_form_klass(ik) ||
|
||||||
|
HeapShared::is_lambda_proxy_klass(ik) ||
|
||||||
|
HeapShared::is_string_concat_klass(ik), "sanity");
|
||||||
|
DEBUG_ONLY({
|
||||||
|
assert(ik->java_super()->is_loaded(), "must be");
|
||||||
|
for (int i = 0; i < ik->local_interfaces()->length(); i++) {
|
||||||
|
assert(ik->local_interfaces()->at(i)->is_loaded(), "must be");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Handle pd;
|
||||||
|
PackageEntry* pkg_entry = nullptr;
|
||||||
|
|
||||||
|
// Since a hidden class does not have a name, it cannot be reloaded
|
||||||
|
// normally via the system dictionary. Instead, we have to finish the
|
||||||
|
// loading job here.
|
||||||
|
|
||||||
|
if (HeapShared::is_lambda_proxy_klass(ik)) {
|
||||||
|
InstanceKlass* nest_host = ik->nest_host_not_null();
|
||||||
|
assert(nest_host->is_loaded(), "must be");
|
||||||
|
pd = Handle(THREAD, nest_host->protection_domain());
|
||||||
|
pkg_entry = nest_host->package();
|
||||||
|
}
|
||||||
|
|
||||||
|
ik->restore_unshareable_info(loader_data, pd, pkg_entry, CHECK);
|
||||||
|
SystemDictionary::load_shared_class_misc(ik, loader_data);
|
||||||
|
ik->add_to_hierarchy(THREAD);
|
||||||
|
assert(ik->is_loaded(), "Must be in at least loaded state");
|
||||||
|
|
||||||
|
DEBUG_ONLY({
|
||||||
|
// Make sure we don't make this hidden class available by name, even if we don't
|
||||||
|
// use any special ClassLoaderData.
|
||||||
|
Handle loader(THREAD, loader_data->class_loader());
|
||||||
|
ResourceMark rm(THREAD);
|
||||||
|
assert(SystemDictionary::resolve_or_null(ik->name(), loader, pd, THREAD) == nullptr,
|
||||||
|
"hidden classes cannot be accessible by name: %s", ik->external_name());
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
CLEAR_PENDING_EXCEPTION;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AOTLinkedClassBulkLoader::finish_loading_javabase_classes(TRAPS) {
|
||||||
|
init_required_classes_for_loader(Handle(), AOTLinkedClassTable::for_static_archive()->boot(), CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some AOT-linked classes for <class_loader> must be initialized early. This includes
|
||||||
|
// - classes that were AOT-initialized by AOTClassInitializer
|
||||||
|
// - the classes of all objects that are reachable from the archived mirrors of
|
||||||
|
// the AOT-linked classes for <class_loader>.
|
||||||
|
void AOTLinkedClassBulkLoader::init_required_classes_for_loader(Handle class_loader, Array<InstanceKlass*>* classes, TRAPS) {
|
||||||
|
if (classes != nullptr) {
|
||||||
|
for (int i = 0; i < classes->length(); i++) {
|
||||||
|
InstanceKlass* ik = classes->at(i);
|
||||||
|
if (ik->class_loader_data() == nullptr) {
|
||||||
|
// This class is not yet loaded. We will initialize it in a later phase.
|
||||||
|
// For example, we have loaded only AOTLinkedClassCategory::BOOT1 classes
|
||||||
|
// but k is part of AOTLinkedClassCategory::BOOT2.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ik->has_aot_initialized_mirror()) {
|
||||||
|
ik->initialize_with_aot_initialized_mirror(CHECK);
|
||||||
|
} else {
|
||||||
|
// Some cached heap objects may hold references to methods in aot-linked
|
||||||
|
// classes (via MemberName). We need to make sure all classes are
|
||||||
|
// linked to allow such MemberNames to be invoked.
|
||||||
|
ik->link_class(CHECK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HeapShared::init_classes_for_special_subgraph(class_loader, CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AOTLinkedClassBulkLoader::is_pending_aot_linked_class(Klass* k) {
|
||||||
|
if (!CDSConfig::is_using_aot_linked_classes()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_all_completed) { // no more pending aot-linked classes
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k->is_objArray_klass()) {
|
||||||
|
k = ObjArrayKlass::cast(k)->bottom_klass();
|
||||||
|
}
|
||||||
|
if (!k->is_instance_klass()) {
|
||||||
|
// type array klasses (and their higher dimensions),
|
||||||
|
// must have been loaded before a GC can ever happen.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's a small window during VM start-up where a not-yet loaded aot-linked
|
||||||
|
// class k may be discovered by the GC during VM initialization. This can happen
|
||||||
|
// when the heap contains an aot-cached instance of k, but k is not ready to be
|
||||||
|
// loaded yet. (TODO: JDK-8342429 eliminates this possibility)
|
||||||
|
//
|
||||||
|
// The following checks try to limit this window as much as possible for each of
|
||||||
|
// the four AOTLinkedClassCategory of classes that can be aot-linked.
|
||||||
|
|
||||||
|
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||||
|
if (ik->is_shared_boot_class()) {
|
||||||
|
if (ik->module() != nullptr && ik->in_javabase_module()) {
|
||||||
|
// AOTLinkedClassCategory::BOOT1 -- all aot-linked classes in
|
||||||
|
// java.base must have been loaded before a GC can ever happen.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// AOTLinkedClassCategory::BOOT2 classes cannot be loaded until
|
||||||
|
// module system is ready.
|
||||||
|
return !_boot2_completed;
|
||||||
|
}
|
||||||
|
} else if (ik->is_shared_platform_class()) {
|
||||||
|
// AOTLinkedClassCategory::PLATFORM classes cannot be loaded until
|
||||||
|
// the platform class loader is initialized.
|
||||||
|
return !_platform_completed;
|
||||||
|
} else if (ik->is_shared_app_class()) {
|
||||||
|
// AOTLinkedClassCategory::APP cannot be loaded until the app class loader
|
||||||
|
// is initialized.
|
||||||
|
return !_app_completed;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
69
src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp
Normal file
69
src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP
|
||||||
|
#define SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP
|
||||||
|
|
||||||
|
#include "memory/allStatic.hpp"
|
||||||
|
#include "memory/allocation.hpp"
|
||||||
|
#include "runtime/handles.hpp"
|
||||||
|
#include "utilities/exceptions.hpp"
|
||||||
|
#include "utilities/macros.hpp"
|
||||||
|
|
||||||
|
class AOTLinkedClassTable;
|
||||||
|
class ClassLoaderData;
|
||||||
|
class InstanceKlass;
|
||||||
|
class SerializeClosure;
|
||||||
|
template <typename T> class Array;
|
||||||
|
enum class AOTLinkedClassCategory : int;
|
||||||
|
|
||||||
|
// During a Production Run, the AOTLinkedClassBulkLoader loads all classes from
|
||||||
|
// a AOTLinkedClassTable into their respective ClassLoaders. This happens very early
|
||||||
|
// in the JVM bootstrap stage, before any application code is executed.
|
||||||
|
//
|
||||||
|
class AOTLinkedClassBulkLoader : AllStatic {
|
||||||
|
static bool _boot2_completed;
|
||||||
|
static bool _platform_completed;
|
||||||
|
static bool _app_completed;
|
||||||
|
static bool _all_completed;
|
||||||
|
static void load_classes_in_loader(JavaThread* current, AOTLinkedClassCategory class_category, oop class_loader_oop);
|
||||||
|
static void load_classes_in_loader_impl(AOTLinkedClassCategory class_category, oop class_loader_oop, TRAPS);
|
||||||
|
static void load_table(AOTLinkedClassTable* table, AOTLinkedClassCategory class_category, Handle loader, TRAPS);
|
||||||
|
static void initiate_loading(JavaThread* current, const char* category, Handle initiating_loader, Array<InstanceKlass*>* classes);
|
||||||
|
static void load_classes_impl(AOTLinkedClassCategory class_category, Array<InstanceKlass*>* classes,
|
||||||
|
const char* category_name, Handle loader, TRAPS);
|
||||||
|
static void load_hidden_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS);
|
||||||
|
static void init_required_classes_for_loader(Handle class_loader, Array<InstanceKlass*>* classes, TRAPS);
|
||||||
|
public:
|
||||||
|
static void serialize(SerializeClosure* soc, bool is_static_archive) NOT_CDS_RETURN;
|
||||||
|
|
||||||
|
static void load_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
|
||||||
|
static void load_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
|
||||||
|
static void finish_loading_javabase_classes(TRAPS) NOT_CDS_RETURN;
|
||||||
|
static void exit_on_exception(JavaThread* current);
|
||||||
|
|
||||||
|
static bool is_pending_aot_linked_class(Klass* k) NOT_CDS_RETURN_(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP
|
40
src/hotspot/share/cds/aotLinkedClassTable.cpp
Normal file
40
src/hotspot/share/cds/aotLinkedClassTable.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotLinkedClassTable.hpp"
|
||||||
|
#include "cds/cdsConfig.hpp"
|
||||||
|
#include "cds/serializeClosure.hpp"
|
||||||
|
#include "oops/array.hpp"
|
||||||
|
|
||||||
|
AOTLinkedClassTable AOTLinkedClassTable::_for_static_archive;
|
||||||
|
AOTLinkedClassTable AOTLinkedClassTable::_for_dynamic_archive;
|
||||||
|
|
||||||
|
void AOTLinkedClassTable::serialize(SerializeClosure* soc) {
|
||||||
|
soc->do_ptr((void**)&_boot);
|
||||||
|
soc->do_ptr((void**)&_boot2);
|
||||||
|
soc->do_ptr((void**)&_platform);
|
||||||
|
soc->do_ptr((void**)&_app);
|
||||||
|
}
|
||||||
|
|
77
src/hotspot/share/cds/aotLinkedClassTable.hpp
Normal file
77
src/hotspot/share/cds/aotLinkedClassTable.hpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARE_CDS_AOTLINKEDCLASSTABLE_HPP
|
||||||
|
#define SHARE_CDS_AOTLINKEDCLASSTABLE_HPP
|
||||||
|
|
||||||
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
|
||||||
|
template <typename T> class Array;
|
||||||
|
class InstanceKlass;
|
||||||
|
class SerializeClosure;
|
||||||
|
|
||||||
|
// Classes to be bulk-loaded, in the "linked" state, at VM bootstrap.
|
||||||
|
//
|
||||||
|
// AOTLinkedClassTable is produced by AOTClassLinker when an AOTCache is assembled.
|
||||||
|
//
|
||||||
|
// AOTLinkedClassTable is consumed by AOTLinkedClassBulkLoader when an AOTCache is used
|
||||||
|
// in a production run.
|
||||||
|
//
|
||||||
|
class AOTLinkedClassTable {
|
||||||
|
// The VM may load up to 2 CDS archives -- static and dynamic. Each
|
||||||
|
// archive can have its own AOTLinkedClassTable.
|
||||||
|
static AOTLinkedClassTable _for_static_archive;
|
||||||
|
static AOTLinkedClassTable _for_dynamic_archive;
|
||||||
|
|
||||||
|
Array<InstanceKlass*>* _boot; // only java.base classes
|
||||||
|
Array<InstanceKlass*>* _boot2; // boot classes in other modules
|
||||||
|
Array<InstanceKlass*>* _platform;
|
||||||
|
Array<InstanceKlass*>* _app;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AOTLinkedClassTable() :
|
||||||
|
_boot(nullptr), _boot2(nullptr),
|
||||||
|
_platform(nullptr), _app(nullptr) {}
|
||||||
|
|
||||||
|
static AOTLinkedClassTable* for_static_archive() { return &_for_static_archive; }
|
||||||
|
static AOTLinkedClassTable* for_dynamic_archive() { return &_for_dynamic_archive; }
|
||||||
|
|
||||||
|
static AOTLinkedClassTable* get(bool is_static_archive) {
|
||||||
|
return is_static_archive ? for_static_archive() : for_dynamic_archive();
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<InstanceKlass*>* boot() const { return _boot; }
|
||||||
|
Array<InstanceKlass*>* boot2() const { return _boot2; }
|
||||||
|
Array<InstanceKlass*>* platform() const { return _platform; }
|
||||||
|
Array<InstanceKlass*>* app() const { return _app; }
|
||||||
|
|
||||||
|
void set_boot (Array<InstanceKlass*>* value) { _boot = value; }
|
||||||
|
void set_boot2 (Array<InstanceKlass*>* value) { _boot2 = value; }
|
||||||
|
void set_platform(Array<InstanceKlass*>* value) { _platform = value; }
|
||||||
|
void set_app (Array<InstanceKlass*>* value) { _app = value; }
|
||||||
|
|
||||||
|
void serialize(SerializeClosure* soc);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARE_CDS_AOTLINKEDCLASSTABLE_HPP
|
@ -23,6 +23,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassLinker.hpp"
|
||||||
|
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||||
#include "cds/archiveBuilder.hpp"
|
#include "cds/archiveBuilder.hpp"
|
||||||
#include "cds/archiveHeapWriter.hpp"
|
#include "cds/archiveHeapWriter.hpp"
|
||||||
#include "cds/archiveUtils.hpp"
|
#include "cds/archiveUtils.hpp"
|
||||||
@ -33,7 +35,9 @@
|
|||||||
#include "cds/heapShared.hpp"
|
#include "cds/heapShared.hpp"
|
||||||
#include "cds/metaspaceShared.hpp"
|
#include "cds/metaspaceShared.hpp"
|
||||||
#include "cds/regeneratedClasses.hpp"
|
#include "cds/regeneratedClasses.hpp"
|
||||||
|
#include "classfile/classLoader.hpp"
|
||||||
#include "classfile/classLoaderDataShared.hpp"
|
#include "classfile/classLoaderDataShared.hpp"
|
||||||
|
#include "classfile/classLoaderExt.hpp"
|
||||||
#include "classfile/javaClasses.hpp"
|
#include "classfile/javaClasses.hpp"
|
||||||
#include "classfile/symbolTable.hpp"
|
#include "classfile/symbolTable.hpp"
|
||||||
#include "classfile/systemDictionaryShared.hpp"
|
#include "classfile/systemDictionaryShared.hpp"
|
||||||
@ -226,6 +230,10 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re
|
|||||||
assert(klass->is_klass(), "must be");
|
assert(klass->is_klass(), "must be");
|
||||||
if (!is_excluded(klass)) {
|
if (!is_excluded(klass)) {
|
||||||
_klasses->append(klass);
|
_klasses->append(klass);
|
||||||
|
if (klass->is_hidden()) {
|
||||||
|
assert(klass->is_instance_klass(), "must be");
|
||||||
|
assert(SystemDictionaryShared::should_hidden_class_be_archived(InstanceKlass::cast(klass)), "must be");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// See RunTimeClassInfo::get_for(): make sure we have enough space for both maximum
|
// See RunTimeClassInfo::get_for(): make sure we have enough space for both maximum
|
||||||
// Klass alignment as well as the RuntimeInfo* pointer we will embed in front of a Klass.
|
// Klass alignment as well as the RuntimeInfo* pointer we will embed in front of a Klass.
|
||||||
@ -284,6 +292,8 @@ void ArchiveBuilder::gather_klasses_and_symbols() {
|
|||||||
// but this should be enough for now
|
// but this should be enough for now
|
||||||
_estimated_metaspaceobj_bytes += 200 * 1024 * 1024;
|
_estimated_metaspaceobj_bytes += 200 * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AOTClassLinker::add_candidates();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ArchiveBuilder::compare_symbols_by_address(Symbol** a, Symbol** b) {
|
int ArchiveBuilder::compare_symbols_by_address(Symbol** a, Symbol** b) {
|
||||||
@ -310,6 +320,15 @@ size_t ArchiveBuilder::estimate_archive_size() {
|
|||||||
size_t dictionary_est = SystemDictionaryShared::estimate_size_for_archive();
|
size_t dictionary_est = SystemDictionaryShared::estimate_size_for_archive();
|
||||||
_estimated_hashtable_bytes = symbol_table_est + dictionary_est;
|
_estimated_hashtable_bytes = symbol_table_est + dictionary_est;
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
// This is difficult to estimate when dumping the dynamic archive, as the
|
||||||
|
// AOTLinkedClassTable may need to contain classes in the static archive as well.
|
||||||
|
//
|
||||||
|
// Just give a generous estimate for now. We will remove estimate_archive_size()
|
||||||
|
// in JDK-8340416
|
||||||
|
_estimated_hashtable_bytes += 20 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
|
|
||||||
total += _estimated_metaspaceobj_bytes;
|
total += _estimated_metaspaceobj_bytes;
|
||||||
@ -423,11 +442,12 @@ bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* ref, bool read
|
|||||||
if (src_obj == nullptr) {
|
if (src_obj == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remember_embedded_pointer_in_enclosing_obj(ref);
|
||||||
if (RegeneratedClasses::has_been_regenerated(src_obj)) {
|
if (RegeneratedClasses::has_been_regenerated(src_obj)) {
|
||||||
// No need to copy it. We will later relocate it to point to the regenerated klass/method.
|
// No need to copy it. We will later relocate it to point to the regenerated klass/method.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
remember_embedded_pointer_in_enclosing_obj(ref);
|
|
||||||
|
|
||||||
FollowMode follow_mode = get_follow_mode(ref);
|
FollowMode follow_mode = get_follow_mode(ref);
|
||||||
SourceObjInfo src_info(ref, read_only, follow_mode);
|
SourceObjInfo src_info(ref, read_only, follow_mode);
|
||||||
@ -740,6 +760,16 @@ void ArchiveBuilder::mark_and_relocate_to_buffered_addr(address* ptr_location) {
|
|||||||
ArchivePtrMarker::mark_pointer(ptr_location);
|
ArchivePtrMarker::mark_pointer(ptr_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ArchiveBuilder::has_been_buffered(address src_addr) const {
|
||||||
|
if (RegeneratedClasses::has_been_regenerated(src_addr) ||
|
||||||
|
_src_obj_table.get(src_addr) == nullptr ||
|
||||||
|
get_buffered_addr(src_addr) == nullptr) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
address ArchiveBuilder::get_buffered_addr(address src_addr) const {
|
address ArchiveBuilder::get_buffered_addr(address src_addr) const {
|
||||||
SourceObjInfo* p = _src_obj_table.get(src_addr);
|
SourceObjInfo* p = _src_obj_table.get(src_addr);
|
||||||
assert(p != nullptr, "src_addr " INTPTR_FORMAT " is used but has not been archived",
|
assert(p != nullptr, "src_addr " INTPTR_FORMAT " is used but has not been archived",
|
||||||
@ -767,17 +797,35 @@ void ArchiveBuilder::relocate_metaspaceobj_embedded_pointers() {
|
|||||||
relocate_embedded_pointers(&_ro_src_objs);
|
relocate_embedded_pointers(&_ro_src_objs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ADD_COUNT(x) \
|
||||||
|
x += 1; \
|
||||||
|
x ## _a += aotlinked ? 1 : 0; \
|
||||||
|
x ## _i += inited ? 1 : 0;
|
||||||
|
|
||||||
|
#define DECLARE_INSTANCE_KLASS_COUNTER(x) \
|
||||||
|
int x = 0; \
|
||||||
|
int x ## _a = 0; \
|
||||||
|
int x ## _i = 0;
|
||||||
|
|
||||||
void ArchiveBuilder::make_klasses_shareable() {
|
void ArchiveBuilder::make_klasses_shareable() {
|
||||||
int num_instance_klasses = 0;
|
DECLARE_INSTANCE_KLASS_COUNTER(num_instance_klasses);
|
||||||
int num_boot_klasses = 0;
|
DECLARE_INSTANCE_KLASS_COUNTER(num_boot_klasses);
|
||||||
int num_platform_klasses = 0;
|
DECLARE_INSTANCE_KLASS_COUNTER(num_vm_klasses);
|
||||||
int num_app_klasses = 0;
|
DECLARE_INSTANCE_KLASS_COUNTER(num_platform_klasses);
|
||||||
int num_hidden_klasses = 0;
|
DECLARE_INSTANCE_KLASS_COUNTER(num_app_klasses);
|
||||||
|
DECLARE_INSTANCE_KLASS_COUNTER(num_old_klasses);
|
||||||
|
DECLARE_INSTANCE_KLASS_COUNTER(num_hidden_klasses);
|
||||||
|
DECLARE_INSTANCE_KLASS_COUNTER(num_enum_klasses);
|
||||||
|
DECLARE_INSTANCE_KLASS_COUNTER(num_unregistered_klasses);
|
||||||
int num_unlinked_klasses = 0;
|
int num_unlinked_klasses = 0;
|
||||||
int num_unregistered_klasses = 0;
|
|
||||||
int num_obj_array_klasses = 0;
|
int num_obj_array_klasses = 0;
|
||||||
int num_type_array_klasses = 0;
|
int num_type_array_klasses = 0;
|
||||||
|
|
||||||
|
int boot_unlinked = 0;
|
||||||
|
int platform_unlinked = 0;
|
||||||
|
int app_unlinked = 0;
|
||||||
|
int unreg_unlinked = 0;
|
||||||
|
|
||||||
for (int i = 0; i < klasses()->length(); i++) {
|
for (int i = 0; i < klasses()->length(); i++) {
|
||||||
// Some of the code in ConstantPool::remove_unshareable_info() requires the classes
|
// Some of the code in ConstantPool::remove_unshareable_info() requires the classes
|
||||||
// to be in linked state, so it must be call here before the next loop, which returns
|
// to be in linked state, so it must be call here before the next loop, which returns
|
||||||
@ -791,8 +839,12 @@ void ArchiveBuilder::make_klasses_shareable() {
|
|||||||
for (int i = 0; i < klasses()->length(); i++) {
|
for (int i = 0; i < klasses()->length(); i++) {
|
||||||
const char* type;
|
const char* type;
|
||||||
const char* unlinked = "";
|
const char* unlinked = "";
|
||||||
|
const char* kind = "";
|
||||||
const char* hidden = "";
|
const char* hidden = "";
|
||||||
|
const char* old = "";
|
||||||
const char* generated = "";
|
const char* generated = "";
|
||||||
|
const char* aotlinked_msg = "";
|
||||||
|
const char* inited_msg = "";
|
||||||
Klass* k = get_buffered_addr(klasses()->at(i));
|
Klass* k = get_buffered_addr(klasses()->at(i));
|
||||||
k->remove_java_mirror();
|
k->remove_java_mirror();
|
||||||
#ifdef _LP64
|
#ifdef _LP64
|
||||||
@ -815,60 +867,127 @@ void ArchiveBuilder::make_klasses_shareable() {
|
|||||||
k->remove_unshareable_info();
|
k->remove_unshareable_info();
|
||||||
} else {
|
} else {
|
||||||
assert(k->is_instance_klass(), " must be");
|
assert(k->is_instance_klass(), " must be");
|
||||||
num_instance_klasses ++;
|
|
||||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||||
if (ik->is_shared_boot_class()) {
|
InstanceKlass* src_ik = get_source_addr(ik);
|
||||||
|
bool aotlinked = AOTClassLinker::is_candidate(src_ik);
|
||||||
|
bool inited = ik->has_aot_initialized_mirror();
|
||||||
|
ADD_COUNT(num_instance_klasses);
|
||||||
|
if (CDSConfig::is_dumping_dynamic_archive()) {
|
||||||
|
// For static dump, class loader type are already set.
|
||||||
|
ik->assign_class_loader_type();
|
||||||
|
}
|
||||||
|
if (ik->is_hidden()) {
|
||||||
|
ADD_COUNT(num_hidden_klasses);
|
||||||
|
hidden = " hidden";
|
||||||
|
oop loader = k->class_loader();
|
||||||
|
if (loader == nullptr) {
|
||||||
|
type = "boot";
|
||||||
|
ADD_COUNT(num_boot_klasses);
|
||||||
|
} else if (loader == SystemDictionary::java_platform_loader()) {
|
||||||
|
type = "plat";
|
||||||
|
ADD_COUNT(num_platform_klasses);
|
||||||
|
} else if (loader == SystemDictionary::java_system_loader()) {
|
||||||
|
type = "app";
|
||||||
|
ADD_COUNT(num_app_klasses);
|
||||||
|
} else {
|
||||||
|
type = "bad";
|
||||||
|
assert(0, "shouldn't happen");
|
||||||
|
}
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
assert(HeapShared::is_archivable_hidden_klass(ik), "sanity");
|
||||||
|
} else {
|
||||||
|
// Legacy CDS support for lambda proxies
|
||||||
|
assert(HeapShared::is_lambda_proxy_klass(ik), "sanity");
|
||||||
|
}
|
||||||
|
} else if (ik->is_shared_boot_class()) {
|
||||||
type = "boot";
|
type = "boot";
|
||||||
num_boot_klasses ++;
|
ADD_COUNT(num_boot_klasses);
|
||||||
} else if (ik->is_shared_platform_class()) {
|
} else if (ik->is_shared_platform_class()) {
|
||||||
type = "plat";
|
type = "plat";
|
||||||
num_platform_klasses ++;
|
ADD_COUNT(num_platform_klasses);
|
||||||
} else if (ik->is_shared_app_class()) {
|
} else if (ik->is_shared_app_class()) {
|
||||||
type = "app";
|
type = "app";
|
||||||
num_app_klasses ++;
|
ADD_COUNT(num_app_klasses);
|
||||||
} else {
|
} else {
|
||||||
assert(ik->is_shared_unregistered_class(), "must be");
|
assert(ik->is_shared_unregistered_class(), "must be");
|
||||||
type = "unreg";
|
type = "unreg";
|
||||||
num_unregistered_klasses ++;
|
ADD_COUNT(num_unregistered_klasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AOTClassLinker::is_vm_class(src_ik)) {
|
||||||
|
ADD_COUNT(num_vm_klasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ik->is_linked()) {
|
if (!ik->is_linked()) {
|
||||||
num_unlinked_klasses ++;
|
num_unlinked_klasses ++;
|
||||||
unlinked = " ** unlinked";
|
unlinked = " unlinked";
|
||||||
|
if (ik->is_shared_boot_class()) {
|
||||||
|
boot_unlinked ++;
|
||||||
|
} else if (ik->is_shared_platform_class()) {
|
||||||
|
platform_unlinked ++;
|
||||||
|
} else if (ik->is_shared_app_class()) {
|
||||||
|
app_unlinked ++;
|
||||||
|
} else {
|
||||||
|
unreg_unlinked ++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ik->is_hidden()) {
|
if (ik->is_interface()) {
|
||||||
num_hidden_klasses ++;
|
kind = " interface";
|
||||||
hidden = " ** hidden";
|
} else if (src_ik->is_enum_subclass()) {
|
||||||
|
kind = " enum";
|
||||||
|
ADD_COUNT(num_enum_klasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ik->can_be_verified_at_dumptime()) {
|
||||||
|
ADD_COUNT(num_old_klasses);
|
||||||
|
old = " old";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ik->is_generated_shared_class()) {
|
if (ik->is_generated_shared_class()) {
|
||||||
generated = " ** generated";
|
generated = " generated";
|
||||||
}
|
}
|
||||||
|
if (aotlinked) {
|
||||||
|
aotlinked_msg = " aot-linked";
|
||||||
|
}
|
||||||
|
if (inited) {
|
||||||
|
inited_msg = " inited";
|
||||||
|
}
|
||||||
|
|
||||||
MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik);
|
MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik);
|
||||||
ik->remove_unshareable_info();
|
ik->remove_unshareable_info();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_is_enabled(Debug, cds, class)) {
|
if (log_is_enabled(Debug, cds, class)) {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
log_debug(cds, class)("klasses[%5d] = " PTR_FORMAT " %-5s %s%s%s%s", i,
|
log_debug(cds, class)("klasses[%5d] = " PTR_FORMAT " %-5s %s%s%s%s%s%s%s%s", i,
|
||||||
p2i(to_requested(k)), type, k->external_name(),
|
p2i(to_requested(k)), type, k->external_name(),
|
||||||
hidden, unlinked, generated);
|
kind, hidden, old, unlinked, generated, aotlinked_msg, inited_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define STATS_FORMAT "= %5d, aot-linked = %5d, inited = %5d"
|
||||||
|
#define STATS_PARAMS(x) num_ ## x, num_ ## x ## _a, num_ ## x ## _i
|
||||||
|
|
||||||
log_info(cds)("Number of classes %d", num_instance_klasses + num_obj_array_klasses + num_type_array_klasses);
|
log_info(cds)("Number of classes %d", num_instance_klasses + num_obj_array_klasses + num_type_array_klasses);
|
||||||
log_info(cds)(" instance classes = %5d", num_instance_klasses);
|
log_info(cds)(" instance classes " STATS_FORMAT, STATS_PARAMS(instance_klasses));
|
||||||
log_info(cds)(" boot = %5d", num_boot_klasses);
|
log_info(cds)(" boot " STATS_FORMAT, STATS_PARAMS(boot_klasses));
|
||||||
log_info(cds)(" app = %5d", num_app_klasses);
|
log_info(cds)(" vm " STATS_FORMAT, STATS_PARAMS(vm_klasses));
|
||||||
log_info(cds)(" platform = %5d", num_platform_klasses);
|
log_info(cds)(" platform " STATS_FORMAT, STATS_PARAMS(platform_klasses));
|
||||||
log_info(cds)(" unregistered = %5d", num_unregistered_klasses);
|
log_info(cds)(" app " STATS_FORMAT, STATS_PARAMS(app_klasses));
|
||||||
log_info(cds)(" (hidden) = %5d", num_hidden_klasses);
|
log_info(cds)(" unregistered " STATS_FORMAT, STATS_PARAMS(unregistered_klasses));
|
||||||
log_info(cds)(" (unlinked) = %5d", num_unlinked_klasses);
|
log_info(cds)(" (enum) " STATS_FORMAT, STATS_PARAMS(enum_klasses));
|
||||||
|
log_info(cds)(" (hidden) " STATS_FORMAT, STATS_PARAMS(hidden_klasses));
|
||||||
|
log_info(cds)(" (old) " STATS_FORMAT, STATS_PARAMS(old_klasses));
|
||||||
|
log_info(cds)(" (unlinked) = %5d, boot = %d, plat = %d, app = %d, unreg = %d",
|
||||||
|
num_unlinked_klasses, boot_unlinked, platform_unlinked, app_unlinked, unreg_unlinked);
|
||||||
log_info(cds)(" obj array classes = %5d", num_obj_array_klasses);
|
log_info(cds)(" obj array classes = %5d", num_obj_array_klasses);
|
||||||
log_info(cds)(" type array classes = %5d", num_type_array_klasses);
|
log_info(cds)(" type array classes = %5d", num_type_array_klasses);
|
||||||
log_info(cds)(" symbols = %5d", _symbols->length());
|
log_info(cds)(" symbols = %5d", _symbols->length());
|
||||||
|
|
||||||
|
#undef STATS_FORMAT
|
||||||
|
#undef STATS_PARAMS
|
||||||
|
|
||||||
DynamicArchive::make_array_klasses_shareable();
|
DynamicArchive::make_array_klasses_shareable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -876,6 +995,7 @@ void ArchiveBuilder::serialize_dynamic_archivable_items(SerializeClosure* soc) {
|
|||||||
SymbolTable::serialize_shared_table_header(soc, false);
|
SymbolTable::serialize_shared_table_header(soc, false);
|
||||||
SystemDictionaryShared::serialize_dictionary_headers(soc, false);
|
SystemDictionaryShared::serialize_dictionary_headers(soc, false);
|
||||||
DynamicArchive::serialize_array_klasses(soc);
|
DynamicArchive::serialize_array_klasses(soc);
|
||||||
|
AOTLinkedClassBulkLoader::serialize(soc, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
uintx ArchiveBuilder::buffer_to_offset(address p) const {
|
uintx ArchiveBuilder::buffer_to_offset(address p) const {
|
||||||
|
@ -292,7 +292,7 @@ public:
|
|||||||
intx buffer_to_requested_delta() const { return _buffer_to_requested_delta; }
|
intx buffer_to_requested_delta() const { return _buffer_to_requested_delta; }
|
||||||
|
|
||||||
bool is_in_buffer_space(address p) const {
|
bool is_in_buffer_space(address p) const {
|
||||||
return (buffer_bottom() <= p && p < buffer_top());
|
return (buffer_bottom() != nullptr && buffer_bottom() <= p && p < buffer_top());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> bool is_in_requested_static_archive(T p) const {
|
template <typename T> bool is_in_requested_static_archive(T p) const {
|
||||||
@ -420,6 +420,11 @@ public:
|
|||||||
mark_and_relocate_to_buffered_addr((address*)ptr_location);
|
mark_and_relocate_to_buffered_addr((address*)ptr_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_been_buffered(address src_addr) const;
|
||||||
|
template <typename T> bool has_been_buffered(T src_addr) const {
|
||||||
|
return has_been_buffered((address)src_addr);
|
||||||
|
}
|
||||||
|
|
||||||
address get_buffered_addr(address src_addr) const;
|
address get_buffered_addr(address src_addr) const;
|
||||||
template <typename T> T get_buffered_addr(T src_addr) const {
|
template <typename T> T get_buffered_addr(T src_addr) const {
|
||||||
return (T)get_buffered_addr((address)src_addr);
|
return (T)get_buffered_addr((address)src_addr);
|
||||||
|
@ -449,10 +449,6 @@ class PatchNativePointers: public BitMapClosure {
|
|||||||
bool do_bit(size_t offset) {
|
bool do_bit(size_t offset) {
|
||||||
Metadata** p = _start + offset;
|
Metadata** p = _start + offset;
|
||||||
*p = (Metadata*)(address(*p) + MetaspaceShared::relocation_delta());
|
*p = (Metadata*)(address(*p) + MetaspaceShared::relocation_delta());
|
||||||
// Currently we have only Klass pointers in heap objects.
|
|
||||||
// This needs to be relaxed when we support other types of native
|
|
||||||
// pointers such as Method.
|
|
||||||
assert(((Klass*)(*p))->is_klass(), "must be");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,14 +27,15 @@
|
|||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "cds/filemap.hpp"
|
#include "cds/filemap.hpp"
|
||||||
#include "cds/heapShared.hpp"
|
#include "cds/heapShared.hpp"
|
||||||
|
#include "classfile/javaClasses.hpp"
|
||||||
#include "classfile/systemDictionary.hpp"
|
#include "classfile/systemDictionary.hpp"
|
||||||
#include "gc/shared/collectedHeap.hpp"
|
#include "gc/shared/collectedHeap.hpp"
|
||||||
#include "memory/iterator.inline.hpp"
|
#include "memory/iterator.inline.hpp"
|
||||||
#include "memory/oopFactory.hpp"
|
#include "memory/oopFactory.hpp"
|
||||||
#include "memory/universe.hpp"
|
#include "memory/universe.hpp"
|
||||||
#include "oops/compressedOops.hpp"
|
#include "oops/compressedOops.hpp"
|
||||||
#include "oops/oop.inline.hpp"
|
|
||||||
#include "oops/objArrayOop.inline.hpp"
|
#include "oops/objArrayOop.inline.hpp"
|
||||||
|
#include "oops/oop.inline.hpp"
|
||||||
#include "oops/oopHandle.inline.hpp"
|
#include "oops/oopHandle.inline.hpp"
|
||||||
#include "oops/typeArrayKlass.hpp"
|
#include "oops/typeArrayKlass.hpp"
|
||||||
#include "oops/typeArrayOop.hpp"
|
#include "oops/typeArrayOop.hpp"
|
||||||
@ -535,7 +536,14 @@ oop ArchiveHeapWriter::load_oop_from_buffer(narrowOop* buffered_addr) {
|
|||||||
|
|
||||||
template <typename T> void ArchiveHeapWriter::relocate_field_in_buffer(T* field_addr_in_buffer, CHeapBitMap* oopmap) {
|
template <typename T> void ArchiveHeapWriter::relocate_field_in_buffer(T* field_addr_in_buffer, CHeapBitMap* oopmap) {
|
||||||
oop source_referent = load_source_oop_from_buffer<T>(field_addr_in_buffer);
|
oop source_referent = load_source_oop_from_buffer<T>(field_addr_in_buffer);
|
||||||
if (!CompressedOops::is_null(source_referent)) {
|
if (source_referent != nullptr) {
|
||||||
|
if (java_lang_Class::is_instance(source_referent)) {
|
||||||
|
// When the source object points to a "real" mirror, the buffered object should point
|
||||||
|
// to the "scratch" mirror, which has all unarchivable fields scrubbed (to be reinstated
|
||||||
|
// at run time).
|
||||||
|
source_referent = HeapShared::scratch_java_mirror(source_referent);
|
||||||
|
assert(source_referent != nullptr, "must be");
|
||||||
|
}
|
||||||
oop request_referent = source_obj_to_requested_obj(source_referent);
|
oop request_referent = source_obj_to_requested_obj(source_referent);
|
||||||
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
|
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
|
||||||
mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
|
mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
|
||||||
@ -731,7 +739,9 @@ void ArchiveHeapWriter::compute_ptrmap(ArchiveHeapInfo* heap_info) {
|
|||||||
|
|
||||||
Metadata** buffered_field_addr = requested_addr_to_buffered_addr(requested_field_addr);
|
Metadata** buffered_field_addr = requested_addr_to_buffered_addr(requested_field_addr);
|
||||||
Metadata* native_ptr = *buffered_field_addr;
|
Metadata* native_ptr = *buffered_field_addr;
|
||||||
assert(native_ptr != nullptr, "sanity");
|
guarantee(native_ptr != nullptr, "sanity");
|
||||||
|
guarantee(ArchiveBuilder::current()->has_been_buffered((address)native_ptr),
|
||||||
|
"Metadata %p should have been archived", native_ptr);
|
||||||
|
|
||||||
address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr);
|
address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr);
|
||||||
address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr);
|
address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr);
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "memory/metaspaceUtils.hpp"
|
#include "memory/metaspaceUtils.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "oops/compressedOops.inline.hpp"
|
#include "oops/compressedOops.inline.hpp"
|
||||||
|
#include "oops/klass.inline.hpp"
|
||||||
#include "runtime/arguments.hpp"
|
#include "runtime/arguments.hpp"
|
||||||
#include "utilities/bitMap.inline.hpp"
|
#include "utilities/bitMap.inline.hpp"
|
||||||
#include "utilities/debug.hpp"
|
#include "utilities/debug.hpp"
|
||||||
@ -370,6 +371,14 @@ void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ArchiveUtils::has_aot_initialized_mirror(InstanceKlass* src_ik) {
|
||||||
|
if (SystemDictionaryShared::is_excluded_class(src_ik)) {
|
||||||
|
assert(!ArchiveBuilder::current()->has_been_buffered(src_ik), "sanity");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ArchiveBuilder::current()->get_buffered_addr(src_ik)->has_aot_initialized_mirror();
|
||||||
|
}
|
||||||
|
|
||||||
size_t HeapRootSegments::size_in_bytes(size_t seg_idx) {
|
size_t HeapRootSegments::size_in_bytes(size_t seg_idx) {
|
||||||
assert(seg_idx < _count, "In range");
|
assert(seg_idx < _count, "In range");
|
||||||
return objArrayOopDesc::object_size(size_in_elems(seg_idx)) * HeapWordSize;
|
return objArrayOopDesc::object_size(size_in_elems(seg_idx)) * HeapWordSize;
|
||||||
|
@ -38,6 +38,9 @@ class BootstrapInfo;
|
|||||||
class ReservedSpace;
|
class ReservedSpace;
|
||||||
class VirtualSpace;
|
class VirtualSpace;
|
||||||
|
|
||||||
|
template<class E> class Array;
|
||||||
|
template<class E> class GrowableArray;
|
||||||
|
|
||||||
// ArchivePtrMarker is used to mark the location of pointers embedded in a CDS archive. E.g., when an
|
// ArchivePtrMarker is used to mark the location of pointers embedded in a CDS archive. E.g., when an
|
||||||
// InstanceKlass k is dumped, we mark the location of the k->_name pointer by effectively calling
|
// InstanceKlass k is dumped, we mark the location of the k->_name pointer by effectively calling
|
||||||
// mark_pointer(/*ptr_loc=*/&k->_name). It's required that (_prt_base <= ptr_loc < _ptr_end). _ptr_base is
|
// mark_pointer(/*ptr_loc=*/&k->_name). It's required that (_prt_base <= ptr_loc < _ptr_end). _ptr_base is
|
||||||
@ -253,6 +256,8 @@ class ArchiveUtils {
|
|||||||
public:
|
public:
|
||||||
static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF;
|
static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF;
|
||||||
static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN;
|
static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN;
|
||||||
|
static bool has_aot_initialized_mirror(InstanceKlass* src_ik);
|
||||||
|
template <typename T> static Array<T>* archive_array(GrowableArray<T>* tmp_array);
|
||||||
|
|
||||||
// offset must represent an object of type T in the mapped shared space. Return
|
// offset must represent an object of type T in the mapped shared space. Return
|
||||||
// a direct pointer to this object.
|
// a direct pointer to this object.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,7 +27,10 @@
|
|||||||
|
|
||||||
#include "cds/archiveUtils.hpp"
|
#include "cds/archiveUtils.hpp"
|
||||||
|
|
||||||
|
#include "cds/archiveBuilder.hpp"
|
||||||
|
#include "oops/array.hpp"
|
||||||
#include "utilities/bitMap.inline.hpp"
|
#include "utilities/bitMap.inline.hpp"
|
||||||
|
#include "utilities/growableArray.hpp"
|
||||||
|
|
||||||
inline bool SharedDataRelocator::do_bit(size_t offset) {
|
inline bool SharedDataRelocator::do_bit(size_t offset) {
|
||||||
address* p = _patch_base + offset;
|
address* p = _patch_base + offset;
|
||||||
@ -47,4 +50,19 @@ inline bool SharedDataRelocator::do_bit(size_t offset) {
|
|||||||
return true; // keep iterating
|
return true; // keep iterating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the address of an Array<T> that's allocated in the ArchiveBuilder "buffer" space.
|
||||||
|
template <typename T>
|
||||||
|
Array<T>* ArchiveUtils::archive_array(GrowableArray<T>* tmp_array) {
|
||||||
|
Array<T>* archived_array = ArchiveBuilder::new_ro_array<T>(tmp_array->length());
|
||||||
|
for (int i = 0; i < tmp_array->length(); i++) {
|
||||||
|
archived_array->at_put(i, tmp_array->at(i));
|
||||||
|
if (std::is_pointer<T>::value) {
|
||||||
|
ArchivePtrMarker::mark_pointer(archived_array->adr_at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return archived_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // SHARE_CDS_ARCHIVEUTILS_INLINE_HPP
|
#endif // SHARE_CDS_ARCHIVEUTILS_INLINE_HPP
|
||||||
|
@ -33,19 +33,27 @@
|
|||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "memory/universe.hpp"
|
#include "memory/universe.hpp"
|
||||||
#include "runtime/arguments.hpp"
|
#include "runtime/arguments.hpp"
|
||||||
|
#include "runtime/globals_extension.hpp"
|
||||||
#include "runtime/java.hpp"
|
#include "runtime/java.hpp"
|
||||||
|
#include "runtime/vmThread.hpp"
|
||||||
#include "utilities/defaultStream.hpp"
|
#include "utilities/defaultStream.hpp"
|
||||||
|
#include "utilities/formatBuffer.hpp"
|
||||||
|
|
||||||
bool CDSConfig::_is_dumping_static_archive = false;
|
bool CDSConfig::_is_dumping_static_archive = false;
|
||||||
bool CDSConfig::_is_dumping_dynamic_archive = false;
|
bool CDSConfig::_is_dumping_dynamic_archive = false;
|
||||||
bool CDSConfig::_is_using_optimized_module_handling = true;
|
bool CDSConfig::_is_using_optimized_module_handling = true;
|
||||||
bool CDSConfig::_is_dumping_full_module_graph = true;
|
bool CDSConfig::_is_dumping_full_module_graph = true;
|
||||||
bool CDSConfig::_is_using_full_module_graph = true;
|
bool CDSConfig::_is_using_full_module_graph = true;
|
||||||
|
bool CDSConfig::_has_aot_linked_classes = false;
|
||||||
|
bool CDSConfig::_has_archived_invokedynamic = false;
|
||||||
|
bool CDSConfig::_old_cds_flags_used = false;
|
||||||
|
|
||||||
char* CDSConfig::_default_archive_path = nullptr;
|
char* CDSConfig::_default_archive_path = nullptr;
|
||||||
char* CDSConfig::_static_archive_path = nullptr;
|
char* CDSConfig::_static_archive_path = nullptr;
|
||||||
char* CDSConfig::_dynamic_archive_path = nullptr;
|
char* CDSConfig::_dynamic_archive_path = nullptr;
|
||||||
|
|
||||||
|
JavaThread* CDSConfig::_dumper_thread = nullptr;
|
||||||
|
|
||||||
int CDSConfig::get_status() {
|
int CDSConfig::get_status() {
|
||||||
assert(Universe::is_fully_initialized(), "status is finalized only after Universe is initialized");
|
assert(Universe::is_fully_initialized(), "status is finalized only after Universe is initialized");
|
||||||
return (is_dumping_archive() ? IS_DUMPING_ARCHIVE : 0) |
|
return (is_dumping_archive() ? IS_DUMPING_ARCHIVE : 0) |
|
||||||
@ -54,7 +62,6 @@ int CDSConfig::get_status() {
|
|||||||
(is_using_archive() ? IS_USING_ARCHIVE : 0);
|
(is_using_archive() ? IS_USING_ARCHIVE : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CDSConfig::initialize() {
|
void CDSConfig::initialize() {
|
||||||
if (is_dumping_static_archive()) {
|
if (is_dumping_static_archive()) {
|
||||||
if (RequireSharedSpaces) {
|
if (RequireSharedSpaces) {
|
||||||
@ -334,7 +341,95 @@ bool CDSConfig::has_unsupported_runtime_module_options() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CHECK_ALIAS(f) check_flag_alias(FLAG_IS_DEFAULT(f), #f)
|
||||||
|
|
||||||
|
void CDSConfig::check_flag_alias(bool alias_is_default, const char* alias_name) {
|
||||||
|
if (_old_cds_flags_used && !alias_is_default) {
|
||||||
|
vm_exit_during_initialization(err_msg("Option %s cannot be used at the same time with "
|
||||||
|
"-Xshare:on, -Xshare:auto, -Xshare:off, -Xshare:dump, "
|
||||||
|
"DumpLoadedClassList, SharedClassListFile, or SharedArchiveFile",
|
||||||
|
alias_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDSConfig::check_flag_aliases() {
|
||||||
|
if (!FLAG_IS_DEFAULT(DumpLoadedClassList) ||
|
||||||
|
!FLAG_IS_DEFAULT(SharedClassListFile) ||
|
||||||
|
!FLAG_IS_DEFAULT(SharedArchiveFile)) {
|
||||||
|
_old_cds_flags_used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_ALIAS(AOTCache);
|
||||||
|
CHECK_ALIAS(AOTConfiguration);
|
||||||
|
CHECK_ALIAS(AOTMode);
|
||||||
|
|
||||||
|
if (FLAG_IS_DEFAULT(AOTCache) && FLAG_IS_DEFAULT(AOTConfiguration) && FLAG_IS_DEFAULT(AOTMode)) {
|
||||||
|
// Aliases not used.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAG_IS_DEFAULT(AOTMode) || strcmp(AOTMode, "auto") == 0 || strcmp(AOTMode, "on") == 0) {
|
||||||
|
if (!FLAG_IS_DEFAULT(AOTConfiguration)) {
|
||||||
|
vm_exit_during_initialization("AOTConfiguration can only be used with -XX:AOTMode=record or -XX:AOTMode=create");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FLAG_IS_DEFAULT(AOTCache)) {
|
||||||
|
assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked");
|
||||||
|
FLAG_SET_ERGO(SharedArchiveFile, AOTCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
UseSharedSpaces = true;
|
||||||
|
if (FLAG_IS_DEFAULT(AOTMode) || (strcmp(AOTMode, "auto") == 0)) {
|
||||||
|
RequireSharedSpaces = false;
|
||||||
|
} else {
|
||||||
|
assert(strcmp(AOTMode, "on") == 0, "already checked");
|
||||||
|
RequireSharedSpaces = true;
|
||||||
|
}
|
||||||
|
} else if (strcmp(AOTMode, "off") == 0) {
|
||||||
|
UseSharedSpaces = false;
|
||||||
|
RequireSharedSpaces = false;
|
||||||
|
} else {
|
||||||
|
// AOTMode is record or create
|
||||||
|
if (FLAG_IS_DEFAULT(AOTConfiguration)) {
|
||||||
|
vm_exit_during_initialization(err_msg("-XX:AOTMode=%s cannot be used without setting AOTConfiguration", AOTMode));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(AOTMode, "record") == 0) {
|
||||||
|
if (!FLAG_IS_DEFAULT(AOTCache)) {
|
||||||
|
vm_exit_during_initialization("AOTCache must not be specified when using -XX:AOTMode=record");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(FLAG_IS_DEFAULT(DumpLoadedClassList), "already checked");
|
||||||
|
FLAG_SET_ERGO(DumpLoadedClassList, AOTConfiguration);
|
||||||
|
UseSharedSpaces = false;
|
||||||
|
RequireSharedSpaces = false;
|
||||||
|
} else {
|
||||||
|
assert(strcmp(AOTMode, "create") == 0, "checked by AOTModeConstraintFunc");
|
||||||
|
if (FLAG_IS_DEFAULT(AOTCache)) {
|
||||||
|
vm_exit_during_initialization("AOTCache must be specified when using -XX:AOTMode=create");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(FLAG_IS_DEFAULT(SharedClassListFile), "already checked");
|
||||||
|
FLAG_SET_ERGO(SharedClassListFile, AOTConfiguration);
|
||||||
|
assert(FLAG_IS_DEFAULT(SharedArchiveFile), "already checked");
|
||||||
|
FLAG_SET_ERGO(SharedArchiveFile, AOTCache);
|
||||||
|
|
||||||
|
CDSConfig::enable_dumping_static_archive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line) {
|
bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_flag_cmd_line) {
|
||||||
|
check_flag_aliases();
|
||||||
|
|
||||||
|
if (AOTClassLinking) {
|
||||||
|
// If AOTClassLinking is specified, enable all AOT optimizations by default.
|
||||||
|
FLAG_SET_ERGO_IF_DEFAULT(AOTInvokeDynamicLinking, true);
|
||||||
|
} else {
|
||||||
|
// AOTInvokeDynamicLinking depends on AOTClassLinking.
|
||||||
|
FLAG_SET_ERGO(AOTInvokeDynamicLinking, false);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_dumping_static_archive()) {
|
if (is_dumping_static_archive()) {
|
||||||
if (!mode_flag_cmd_line) {
|
if (!mode_flag_cmd_line) {
|
||||||
// By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive.
|
// By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive.
|
||||||
@ -353,6 +448,9 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
|
|||||||
// run to another which resulting in non-determinstic CDS archives.
|
// run to another which resulting in non-determinstic CDS archives.
|
||||||
// Disable UseStringDeduplication while dumping CDS archive.
|
// Disable UseStringDeduplication while dumping CDS archive.
|
||||||
UseStringDeduplication = false;
|
UseStringDeduplication = false;
|
||||||
|
|
||||||
|
// Don't use SoftReferences so that objects used by java.lang.invoke tables can be archived.
|
||||||
|
Arguments::PropertyList_add(new SystemProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "false", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecordDynamicDumpInfo is not compatible with ArchiveClassesAtExit
|
// RecordDynamicDumpInfo is not compatible with ArchiveClassesAtExit
|
||||||
@ -397,6 +495,11 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::allow_only_single_java_thread() {
|
||||||
|
// See comments in JVM_StartThread()
|
||||||
|
return is_dumping_static_archive();
|
||||||
|
}
|
||||||
|
|
||||||
bool CDSConfig::is_using_archive() {
|
bool CDSConfig::is_using_archive() {
|
||||||
return UseSharedSpaces;
|
return UseSharedSpaces;
|
||||||
}
|
}
|
||||||
@ -411,12 +514,32 @@ void CDSConfig::stop_using_optimized_module_handling() {
|
|||||||
_is_using_full_module_graph = false; // This requires is_using_optimized_module_handling()
|
_is_using_full_module_graph = false; // This requires is_using_optimized_module_handling()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CDSConfig::DumperThreadMark::DumperThreadMark(JavaThread* current) {
|
||||||
|
assert(_dumper_thread == nullptr, "sanity");
|
||||||
|
_dumper_thread = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDSConfig::DumperThreadMark::~DumperThreadMark() {
|
||||||
|
assert(_dumper_thread != nullptr, "sanity");
|
||||||
|
_dumper_thread = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::current_thread_is_vm_or_dumper() {
|
||||||
|
Thread* t = Thread::current();
|
||||||
|
return t != nullptr && (t->is_VM_thread() || t == _dumper_thread);
|
||||||
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS_JAVA_HEAP
|
#if INCLUDE_CDS_JAVA_HEAP
|
||||||
bool CDSConfig::is_dumping_heap() {
|
bool CDSConfig::is_dumping_heap() {
|
||||||
// heap dump is not supported in dynamic dump
|
// heap dump is not supported in dynamic dump
|
||||||
return is_dumping_static_archive() && HeapShared::can_write();
|
return is_dumping_static_archive() && HeapShared::can_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::is_loading_heap() {
|
||||||
|
return ArchiveHeapLoader::is_in_use();
|
||||||
|
}
|
||||||
|
|
||||||
bool CDSConfig::is_using_full_module_graph() {
|
bool CDSConfig::is_using_full_module_graph() {
|
||||||
if (ClassLoaderDataShared::is_full_module_graph_loaded()) {
|
if (ClassLoaderDataShared::is_full_module_graph_loaded()) {
|
||||||
return true;
|
return true;
|
||||||
@ -455,4 +578,39 @@ void CDSConfig::stop_using_full_module_graph(const char* reason) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::is_dumping_aot_linked_classes() {
|
||||||
|
if (is_dumping_dynamic_archive()) {
|
||||||
|
return is_using_full_module_graph() && AOTClassLinking;
|
||||||
|
} else if (is_dumping_static_archive()) {
|
||||||
|
return is_dumping_full_module_graph() && AOTClassLinking;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::is_using_aot_linked_classes() {
|
||||||
|
// Make sure we have the exact same module graph as in the assembly phase, or else
|
||||||
|
// some aot-linked classes may not be visible so cannot be loaded.
|
||||||
|
return is_using_full_module_graph() && _has_aot_linked_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDSConfig::set_has_aot_linked_classes(bool has_aot_linked_classes) {
|
||||||
|
_has_aot_linked_classes |= has_aot_linked_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::is_initing_classes_at_dump_time() {
|
||||||
|
return is_dumping_heap() && is_dumping_aot_linked_classes();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::is_dumping_invokedynamic() {
|
||||||
|
// Requires is_dumping_aot_linked_classes(). Otherwise the classes of some archived heap
|
||||||
|
// objects used by the archive indy callsites may be replaced at runtime.
|
||||||
|
return AOTInvokeDynamicLinking && is_dumping_aot_linked_classes() && is_dumping_heap();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDSConfig::is_loading_invokedynamic() {
|
||||||
|
return UseSharedSpaces && is_using_full_module_graph() && _has_archived_invokedynamic;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
#include "utilities/globalDefinitions.hpp"
|
#include "utilities/globalDefinitions.hpp"
|
||||||
#include "utilities/macros.hpp"
|
#include "utilities/macros.hpp"
|
||||||
|
|
||||||
|
class JavaThread;
|
||||||
|
|
||||||
class CDSConfig : public AllStatic {
|
class CDSConfig : public AllStatic {
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
static bool _is_dumping_static_archive;
|
static bool _is_dumping_static_archive;
|
||||||
@ -36,10 +38,16 @@ class CDSConfig : public AllStatic {
|
|||||||
static bool _is_using_optimized_module_handling;
|
static bool _is_using_optimized_module_handling;
|
||||||
static bool _is_dumping_full_module_graph;
|
static bool _is_dumping_full_module_graph;
|
||||||
static bool _is_using_full_module_graph;
|
static bool _is_using_full_module_graph;
|
||||||
|
static bool _has_aot_linked_classes;
|
||||||
|
static bool _has_archived_invokedynamic;
|
||||||
|
|
||||||
static char* _default_archive_path;
|
static char* _default_archive_path;
|
||||||
static char* _static_archive_path;
|
static char* _static_archive_path;
|
||||||
static char* _dynamic_archive_path;
|
static char* _dynamic_archive_path;
|
||||||
|
|
||||||
|
static bool _old_cds_flags_used;
|
||||||
|
|
||||||
|
static JavaThread* _dumper_thread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void extract_shared_archive_paths(const char* archive_path,
|
static void extract_shared_archive_paths(const char* archive_path,
|
||||||
@ -47,6 +55,9 @@ class CDSConfig : public AllStatic {
|
|||||||
char** top_archive_path);
|
char** top_archive_path);
|
||||||
static void init_shared_archive_paths();
|
static void init_shared_archive_paths();
|
||||||
|
|
||||||
|
static void check_flag_alias(bool alias_is_default, const char* alias_name);
|
||||||
|
static void check_flag_aliases();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Used by jdk.internal.misc.CDS.getCDSConfigStatus();
|
// Used by jdk.internal.misc.CDS.getCDSConfigStatus();
|
||||||
static const int IS_DUMPING_ARCHIVE = 1 << 0;
|
static const int IS_DUMPING_ARCHIVE = 1 << 0;
|
||||||
@ -57,6 +68,8 @@ public:
|
|||||||
|
|
||||||
// Initialization and command-line checking
|
// Initialization and command-line checking
|
||||||
static void initialize() NOT_CDS_RETURN;
|
static void initialize() NOT_CDS_RETURN;
|
||||||
|
static void set_old_cds_flags_used() { CDS_ONLY(_old_cds_flags_used = true); }
|
||||||
|
static bool old_cds_flags_used() { return CDS_ONLY(_old_cds_flags_used) NOT_CDS(false); }
|
||||||
static void check_internal_module_property(const char* key, const char* value) NOT_CDS_RETURN;
|
static void check_internal_module_property(const char* key, const char* value) NOT_CDS_RETURN;
|
||||||
static void check_incompatible_property(const char* key, const char* value) NOT_CDS_RETURN;
|
static void check_incompatible_property(const char* key, const char* value) NOT_CDS_RETURN;
|
||||||
static void check_unsupported_dumping_module_options() NOT_CDS_RETURN;
|
static void check_unsupported_dumping_module_options() NOT_CDS_RETURN;
|
||||||
@ -79,12 +92,19 @@ public:
|
|||||||
static void enable_dumping_dynamic_archive() { CDS_ONLY(_is_dumping_dynamic_archive = true); }
|
static void enable_dumping_dynamic_archive() { CDS_ONLY(_is_dumping_dynamic_archive = true); }
|
||||||
static void disable_dumping_dynamic_archive() { CDS_ONLY(_is_dumping_dynamic_archive = false); }
|
static void disable_dumping_dynamic_archive() { CDS_ONLY(_is_dumping_dynamic_archive = false); }
|
||||||
|
|
||||||
|
// Misc CDS features
|
||||||
|
static bool allow_only_single_java_thread() NOT_CDS_RETURN_(false);
|
||||||
|
|
||||||
// optimized_module_handling -- can we skip some expensive operations related to modules?
|
// optimized_module_handling -- can we skip some expensive operations related to modules?
|
||||||
static bool is_using_optimized_module_handling() { return CDS_ONLY(_is_using_optimized_module_handling) NOT_CDS(false); }
|
static bool is_using_optimized_module_handling() { return CDS_ONLY(_is_using_optimized_module_handling) NOT_CDS(false); }
|
||||||
static void stop_using_optimized_module_handling() NOT_CDS_RETURN;
|
static void stop_using_optimized_module_handling() NOT_CDS_RETURN;
|
||||||
|
|
||||||
static bool is_logging_lambda_form_invokers() NOT_CDS_RETURN_(false);
|
static bool is_logging_lambda_form_invokers() NOT_CDS_RETURN_(false);
|
||||||
|
|
||||||
|
static bool is_dumping_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static bool is_using_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static void set_has_aot_linked_classes(bool has_aot_linked_classes) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
|
||||||
// archive_path
|
// archive_path
|
||||||
|
|
||||||
// Points to the classes.jsa in $JAVA_HOME
|
// Points to the classes.jsa in $JAVA_HOME
|
||||||
@ -96,13 +116,34 @@ public:
|
|||||||
|
|
||||||
// --- Archived java objects
|
// --- Archived java objects
|
||||||
|
|
||||||
static bool is_dumping_heap() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
static bool is_dumping_heap() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static bool is_loading_heap() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static bool is_initing_classes_at_dump_time() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
|
||||||
|
static bool is_dumping_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static bool is_loading_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static void set_has_archived_invokedynamic() { CDS_JAVA_HEAP_ONLY(_has_archived_invokedynamic = true); }
|
||||||
|
|
||||||
// full_module_graph (requires optimized_module_handling)
|
// full_module_graph (requires optimized_module_handling)
|
||||||
static bool is_dumping_full_module_graph() { return CDS_ONLY(_is_dumping_full_module_graph) NOT_CDS(false); }
|
static bool is_dumping_full_module_graph() { return CDS_ONLY(_is_dumping_full_module_graph) NOT_CDS(false); }
|
||||||
static bool is_using_full_module_graph() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
static bool is_using_full_module_graph() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
static void stop_dumping_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
|
static void stop_dumping_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
static void stop_using_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
|
static void stop_using_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
|
||||||
|
|
||||||
|
// Some CDS functions assume that they are called only within a single-threaded context. I.e.,
|
||||||
|
// they are called from:
|
||||||
|
// - The VM thread (e.g., inside VM_PopulateDumpSharedSpace)
|
||||||
|
// - The thread that performs prepatory steps before switching to the VM thread
|
||||||
|
// Since these two threads never execute concurrently, we can avoid using locks in these CDS
|
||||||
|
// function. For safety, these functions should assert with CDSConfig::current_thread_is_vm_or_dumper().
|
||||||
|
class DumperThreadMark {
|
||||||
|
public:
|
||||||
|
DumperThreadMark(JavaThread* current);
|
||||||
|
~DumperThreadMark();
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool current_thread_is_vm_or_dumper() NOT_CDS_RETURN_(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_CDS_CDSCONFIG_HPP
|
#endif // SHARE_CDS_CDSCONFIG_HPP
|
||||||
|
@ -39,7 +39,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) {
|
|||||||
Klass* k = orig_obj->klass();
|
Klass* k = orig_obj->klass();
|
||||||
Klass* buffered_k = ArchiveBuilder::get_buffered_klass(k);
|
Klass* buffered_k = ArchiveBuilder::get_buffered_klass(k);
|
||||||
return k->is_instance_klass() &&
|
return k->is_instance_klass() &&
|
||||||
InstanceKlass::cast(k)->java_super() == vmClasses::Enum_klass();
|
InstanceKlass::cast(k)->is_enum_subclass();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Handling of Enum objects
|
// -- Handling of Enum objects
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,10 +23,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassInitializer.hpp"
|
||||||
#include "cds/archiveBuilder.hpp"
|
#include "cds/archiveBuilder.hpp"
|
||||||
#include "cds/cdsHeapVerifier.hpp"
|
#include "cds/cdsHeapVerifier.hpp"
|
||||||
#include "classfile/classLoaderDataGraph.hpp"
|
#include "classfile/classLoaderDataGraph.hpp"
|
||||||
#include "classfile/javaClasses.inline.hpp"
|
#include "classfile/javaClasses.inline.hpp"
|
||||||
|
#include "classfile/moduleEntry.hpp"
|
||||||
|
#include "classfile/systemDictionaryShared.hpp"
|
||||||
|
#include "classfile/vmSymbols.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "logging/logStream.hpp"
|
#include "logging/logStream.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
@ -38,12 +42,13 @@
|
|||||||
#if INCLUDE_CDS_JAVA_HEAP
|
#if INCLUDE_CDS_JAVA_HEAP
|
||||||
|
|
||||||
// CDSHeapVerifier is used to check for problems where an archived object references a
|
// 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,
|
// static field that may be get a different value at runtime. In the following example,
|
||||||
// Foo.get.test()
|
// Foo.get.test()
|
||||||
// correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled.
|
// correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled,
|
||||||
|
// because the archived archivedFoo.bar value is different than Bar.bar.
|
||||||
//
|
//
|
||||||
// class Foo {
|
// class Foo {
|
||||||
// final Foo archivedFoo; // this field is archived by CDS
|
// static final Foo archivedFoo; // this field is archived by CDS
|
||||||
// Bar bar;
|
// Bar bar;
|
||||||
// static {
|
// static {
|
||||||
// CDS.initializeFromArchive(Foo.class);
|
// CDS.initializeFromArchive(Foo.class);
|
||||||
@ -68,8 +73,8 @@
|
|||||||
// [2] CDSHeapVerifier::do_entry() checks all the archived objects. None of them
|
// [2] CDSHeapVerifier::do_entry() checks all the archived objects. None of them
|
||||||
// should be in [1]
|
// should be in [1]
|
||||||
//
|
//
|
||||||
// However, it's legal for *some* static fields to be references. This leads to the
|
// However, it's legal for *some* static fields to be referenced. The reasons are explained
|
||||||
// table of ADD_EXCL below.
|
// in the table of ADD_EXCL below.
|
||||||
//
|
//
|
||||||
// [A] In most of the cases, the module bootstrap code will update the static field
|
// [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.,
|
// to point to part of the archived module graph. E.g.,
|
||||||
@ -123,6 +128,12 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
|
|||||||
ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E
|
ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E
|
||||||
"ZERO_INT"); // E
|
"ZERO_INT"); // E
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
ADD_EXCL("java/lang/invoke/InvokerBytecodeGenerator", "MEMBERNAME_FACTORY", // D
|
||||||
|
"CD_Object_array", // E same as <...>ConstantUtils.CD_Object_array::CD_Object
|
||||||
|
"INVOKER_SUPER_DESC"); // E same as java.lang.constant.ConstantDescs::CD_Object
|
||||||
|
}
|
||||||
|
|
||||||
# undef ADD_EXCL
|
# undef ADD_EXCL
|
||||||
|
|
||||||
ClassLoaderDataGraph::classes_do(this);
|
ClassLoaderDataGraph::classes_do(this);
|
||||||
@ -130,15 +141,16 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
|
|||||||
|
|
||||||
CDSHeapVerifier::~CDSHeapVerifier() {
|
CDSHeapVerifier::~CDSHeapVerifier() {
|
||||||
if (_problems > 0) {
|
if (_problems > 0) {
|
||||||
log_warning(cds, heap)("Scanned %d objects. Found %d case(s) where "
|
log_error(cds, heap)("Scanned %d objects. Found %d case(s) where "
|
||||||
"an object points to a static field that may be "
|
"an object points to a static field that "
|
||||||
"reinitialized at runtime.", _archived_objs, _problems);
|
"may hold a different value at runtime.", _archived_objs, _problems);
|
||||||
|
MetaspaceShared::unrecoverable_writing_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CDSHeapVerifier::CheckStaticFields : public FieldClosure {
|
class CDSHeapVerifier::CheckStaticFields : public FieldClosure {
|
||||||
CDSHeapVerifier* _verifier;
|
CDSHeapVerifier* _verifier;
|
||||||
InstanceKlass* _ik;
|
InstanceKlass* _ik; // The class whose static fields are being checked.
|
||||||
const char** _exclusions;
|
const char** _exclusions;
|
||||||
public:
|
public:
|
||||||
CheckStaticFields(CDSHeapVerifier* verifier, InstanceKlass* ik)
|
CheckStaticFields(CDSHeapVerifier* verifier, InstanceKlass* ik)
|
||||||
@ -151,9 +163,14 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fd->signature()->equals("Ljdk/internal/access/JavaLangAccess;")) {
|
||||||
|
// A few classes have static fields that point to SharedSecrets.getJavaLangAccess().
|
||||||
|
// This object carries no state and we can create a new one in the production run.
|
||||||
|
return;
|
||||||
|
}
|
||||||
oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
|
oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
|
||||||
if (static_obj_field != nullptr) {
|
if (static_obj_field != nullptr) {
|
||||||
Klass* klass = static_obj_field->klass();
|
Klass* field_type = static_obj_field->klass();
|
||||||
if (_exclusions != nullptr) {
|
if (_exclusions != nullptr) {
|
||||||
for (const char** p = _exclusions; *p != nullptr; p++) {
|
for (const char** p = _exclusions; *p != nullptr; p++) {
|
||||||
if (fd->name()->equals(*p)) {
|
if (fd->name()->equals(*p)) {
|
||||||
@ -173,11 +190,35 @@ public:
|
|||||||
// This field points to an archived mirror.
|
// This field points to an archived mirror.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (klass->has_archived_enum_objs()) {
|
|
||||||
// This klass is a subclass of java.lang.Enum. If any instance of this klass
|
if (field_type->is_instance_klass()) {
|
||||||
// has been archived, we will archive all static fields of this klass.
|
InstanceKlass* field_ik = InstanceKlass::cast(field_type);
|
||||||
// See HeapShared::initialize_enum_klass().
|
if (field_ik->is_enum_subclass()) {
|
||||||
return;
|
if (field_ik->has_archived_enum_objs() || ArchiveUtils::has_aot_initialized_mirror(field_ik)) {
|
||||||
|
// This field is an Enum. If any instance of this Enum has been archived, we will archive
|
||||||
|
// all static fields of this Enum as well.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_ik->is_hidden() && ArchiveUtils::has_aot_initialized_mirror(field_ik)) {
|
||||||
|
// We have a static field in a core-library class that points to a method reference, which
|
||||||
|
// are safe to archive.
|
||||||
|
guarantee(_ik->module()->name() == vmSymbols::java_base(), "sanity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_ik == vmClasses::MethodType_klass()) {
|
||||||
|
// The identity of MethodTypes are preserved between assembly phase and production runs
|
||||||
|
// (by MethodType::AOTHolder::archivedMethodTypes). No need to check.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_ik == vmClasses::internal_Unsafe_klass() && ArchiveUtils::has_aot_initialized_mirror(field_ik)) {
|
||||||
|
// There's only a single instance of jdk/internal/misc/Unsafe, so all references will
|
||||||
|
// be pointing to this singleton, which has been archived.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This field *may* be initialized to a different value at runtime. Remember it
|
// This field *may* be initialized to a different value at runtime. Remember it
|
||||||
@ -188,7 +229,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Remember all the static object fields of every class that are currently
|
// Remember all the static object fields of every class that are currently
|
||||||
// loaded.
|
// loaded. Later, we will check if any archived objects reference one of
|
||||||
|
// these fields.
|
||||||
void CDSHeapVerifier::do_klass(Klass* k) {
|
void CDSHeapVerifier::do_klass(Klass* k) {
|
||||||
if (k->is_instance_klass()) {
|
if (k->is_instance_klass()) {
|
||||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||||
@ -200,6 +242,12 @@ void CDSHeapVerifier::do_klass(Klass* k) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ArchiveUtils::has_aot_initialized_mirror(ik)) {
|
||||||
|
// ik's <clinit> won't be executed at runtime, the static fields in
|
||||||
|
// ik will carry their values to runtime.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CheckStaticFields csf(this, ik);
|
CheckStaticFields csf(this, ik);
|
||||||
ik->do_local_static_fields(&csf);
|
ik->do_local_static_fields(&csf);
|
||||||
}
|
}
|
||||||
@ -221,10 +269,15 @@ inline bool CDSHeapVerifier::do_entry(oop& orig_obj, HeapShared::CachedOopInfo&
|
|||||||
// should be flagged by CDSHeapVerifier.
|
// should be flagged by CDSHeapVerifier.
|
||||||
return true; /* keep on iterating */
|
return true; /* keep on iterating */
|
||||||
}
|
}
|
||||||
|
if (info->_holder->is_hidden()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
|
char* class_name = info->_holder->name()->as_C_string();
|
||||||
|
char* field_name = info->_name->as_C_string();
|
||||||
LogStream ls(Log(cds, heap)::warning());
|
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("Archive heap points to a static field that may hold a different value at runtime:");
|
||||||
ls.print_cr("Field: %s::%s", info->_holder->name()->as_C_string(), info->_name->as_C_string());
|
ls.print_cr("Field: %s::%s", class_name, field_name);
|
||||||
ls.print("Value: ");
|
ls.print("Value: ");
|
||||||
orig_obj->print_on(&ls);
|
orig_obj->print_on(&ls);
|
||||||
ls.print_cr("--- trace begin ---");
|
ls.print_cr("--- trace begin ---");
|
||||||
@ -266,6 +319,29 @@ void CDSHeapVerifier::trace_to_root(outputStream* st, oop orig_obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* static_field_name(oop mirror, oop field) {
|
||||||
|
Klass* k = java_lang_Class::as_Klass(mirror);
|
||||||
|
if (k->is_instance_klass()) {
|
||||||
|
for (JavaFieldStream fs(InstanceKlass::cast(k)); !fs.done(); fs.next()) {
|
||||||
|
if (fs.access_flags().is_static()) {
|
||||||
|
fieldDescriptor& fd = fs.field_descriptor();
|
||||||
|
switch (fd.field_type()) {
|
||||||
|
case T_OBJECT:
|
||||||
|
case T_ARRAY:
|
||||||
|
if (mirror->obj_field(fd.offset()) == field) {
|
||||||
|
return fs.name()->as_C_string();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<unknown>";
|
||||||
|
}
|
||||||
|
|
||||||
int CDSHeapVerifier::trace_to_root(outputStream* st, oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* info) {
|
int CDSHeapVerifier::trace_to_root(outputStream* st, oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* info) {
|
||||||
int level = 0;
|
int level = 0;
|
||||||
if (info->orig_referrer() != nullptr) {
|
if (info->orig_referrer() != nullptr) {
|
||||||
@ -280,6 +356,9 @@ int CDSHeapVerifier::trace_to_root(outputStream* st, oop orig_obj, oop orig_fiel
|
|||||||
st->print("[%2d] ", level);
|
st->print("[%2d] ", level);
|
||||||
orig_obj->print_address_on(st);
|
orig_obj->print_address_on(st);
|
||||||
st->print(" %s", k->internal_name());
|
st->print(" %s", k->internal_name());
|
||||||
|
if (java_lang_Class::is_instance(orig_obj)) {
|
||||||
|
st->print(" (%s::%s)", java_lang_Class::as_Klass(orig_obj)->external_name(), static_field_name(orig_obj, orig_field));
|
||||||
|
}
|
||||||
if (orig_field != nullptr) {
|
if (orig_field != nullptr) {
|
||||||
if (k->is_instance_klass()) {
|
if (k->is_instance_klass()) {
|
||||||
TraceFields clo(orig_obj, orig_field, st);
|
TraceFields clo(orig_obj, orig_field, st);
|
||||||
|
@ -94,6 +94,30 @@
|
|||||||
"(2) always map at preferred address, and if unsuccessful, " \
|
"(2) always map at preferred address, and if unsuccessful, " \
|
||||||
"do not map the archive") \
|
"do not map the archive") \
|
||||||
range(0, 2) \
|
range(0, 2) \
|
||||||
|
\
|
||||||
|
/*========== New "AOT" flags =========================================*/ \
|
||||||
|
/* The following 3 flags are aliases of -Xshare:dump, */ \
|
||||||
|
/* -XX:SharedArchiveFile=..., etc. See CDSConfig::check_flag_aliases()*/ \
|
||||||
|
\
|
||||||
|
product(ccstr, AOTMode, nullptr, \
|
||||||
|
"Specifies how AOTCache should be created or used. Valid values " \
|
||||||
|
"are: off, record, create, auto, on; the default is auto") \
|
||||||
|
constraint(AOTModeConstraintFunc, AtParse) \
|
||||||
|
\
|
||||||
|
product(ccstr, AOTConfiguration, nullptr, \
|
||||||
|
"Configuration information used by CreateAOTCache") \
|
||||||
|
\
|
||||||
|
product(ccstr, AOTCache, nullptr, \
|
||||||
|
"Cache for improving start up and warm up") \
|
||||||
|
\
|
||||||
|
product(bool, AOTInvokeDynamicLinking, false, DIAGNOSTIC, \
|
||||||
|
"AOT-link JVM_CONSTANT_InvokeDynamic entries in cached " \
|
||||||
|
"ConstantPools") \
|
||||||
|
\
|
||||||
|
product(bool, AOTClassLinking, false, \
|
||||||
|
"Load/link all archived classes for the boot/platform/app " \
|
||||||
|
"loaders before application main") \
|
||||||
|
|
||||||
// end of CDS_FLAGS
|
// end of CDS_FLAGS
|
||||||
|
|
||||||
DECLARE_FLAGS(CDS_FLAGS)
|
DECLARE_FLAGS(CDS_FLAGS)
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotConstantPoolResolver.hpp"
|
||||||
#include "cds/archiveUtils.hpp"
|
#include "cds/archiveUtils.hpp"
|
||||||
#include "cds/classListParser.hpp"
|
#include "cds/classListParser.hpp"
|
||||||
#include "cds/classPrelinker.hpp"
|
|
||||||
#include "cds/lambdaFormInvokers.hpp"
|
#include "cds/lambdaFormInvokers.hpp"
|
||||||
#include "cds/metaspaceShared.hpp"
|
#include "cds/metaspaceShared.hpp"
|
||||||
#include "cds/unregisteredClasses.hpp"
|
#include "cds/unregisteredClasses.hpp"
|
||||||
@ -46,6 +46,7 @@
|
|||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "oops/constantPool.inline.hpp"
|
#include "oops/constantPool.inline.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
|
#include "runtime/globals_extension.hpp"
|
||||||
#include "runtime/handles.inline.hpp"
|
#include "runtime/handles.inline.hpp"
|
||||||
#include "runtime/java.hpp"
|
#include "runtime/java.hpp"
|
||||||
#include "runtime/javaCalls.hpp"
|
#include "runtime/javaCalls.hpp"
|
||||||
@ -69,9 +70,13 @@ ClassListParser::ClassListParser(const char* file, ParseMode parse_mode) :
|
|||||||
log_info(cds)("Parsing %s%s", file,
|
log_info(cds)("Parsing %s%s", file,
|
||||||
parse_lambda_forms_invokers_only() ? " (lambda form invokers only)" : "");
|
parse_lambda_forms_invokers_only() ? " (lambda form invokers only)" : "");
|
||||||
if (!_file_input.is_open()) {
|
if (!_file_input.is_open()) {
|
||||||
char errmsg[JVM_MAXPATHLEN];
|
char reason[JVM_MAXPATHLEN];
|
||||||
os::lasterror(errmsg, JVM_MAXPATHLEN);
|
os::lasterror(reason, JVM_MAXPATHLEN);
|
||||||
vm_exit_during_initialization("Loading classlist failed", errmsg);
|
vm_exit_during_initialization(err_msg("Loading %s %s failed",
|
||||||
|
FLAG_IS_DEFAULT(AOTConfiguration) ?
|
||||||
|
"classlist" : "AOTConfiguration file",
|
||||||
|
file),
|
||||||
|
reason);
|
||||||
}
|
}
|
||||||
_token = _line = nullptr;
|
_token = _line = nullptr;
|
||||||
_interfaces = new (mtClass) GrowableArray<int>(10, mtClass);
|
_interfaces = new (mtClass) GrowableArray<int>(10, mtClass);
|
||||||
@ -594,6 +599,18 @@ void ClassListParser::resolve_indy(JavaThread* current, Symbol* class_name_symbo
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClassListParser::resolve_indy_impl(Symbol* class_name_symbol, TRAPS) {
|
void ClassListParser::resolve_indy_impl(Symbol* class_name_symbol, TRAPS) {
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
// The CP entry for the invokedynamic instruction will be resolved.
|
||||||
|
// No need to do the following.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an older CDS optimization:
|
||||||
|
// We store a pre-generated version of the lambda proxy class in the AOT cache,
|
||||||
|
// which will be loaded via JVM_LookupLambdaProxyClassFromArchive().
|
||||||
|
// This eliminate dynamic class generation of the proxy class, but we still need to
|
||||||
|
// resolve the CP entry for the invokedynamic instruction, which may result in
|
||||||
|
// generation of LambdaForm classes.
|
||||||
Handle class_loader(THREAD, SystemDictionary::java_system_loader());
|
Handle class_loader(THREAD, SystemDictionary::java_system_loader());
|
||||||
Handle protection_domain;
|
Handle protection_domain;
|
||||||
Klass* klass = SystemDictionary::resolve_or_fail(class_name_symbol, class_loader, protection_domain, true, CHECK);
|
Klass* klass = SystemDictionary::resolve_or_fail(class_name_symbol, class_loader, protection_domain, true, CHECK);
|
||||||
@ -836,6 +853,8 @@ void ClassListParser::parse_constant_pool_tag() {
|
|||||||
case JVM_CONSTANT_InterfaceMethodref:
|
case JVM_CONSTANT_InterfaceMethodref:
|
||||||
preresolve_fmi = true;
|
preresolve_fmi = true;
|
||||||
break;
|
break;
|
||||||
|
case JVM_CONSTANT_InvokeDynamic:
|
||||||
|
preresolve_indy = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
constant_pool_resolution_warning("Unsupported constant pool index %d: %s (type=%d)",
|
constant_pool_resolution_warning("Unsupported constant pool index %d: %s (type=%d)",
|
||||||
@ -845,9 +864,12 @@ void ClassListParser::parse_constant_pool_tag() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (preresolve_class) {
|
if (preresolve_class) {
|
||||||
ClassPrelinker::preresolve_class_cp_entries(THREAD, ik, &preresolve_list);
|
AOTConstantPoolResolver::preresolve_class_cp_entries(THREAD, ik, &preresolve_list);
|
||||||
}
|
}
|
||||||
if (preresolve_fmi) {
|
if (preresolve_fmi) {
|
||||||
ClassPrelinker::preresolve_field_and_method_cp_entries(THREAD, ik, &preresolve_list);
|
AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(THREAD, ik, &preresolve_list);
|
||||||
|
}
|
||||||
|
if (preresolve_indy) {
|
||||||
|
AOTConstantPoolResolver::preresolve_indy_cp_entries(THREAD, ik, &preresolve_list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,9 +128,15 @@ void ClassListWriter::write_to_stream(const InstanceKlass* k, outputStream* stre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter out java/lang/invoke/BoundMethodHandle$Species...
|
if (cfs != nullptr && cfs->source() != nullptr) {
|
||||||
if (cfs != nullptr && cfs->source() != nullptr && strcmp(cfs->source(), "_ClassSpecializer_generateConcreteSpeciesCode") == 0) {
|
if (strcmp(cfs->source(), "_ClassSpecializer_generateConcreteSpeciesCode") == 0) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(cfs->source(), "__", 2) == 0) {
|
||||||
|
// generated class: __dynamic_proxy__, __JVM_LookupDefineClass__, etc
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -256,6 +262,18 @@ void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cp->cache() != nullptr) {
|
if (cp->cache() != nullptr) {
|
||||||
|
Array<ResolvedIndyEntry>* indy_entries = cp->cache()->resolved_indy_entries();
|
||||||
|
if (indy_entries != nullptr) {
|
||||||
|
for (int i = 0; i < indy_entries->length(); i++) {
|
||||||
|
ResolvedIndyEntry* rie = indy_entries->adr_at(i);
|
||||||
|
int cp_index = rie->constant_pool_index();
|
||||||
|
if (rie->is_resolved()) {
|
||||||
|
list.at_put(cp_index, true);
|
||||||
|
print = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Array<ResolvedFieldEntry>* field_entries = cp->cache()->resolved_field_entries();
|
Array<ResolvedFieldEntry>* field_entries = cp->cache()->resolved_field_entries();
|
||||||
if (field_entries != nullptr) {
|
if (field_entries != nullptr) {
|
||||||
for (int i = 0; i < field_entries->length(); i++) {
|
for (int i = 0; i < field_entries->length(); i++) {
|
||||||
@ -274,7 +292,8 @@ void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) {
|
|||||||
ResolvedMethodEntry* rme = method_entries->adr_at(i);
|
ResolvedMethodEntry* rme = method_entries->adr_at(i);
|
||||||
if (rme->is_resolved(Bytecodes::_invokevirtual) ||
|
if (rme->is_resolved(Bytecodes::_invokevirtual) ||
|
||||||
rme->is_resolved(Bytecodes::_invokespecial) ||
|
rme->is_resolved(Bytecodes::_invokespecial) ||
|
||||||
rme->is_resolved(Bytecodes::_invokeinterface)) {
|
rme->is_resolved(Bytecodes::_invokeinterface) ||
|
||||||
|
rme->is_resolved(Bytecodes::_invokehandle)) {
|
||||||
list.at_put(rme->constant_pool_index(), true);
|
list.at_put(rme->constant_pool_index(), true);
|
||||||
print = true;
|
print = true;
|
||||||
}
|
}
|
||||||
@ -291,7 +310,8 @@ void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) {
|
|||||||
assert(cp_tag.value() == JVM_CONSTANT_Class ||
|
assert(cp_tag.value() == JVM_CONSTANT_Class ||
|
||||||
cp_tag.value() == JVM_CONSTANT_Fieldref ||
|
cp_tag.value() == JVM_CONSTANT_Fieldref ||
|
||||||
cp_tag.value() == JVM_CONSTANT_Methodref||
|
cp_tag.value() == JVM_CONSTANT_Methodref||
|
||||||
cp_tag.value() == JVM_CONSTANT_InterfaceMethodref, "sanity");
|
cp_tag.value() == JVM_CONSTANT_InterfaceMethodref ||
|
||||||
|
cp_tag.value() == JVM_CONSTANT_InvokeDynamic, "sanity");
|
||||||
stream->print(" %d", i);
|
stream->print(" %d", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,345 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2022, 2024, 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
|
||||||
#include "cds/archiveBuilder.hpp"
|
|
||||||
#include "cds/cdsConfig.hpp"
|
|
||||||
#include "cds/classPrelinker.hpp"
|
|
||||||
#include "cds/regeneratedClasses.hpp"
|
|
||||||
#include "classfile/systemDictionary.hpp"
|
|
||||||
#include "classfile/systemDictionaryShared.hpp"
|
|
||||||
#include "classfile/vmClasses.hpp"
|
|
||||||
#include "interpreter/bytecodeStream.hpp"
|
|
||||||
#include "interpreter/interpreterRuntime.hpp"
|
|
||||||
#include "memory/resourceArea.hpp"
|
|
||||||
#include "oops/constantPool.inline.hpp"
|
|
||||||
#include "oops/instanceKlass.hpp"
|
|
||||||
#include "oops/klass.inline.hpp"
|
|
||||||
#include "runtime/handles.inline.hpp"
|
|
||||||
|
|
||||||
ClassPrelinker::ClassesTable* ClassPrelinker::_processed_classes = nullptr;
|
|
||||||
ClassPrelinker::ClassesTable* ClassPrelinker::_vm_classes = nullptr;
|
|
||||||
|
|
||||||
bool ClassPrelinker::is_vm_class(InstanceKlass* ik) {
|
|
||||||
return (_vm_classes->get(ik) != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassPrelinker::add_one_vm_class(InstanceKlass* ik) {
|
|
||||||
bool created;
|
|
||||||
_vm_classes->put_if_absent(ik, &created);
|
|
||||||
if (created) {
|
|
||||||
InstanceKlass* super = ik->java_super();
|
|
||||||
if (super != nullptr) {
|
|
||||||
add_one_vm_class(super);
|
|
||||||
}
|
|
||||||
Array<InstanceKlass*>* ifs = ik->local_interfaces();
|
|
||||||
for (int i = 0; i < ifs->length(); i++) {
|
|
||||||
add_one_vm_class(ifs->at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassPrelinker::initialize() {
|
|
||||||
assert(_vm_classes == nullptr, "must be");
|
|
||||||
_vm_classes = new (mtClass)ClassesTable();
|
|
||||||
_processed_classes = new (mtClass)ClassesTable();
|
|
||||||
for (auto id : EnumRange<vmClassID>{}) {
|
|
||||||
add_one_vm_class(vmClasses::klass_at(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassPrelinker::dispose() {
|
|
||||||
assert(_vm_classes != nullptr, "must be");
|
|
||||||
delete _vm_classes;
|
|
||||||
delete _processed_classes;
|
|
||||||
_vm_classes = nullptr;
|
|
||||||
_processed_classes = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if we CAN PROVE that cp_index will always resolve to
|
|
||||||
// the same information at both dump time and run time. This is a
|
|
||||||
// necessary (but not sufficient) condition for pre-resolving cp_index
|
|
||||||
// during CDS archive assembly.
|
|
||||||
bool ClassPrelinker::is_resolution_deterministic(ConstantPool* cp, int cp_index) {
|
|
||||||
assert(!is_in_archivebuilder_buffer(cp), "sanity");
|
|
||||||
|
|
||||||
if (cp->tag_at(cp_index).is_klass()) {
|
|
||||||
// We require cp_index to be already resolved. This is fine for now, are we
|
|
||||||
// currently archive only CP entries that are already resolved.
|
|
||||||
Klass* resolved_klass = cp->resolved_klass_at(cp_index);
|
|
||||||
return resolved_klass != nullptr && is_class_resolution_deterministic(cp->pool_holder(), resolved_klass);
|
|
||||||
} else if (cp->tag_at(cp_index).is_field() ||
|
|
||||||
cp->tag_at(cp_index).is_method() ||
|
|
||||||
cp->tag_at(cp_index).is_interface_method()) {
|
|
||||||
int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index);
|
|
||||||
if (!cp->tag_at(klass_cp_index).is_klass()) {
|
|
||||||
// Not yet resolved
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Klass* k = cp->resolved_klass_at(klass_cp_index);
|
|
||||||
if (!is_class_resolution_deterministic(cp->pool_holder(), k)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!k->is_instance_klass()) {
|
|
||||||
// TODO: support non instance klasses as well.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here, We don't check if this entry can actually be resolved to a valid Field/Method.
|
|
||||||
// This method should be called by the ConstantPool to check Fields/Methods that
|
|
||||||
// have already been successfully resolved.
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassPrelinker::is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class) {
|
|
||||||
assert(!is_in_archivebuilder_buffer(cp_holder), "sanity");
|
|
||||||
assert(!is_in_archivebuilder_buffer(resolved_class), "sanity");
|
|
||||||
|
|
||||||
if (resolved_class->is_instance_klass()) {
|
|
||||||
InstanceKlass* ik = InstanceKlass::cast(resolved_class);
|
|
||||||
|
|
||||||
if (!ik->is_shared() && SystemDictionaryShared::is_excluded_class(ik)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cp_holder->is_subtype_of(ik)) {
|
|
||||||
// All super types of ik will be resolved in ik->class_loader() before
|
|
||||||
// ik is defined in this loader, so it's safe to archive the resolved klass reference.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_vm_class(ik)) {
|
|
||||||
if (ik->class_loader() != cp_holder->class_loader()) {
|
|
||||||
// At runtime, cp_holder() may not be able to resolve to the same
|
|
||||||
// ik. For example, a different version of ik may be defined in
|
|
||||||
// cp->pool_holder()'s loader using MethodHandles.Lookup.defineClass().
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (resolved_class->is_objArray_klass()) {
|
|
||||||
Klass* elem = ObjArrayKlass::cast(resolved_class)->bottom_klass();
|
|
||||||
if (elem->is_instance_klass()) {
|
|
||||||
return is_class_resolution_deterministic(cp_holder, InstanceKlass::cast(elem));
|
|
||||||
} else if (elem->is_typeArray_klass()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (resolved_class->is_typeArray_klass()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassPrelinker::dumptime_resolve_constants(InstanceKlass* ik, TRAPS) {
|
|
||||||
if (!ik->is_linked()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool first_time;
|
|
||||||
_processed_classes->put_if_absent(ik, &first_time);
|
|
||||||
if (!first_time) {
|
|
||||||
// We have already resolved the constants in class, so no need to do it again.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
constantPoolHandle cp(THREAD, ik->constants());
|
|
||||||
for (int cp_index = 1; cp_index < cp->length(); cp_index++) { // Index 0 is unused
|
|
||||||
switch (cp->tag_at(cp_index).value()) {
|
|
||||||
case JVM_CONSTANT_String:
|
|
||||||
resolve_string(cp, cp_index, CHECK); // may throw OOM when interning strings.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This works only for the boot/platform/app loaders
|
|
||||||
Klass* ClassPrelinker::find_loaded_class(Thread* current, oop class_loader, Symbol* name) {
|
|
||||||
HandleMark hm(current);
|
|
||||||
Handle h_loader(current, class_loader);
|
|
||||||
Klass* k = SystemDictionary::find_instance_or_array_klass(current, name,
|
|
||||||
h_loader,
|
|
||||||
Handle());
|
|
||||||
if (k != nullptr) {
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
if (h_loader() == SystemDictionary::java_system_loader()) {
|
|
||||||
return find_loaded_class(current, SystemDictionary::java_platform_loader(), name);
|
|
||||||
} else if (h_loader() == SystemDictionary::java_platform_loader()) {
|
|
||||||
return find_loaded_class(current, nullptr, name);
|
|
||||||
} else {
|
|
||||||
assert(h_loader() == nullptr, "This function only works for boot/platform/app loaders %p %p %p",
|
|
||||||
cast_from_oop<address>(h_loader()),
|
|
||||||
cast_from_oop<address>(SystemDictionary::java_system_loader()),
|
|
||||||
cast_from_oop<address>(SystemDictionary::java_platform_loader()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Klass* ClassPrelinker::find_loaded_class(Thread* current, ConstantPool* cp, int class_cp_index) {
|
|
||||||
Symbol* name = cp->klass_name_at(class_cp_index);
|
|
||||||
return find_loaded_class(current, cp->pool_holder()->class_loader(), name);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if INCLUDE_CDS_JAVA_HEAP
|
|
||||||
void ClassPrelinker::resolve_string(constantPoolHandle cp, int cp_index, TRAPS) {
|
|
||||||
if (CDSConfig::is_dumping_heap()) {
|
|
||||||
int cache_index = cp->cp_to_object_index(cp_index);
|
|
||||||
ConstantPool::string_at_impl(cp, cp_index, cache_index, CHECK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void ClassPrelinker::preresolve_class_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list) {
|
|
||||||
if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JavaThread* THREAD = current;
|
|
||||||
constantPoolHandle cp(THREAD, ik->constants());
|
|
||||||
for (int cp_index = 1; cp_index < cp->length(); cp_index++) {
|
|
||||||
if (cp->tag_at(cp_index).value() == JVM_CONSTANT_UnresolvedClass) {
|
|
||||||
if (preresolve_list != nullptr && preresolve_list->at(cp_index) == false) {
|
|
||||||
// This class was not resolved during trial run. Don't attempt to resolve it. Otherwise
|
|
||||||
// the compiler may generate less efficient code.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (find_loaded_class(current, cp(), cp_index) == nullptr) {
|
|
||||||
// Do not resolve any class that has not been loaded yet
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Klass* resolved_klass = cp->klass_at(cp_index, THREAD);
|
|
||||||
if (HAS_PENDING_EXCEPTION) {
|
|
||||||
CLEAR_PENDING_EXCEPTION; // just ignore
|
|
||||||
} else {
|
|
||||||
log_trace(cds, resolve)("Resolved class [%3d] %s -> %s", cp_index, ik->external_name(),
|
|
||||||
resolved_klass->external_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassPrelinker::preresolve_field_and_method_cp_entries(JavaThread* current, InstanceKlass* ik, GrowableArray<bool>* preresolve_list) {
|
|
||||||
JavaThread* THREAD = current;
|
|
||||||
constantPoolHandle cp(THREAD, ik->constants());
|
|
||||||
if (cp->cache() == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < ik->methods()->length(); i++) {
|
|
||||||
Method* m = ik->methods()->at(i);
|
|
||||||
BytecodeStream bcs(methodHandle(THREAD, m));
|
|
||||||
while (!bcs.is_last_bytecode()) {
|
|
||||||
bcs.next();
|
|
||||||
Bytecodes::Code raw_bc = bcs.raw_code();
|
|
||||||
switch (raw_bc) {
|
|
||||||
case Bytecodes::_getfield:
|
|
||||||
case Bytecodes::_putfield:
|
|
||||||
maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
|
|
||||||
if (HAS_PENDING_EXCEPTION) {
|
|
||||||
CLEAR_PENDING_EXCEPTION; // just ignore
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Bytecodes::_invokespecial:
|
|
||||||
case Bytecodes::_invokevirtual:
|
|
||||||
case Bytecodes::_invokeinterface:
|
|
||||||
maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
|
|
||||||
if (HAS_PENDING_EXCEPTION) {
|
|
||||||
CLEAR_PENDING_EXCEPTION; // just ignore
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassPrelinker::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index,
|
|
||||||
GrowableArray<bool>* preresolve_list, TRAPS) {
|
|
||||||
methodHandle mh(THREAD, m);
|
|
||||||
constantPoolHandle cp(THREAD, ik->constants());
|
|
||||||
HandleMark hm(THREAD);
|
|
||||||
int cp_index = cp->to_cp_index(raw_index, bc);
|
|
||||||
|
|
||||||
if (cp->is_resolved(raw_index, bc)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preresolve_list != nullptr && preresolve_list->at(cp_index) == false) {
|
|
||||||
// This field wasn't resolved during the trial run. Don't attempt to resolve it. Otherwise
|
|
||||||
// the compiler may generate less efficient code.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int klass_cp_index = cp->uncached_klass_ref_index_at(cp_index);
|
|
||||||
if (find_loaded_class(THREAD, cp(), klass_cp_index) == nullptr) {
|
|
||||||
// Do not resolve any field/methods from a class that has not been loaded yet.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Klass* resolved_klass = cp->klass_ref_at(raw_index, bc, CHECK);
|
|
||||||
|
|
||||||
switch (bc) {
|
|
||||||
case Bytecodes::_getfield:
|
|
||||||
case Bytecodes::_putfield:
|
|
||||||
InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Bytecodes::_invokevirtual:
|
|
||||||
case Bytecodes::_invokespecial:
|
|
||||||
case Bytecodes::_invokeinterface:
|
|
||||||
InterpreterRuntime::cds_resolve_invoke(bc, raw_index, cp, CHECK);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
ShouldNotReachHere();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log_is_enabled(Trace, cds, resolve)) {
|
|
||||||
ResourceMark rm(THREAD);
|
|
||||||
bool resolved = cp->is_resolved(raw_index, bc);
|
|
||||||
Symbol* name = cp->name_ref_at(raw_index, bc);
|
|
||||||
Symbol* signature = cp->signature_ref_at(raw_index, bc);
|
|
||||||
log_trace(cds, resolve)("%s %s [%3d] %s -> %s.%s:%s",
|
|
||||||
(resolved ? "Resolved" : "Failed to resolve"),
|
|
||||||
Bytecodes::name(bc), cp_index, ik->external_name(),
|
|
||||||
resolved_klass->external_name(),
|
|
||||||
name->as_C_string(), signature->as_C_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ASSERT
|
|
||||||
bool ClassPrelinker::is_in_archivebuilder_buffer(address p) {
|
|
||||||
if (!Thread::current()->is_VM_thread() || ArchiveBuilder::current() == nullptr) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return ArchiveBuilder::current()->is_in_buffer_space(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassLinker.hpp"
|
||||||
#include "cds/dumpAllocStats.hpp"
|
#include "cds/dumpAllocStats.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "logging/logMessage.hpp"
|
#include "logging/logMessage.hpp"
|
||||||
@ -114,6 +115,12 @@ void DumpAllocStats::print_stats(int ro_all, int rw_all) {
|
|||||||
_num_method_cp_entries, _num_method_cp_entries_archived,
|
_num_method_cp_entries, _num_method_cp_entries_archived,
|
||||||
percent_of(_num_method_cp_entries_archived, _num_method_cp_entries),
|
percent_of(_num_method_cp_entries_archived, _num_method_cp_entries),
|
||||||
_num_method_cp_entries_reverted);
|
_num_method_cp_entries_reverted);
|
||||||
|
msg.info("Indy CP entries = %6d, archived = %6d (%5.1f%%), reverted = %6d",
|
||||||
|
_num_indy_cp_entries, _num_indy_cp_entries_archived,
|
||||||
|
percent_of(_num_indy_cp_entries_archived, _num_indy_cp_entries),
|
||||||
|
_num_indy_cp_entries_reverted);
|
||||||
|
msg.info("Platform loader initiated classes = %5d", AOTClassLinker::num_platform_initiated_classes());
|
||||||
|
msg.info("App loader initiated classes = %5d", AOTClassLinker::num_app_initiated_classes());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
|
@ -68,6 +68,9 @@ public:
|
|||||||
int _num_field_cp_entries;
|
int _num_field_cp_entries;
|
||||||
int _num_field_cp_entries_archived;
|
int _num_field_cp_entries_archived;
|
||||||
int _num_field_cp_entries_reverted;
|
int _num_field_cp_entries_reverted;
|
||||||
|
int _num_indy_cp_entries;
|
||||||
|
int _num_indy_cp_entries_archived;
|
||||||
|
int _num_indy_cp_entries_reverted;
|
||||||
int _num_klass_cp_entries;
|
int _num_klass_cp_entries;
|
||||||
int _num_klass_cp_entries_archived;
|
int _num_klass_cp_entries_archived;
|
||||||
int _num_klass_cp_entries_reverted;
|
int _num_klass_cp_entries_reverted;
|
||||||
@ -84,6 +87,9 @@ public:
|
|||||||
_num_field_cp_entries = 0;
|
_num_field_cp_entries = 0;
|
||||||
_num_field_cp_entries_archived = 0;
|
_num_field_cp_entries_archived = 0;
|
||||||
_num_field_cp_entries_reverted = 0;
|
_num_field_cp_entries_reverted = 0;
|
||||||
|
_num_indy_cp_entries = 0;
|
||||||
|
_num_indy_cp_entries_archived = 0;
|
||||||
|
_num_indy_cp_entries_reverted = 0;
|
||||||
_num_klass_cp_entries = 0;
|
_num_klass_cp_entries = 0;
|
||||||
_num_klass_cp_entries_archived = 0;
|
_num_klass_cp_entries_archived = 0;
|
||||||
_num_klass_cp_entries_reverted = 0;
|
_num_klass_cp_entries_reverted = 0;
|
||||||
@ -122,6 +128,12 @@ public:
|
|||||||
_num_field_cp_entries_reverted += reverted ? 1 : 0;
|
_num_field_cp_entries_reverted += reverted ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void record_indy_cp_entry(bool archived, bool reverted) {
|
||||||
|
_num_indy_cp_entries ++;
|
||||||
|
_num_indy_cp_entries_archived += archived ? 1 : 0;
|
||||||
|
_num_indy_cp_entries_reverted += reverted ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
void record_klass_cp_entry(bool archived, bool reverted) {
|
void record_klass_cp_entry(bool archived, bool reverted) {
|
||||||
_num_klass_cp_entries ++;
|
_num_klass_cp_entries ++;
|
||||||
_num_klass_cp_entries_archived += archived ? 1 : 0;
|
_num_klass_cp_entries_archived += archived ? 1 : 0;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -42,7 +42,8 @@ class DumpTimeClassInfo: public CHeapObj<mtClass> {
|
|||||||
bool _excluded;
|
bool _excluded;
|
||||||
bool _is_early_klass;
|
bool _is_early_klass;
|
||||||
bool _has_checked_exclusion;
|
bool _has_checked_exclusion;
|
||||||
|
bool _is_required_hidden_class;
|
||||||
|
bool _has_scanned_constant_pool;
|
||||||
class DTLoaderConstraint {
|
class DTLoaderConstraint {
|
||||||
Symbol* _name;
|
Symbol* _name;
|
||||||
char _loader_type1;
|
char _loader_type1;
|
||||||
@ -137,6 +138,8 @@ public:
|
|||||||
_failed_verification = false;
|
_failed_verification = false;
|
||||||
_is_archived_lambda_proxy = false;
|
_is_archived_lambda_proxy = false;
|
||||||
_has_checked_exclusion = false;
|
_has_checked_exclusion = false;
|
||||||
|
_is_required_hidden_class = false;
|
||||||
|
_has_scanned_constant_pool = false;
|
||||||
_id = -1;
|
_id = -1;
|
||||||
_clsfile_size = -1;
|
_clsfile_size = -1;
|
||||||
_clsfile_crc32 = -1;
|
_clsfile_crc32 = -1;
|
||||||
@ -214,6 +217,11 @@ public:
|
|||||||
InstanceKlass* nest_host() const { return _nest_host; }
|
InstanceKlass* nest_host() const { return _nest_host; }
|
||||||
void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; }
|
void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; }
|
||||||
|
|
||||||
|
bool is_required_hidden_class() const { return _is_required_hidden_class; }
|
||||||
|
void set_is_required_hidden_class() { _is_required_hidden_class = true; }
|
||||||
|
bool has_scanned_constant_pool() const { return _has_scanned_constant_pool; }
|
||||||
|
void set_has_scanned_constant_pool() { _has_scanned_constant_pool = true; }
|
||||||
|
|
||||||
size_t runtime_info_bytesize() const;
|
size_t runtime_info_bytesize() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassLinker.hpp"
|
||||||
#include "cds/archiveBuilder.hpp"
|
#include "cds/archiveBuilder.hpp"
|
||||||
#include "cds/archiveHeapWriter.hpp"
|
#include "cds/archiveHeapWriter.hpp"
|
||||||
#include "cds/archiveUtils.inline.hpp"
|
#include "cds/archiveUtils.inline.hpp"
|
||||||
#include "cds/cds_globals.hpp"
|
#include "cds/cds_globals.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "cds/classPrelinker.hpp"
|
|
||||||
#include "cds/dynamicArchive.hpp"
|
#include "cds/dynamicArchive.hpp"
|
||||||
#include "cds/regeneratedClasses.hpp"
|
#include "cds/regeneratedClasses.hpp"
|
||||||
#include "classfile/classLoader.hpp"
|
#include "classfile/classLoader.hpp"
|
||||||
@ -47,8 +47,8 @@
|
|||||||
#include "runtime/arguments.hpp"
|
#include "runtime/arguments.hpp"
|
||||||
#include "runtime/os.hpp"
|
#include "runtime/os.hpp"
|
||||||
#include "runtime/sharedRuntime.hpp"
|
#include "runtime/sharedRuntime.hpp"
|
||||||
#include "runtime/vmThread.hpp"
|
|
||||||
#include "runtime/vmOperations.hpp"
|
#include "runtime/vmOperations.hpp"
|
||||||
|
#include "runtime/vmThread.hpp"
|
||||||
#include "utilities/align.hpp"
|
#include "utilities/align.hpp"
|
||||||
#include "utilities/bitMap.inline.hpp"
|
#include "utilities/bitMap.inline.hpp"
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ public:
|
|||||||
|
|
||||||
// Block concurrent class unloading from changing the _dumptime_table
|
// Block concurrent class unloading from changing the _dumptime_table
|
||||||
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||||||
SystemDictionaryShared::check_excluded_classes();
|
SystemDictionaryShared::find_all_archivable_classes();
|
||||||
|
|
||||||
if (SystemDictionaryShared::is_dumptime_table_empty()) {
|
if (SystemDictionaryShared::is_dumptime_table_empty()) {
|
||||||
log_warning(cds, dynamic)("There is no class to be included in the dynamic archive.");
|
log_warning(cds, dynamic)("There is no class to be included in the dynamic archive.");
|
||||||
@ -135,6 +135,11 @@ public:
|
|||||||
|
|
||||||
verify_estimate_size(_estimated_metaspaceobj_bytes, "MetaspaceObjs");
|
verify_estimate_size(_estimated_metaspaceobj_bytes, "MetaspaceObjs");
|
||||||
|
|
||||||
|
sort_methods();
|
||||||
|
|
||||||
|
log_info(cds)("Make classes shareable");
|
||||||
|
make_klasses_shareable();
|
||||||
|
|
||||||
char* serialized_data;
|
char* serialized_data;
|
||||||
{
|
{
|
||||||
// Write the symbol table and system dictionaries to the RO space.
|
// Write the symbol table and system dictionaries to the RO space.
|
||||||
@ -147,6 +152,7 @@ public:
|
|||||||
ArchiveBuilder::OtherROAllocMark mark;
|
ArchiveBuilder::OtherROAllocMark mark;
|
||||||
SystemDictionaryShared::write_to_archive(false);
|
SystemDictionaryShared::write_to_archive(false);
|
||||||
DynamicArchive::dump_array_klasses();
|
DynamicArchive::dump_array_klasses();
|
||||||
|
AOTClassLinker::write_to_archive();
|
||||||
|
|
||||||
serialized_data = ro_region()->top();
|
serialized_data = ro_region()->top();
|
||||||
WriteClosure wc(ro_region());
|
WriteClosure wc(ro_region());
|
||||||
@ -155,11 +161,6 @@ public:
|
|||||||
|
|
||||||
verify_estimate_size(_estimated_hashtable_bytes, "Hashtables");
|
verify_estimate_size(_estimated_hashtable_bytes, "Hashtables");
|
||||||
|
|
||||||
sort_methods();
|
|
||||||
|
|
||||||
log_info(cds)("Make classes shareable");
|
|
||||||
make_klasses_shareable();
|
|
||||||
|
|
||||||
log_info(cds)("Adjust lambda proxy class dictionary");
|
log_info(cds)("Adjust lambda proxy class dictionary");
|
||||||
SystemDictionaryShared::adjust_lambda_proxy_class_dictionary();
|
SystemDictionaryShared::adjust_lambda_proxy_class_dictionary();
|
||||||
|
|
||||||
@ -234,7 +235,7 @@ void DynamicArchiveBuilder::release_header() {
|
|||||||
|
|
||||||
void DynamicArchiveBuilder::post_dump() {
|
void DynamicArchiveBuilder::post_dump() {
|
||||||
ArchivePtrMarker::reset_map_and_vs();
|
ArchivePtrMarker::reset_map_and_vs();
|
||||||
ClassPrelinker::dispose();
|
AOTClassLinker::dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicArchiveBuilder::sort_methods() {
|
void DynamicArchiveBuilder::sort_methods() {
|
||||||
@ -497,6 +498,7 @@ void DynamicArchive::check_for_dynamic_dump() {
|
|||||||
void DynamicArchive::dump_at_exit(JavaThread* current, const char* archive_name) {
|
void DynamicArchive::dump_at_exit(JavaThread* current, const char* archive_name) {
|
||||||
ExceptionMark em(current);
|
ExceptionMark em(current);
|
||||||
ResourceMark rm(current);
|
ResourceMark rm(current);
|
||||||
|
CDSConfig::DumperThreadMark dumper_thread_mark(current);
|
||||||
|
|
||||||
if (!CDSConfig::is_dumping_dynamic_archive() || archive_name == nullptr) {
|
if (!CDSConfig::is_dumping_dynamic_archive() || archive_name == nullptr) {
|
||||||
return;
|
return;
|
||||||
@ -526,6 +528,7 @@ void DynamicArchive::dump_at_exit(JavaThread* current, const char* archive_name)
|
|||||||
|
|
||||||
// This is called by "jcmd VM.cds dynamic_dump"
|
// This is called by "jcmd VM.cds dynamic_dump"
|
||||||
void DynamicArchive::dump_for_jcmd(const char* archive_name, TRAPS) {
|
void DynamicArchive::dump_for_jcmd(const char* archive_name, TRAPS) {
|
||||||
|
CDSConfig::DumperThreadMark dumper_thread_mark(THREAD);
|
||||||
assert(CDSConfig::is_using_archive() && RecordDynamicDumpInfo, "already checked in arguments.cpp");
|
assert(CDSConfig::is_using_archive() && RecordDynamicDumpInfo, "already checked in arguments.cpp");
|
||||||
assert(ArchiveClassesAtExit == nullptr, "already checked in arguments.cpp");
|
assert(ArchiveClassesAtExit == nullptr, "already checked in arguments.cpp");
|
||||||
assert(CDSConfig::is_dumping_dynamic_archive(), "already checked by check_for_dynamic_dump() during VM startup");
|
assert(CDSConfig::is_dumping_dynamic_archive(), "already checked by check_for_dynamic_dump() during VM startup");
|
||||||
|
@ -223,7 +223,9 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
|
|||||||
}
|
}
|
||||||
_max_heap_size = MaxHeapSize;
|
_max_heap_size = MaxHeapSize;
|
||||||
_use_optimized_module_handling = CDSConfig::is_using_optimized_module_handling();
|
_use_optimized_module_handling = CDSConfig::is_using_optimized_module_handling();
|
||||||
|
_has_aot_linked_classes = CDSConfig::is_dumping_aot_linked_classes();
|
||||||
_has_full_module_graph = CDSConfig::is_dumping_full_module_graph();
|
_has_full_module_graph = CDSConfig::is_dumping_full_module_graph();
|
||||||
|
_has_archived_invokedynamic = CDSConfig::is_dumping_invokedynamic();
|
||||||
|
|
||||||
// The following fields are for sanity checks for whether this archive
|
// The following fields are for sanity checks for whether this archive
|
||||||
// will function correctly with this JVM and the bootclasspath it's
|
// will function correctly with this JVM and the bootclasspath it's
|
||||||
@ -313,6 +315,8 @@ void FileMapHeader::print(outputStream* st) {
|
|||||||
st->print_cr("- allow_archiving_with_java_agent:%d", _allow_archiving_with_java_agent);
|
st->print_cr("- allow_archiving_with_java_agent:%d", _allow_archiving_with_java_agent);
|
||||||
st->print_cr("- use_optimized_module_handling: %d", _use_optimized_module_handling);
|
st->print_cr("- use_optimized_module_handling: %d", _use_optimized_module_handling);
|
||||||
st->print_cr("- has_full_module_graph %d", _has_full_module_graph);
|
st->print_cr("- has_full_module_graph %d", _has_full_module_graph);
|
||||||
|
st->print_cr("- has_aot_linked_classes %d", _has_aot_linked_classes);
|
||||||
|
st->print_cr("- has_archived_invokedynamic %d", _has_archived_invokedynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedClassPathEntry::init_as_non_existent(const char* path, TRAPS) {
|
void SharedClassPathEntry::init_as_non_existent(const char* path, TRAPS) {
|
||||||
@ -1060,7 +1064,9 @@ bool FileMapInfo::validate_shared_path_table() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_non_existent_class_paths();
|
if (!validate_non_existent_class_paths()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_validating_shared_path_table = false;
|
_validating_shared_path_table = false;
|
||||||
|
|
||||||
@ -1076,7 +1082,7 @@ bool FileMapInfo::validate_shared_path_table() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileMapInfo::validate_non_existent_class_paths() {
|
bool FileMapInfo::validate_non_existent_class_paths() {
|
||||||
// All of the recorded non-existent paths came from the Class-Path: attribute from the JAR
|
// All of the recorded non-existent paths came from the Class-Path: attribute from the JAR
|
||||||
// files on the app classpath. If any of these are found to exist during runtime,
|
// files on the app classpath. If any of these are found to exist during runtime,
|
||||||
// it will change how classes are loading for the app loader. For safety, disable
|
// it will change how classes are loading for the app loader. For safety, disable
|
||||||
@ -1089,11 +1095,19 @@ void FileMapInfo::validate_non_existent_class_paths() {
|
|||||||
i++) {
|
i++) {
|
||||||
SharedClassPathEntry* ent = shared_path(i);
|
SharedClassPathEntry* ent = shared_path(i);
|
||||||
if (!ent->check_non_existent()) {
|
if (!ent->check_non_existent()) {
|
||||||
log_warning(cds)("Archived non-system classes are disabled because the "
|
if (header()->has_aot_linked_classes()) {
|
||||||
"file %s exists", ent->name());
|
log_error(cds)("CDS archive has aot-linked classes. It cannot be used because the "
|
||||||
header()->set_has_platform_or_app_classes(false);
|
"file %s exists", ent->name());
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
log_warning(cds)("Archived non-system classes are disabled because the "
|
||||||
|
"file %s exists", ent->name());
|
||||||
|
header()->set_has_platform_or_app_classes(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A utility class for reading/validating the GenericCDSFileMapHeader portion of
|
// A utility class for reading/validating the GenericCDSFileMapHeader portion of
|
||||||
@ -2074,7 +2088,16 @@ void FileMapInfo::map_or_load_heap_region() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
CDSConfig::stop_using_full_module_graph();
|
if (CDSConfig::is_using_aot_linked_classes()) {
|
||||||
|
// It's too late to recover -- we have already committed to use the archived metaspace objects, but
|
||||||
|
// the archived heap objects cannot be loaded, so we don't have the archived FMG to guarantee that
|
||||||
|
// all AOT-linked classes are visible.
|
||||||
|
//
|
||||||
|
// We get here because the heap is too small. The app will fail anyway. So let's quit.
|
||||||
|
MetaspaceShared::unrecoverable_loading_error("CDS archive has aot-linked classes but the archived "
|
||||||
|
"heap objects cannot be loaded. Try increasing your heap size.");
|
||||||
|
}
|
||||||
|
CDSConfig::stop_using_full_module_graph("archive heap loading failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2429,6 +2452,34 @@ bool FileMapInfo::initialize() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileMapInfo::validate_aot_class_linking() {
|
||||||
|
// These checks need to be done after FileMapInfo::initialize(), which gets called before Universe::heap()
|
||||||
|
// is available.
|
||||||
|
if (header()->has_aot_linked_classes()) {
|
||||||
|
CDSConfig::set_has_aot_linked_classes(true);
|
||||||
|
if (JvmtiExport::should_post_class_file_load_hook()) {
|
||||||
|
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (JvmtiExport::has_early_vmstart_env()) {
|
||||||
|
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when JVMTI early vm start is in use.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!CDSConfig::is_using_full_module_graph()) {
|
||||||
|
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* prop = Arguments::get_property("java.security.manager");
|
||||||
|
if (prop != nullptr && strcmp(prop, "disallow") != 0) {
|
||||||
|
log_error(cds)("CDS archive has aot-linked classes. It cannot be used with -Djava.security.manager=%s.", prop);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// The 2 core spaces are RW->RO
|
// The 2 core spaces are RW->RO
|
||||||
FileMapRegion* FileMapInfo::first_core_region() const {
|
FileMapRegion* FileMapInfo::first_core_region() const {
|
||||||
return region_at(MetaspaceShared::rw);
|
return region_at(MetaspaceShared::rw);
|
||||||
@ -2478,6 +2529,11 @@ bool FileMapHeader::validate() {
|
|||||||
// header data
|
// header data
|
||||||
const char* prop = Arguments::get_property("java.system.class.loader");
|
const char* prop = Arguments::get_property("java.system.class.loader");
|
||||||
if (prop != nullptr) {
|
if (prop != nullptr) {
|
||||||
|
if (has_aot_linked_classes()) {
|
||||||
|
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when the "
|
||||||
|
"java.system.class.loader property is specified.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
log_warning(cds)("Archived non-system classes are disabled because the "
|
log_warning(cds)("Archived non-system classes are disabled because the "
|
||||||
"java.system.class.loader property is specified (value = \"%s\"). "
|
"java.system.class.loader property is specified (value = \"%s\"). "
|
||||||
"To use archived non-system classes, this property must not be set", prop);
|
"To use archived non-system classes, this property must not be set", prop);
|
||||||
@ -2542,9 +2598,15 @@ bool FileMapHeader::validate() {
|
|||||||
log_info(cds)("optimized module handling: disabled because archive was created without optimized module handling");
|
log_info(cds)("optimized module handling: disabled because archive was created without optimized module handling");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_static() && !_has_full_module_graph) {
|
if (is_static()) {
|
||||||
// Only the static archive can contain the full module graph.
|
// Only the static archive can contain the full module graph.
|
||||||
CDSConfig::stop_using_full_module_graph("archive was created without full module graph");
|
if (!_has_full_module_graph) {
|
||||||
|
CDSConfig::stop_using_full_module_graph("archive was created without full module graph");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_has_archived_invokedynamic) {
|
||||||
|
CDSConfig::set_has_archived_invokedynamic();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -228,7 +228,9 @@ private:
|
|||||||
bool _allow_archiving_with_java_agent; // setting of the AllowArchivingWithJavaAgent option
|
bool _allow_archiving_with_java_agent; // setting of the AllowArchivingWithJavaAgent option
|
||||||
bool _use_optimized_module_handling;// No module-relation VM options were specified, so we can skip
|
bool _use_optimized_module_handling;// No module-relation VM options were specified, so we can skip
|
||||||
// some expensive operations.
|
// some expensive operations.
|
||||||
|
bool _has_aot_linked_classes; // Was the CDS archive created with -XX:+AOTClassLinking
|
||||||
bool _has_full_module_graph; // Does this CDS archive contain the full archived module graph?
|
bool _has_full_module_graph; // Does this CDS archive contain the full archived module graph?
|
||||||
|
bool _has_archived_invokedynamic; // Does the archive have aot-linked invokedynamic CP entries?
|
||||||
HeapRootSegments _heap_root_segments; // Heap root segments info
|
HeapRootSegments _heap_root_segments; // Heap root segments info
|
||||||
size_t _heap_oopmap_start_pos; // The first bit in the oopmap corresponds to this position in the heap.
|
size_t _heap_oopmap_start_pos; // The first bit in the oopmap corresponds to this position in the heap.
|
||||||
size_t _heap_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap.
|
size_t _heap_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap.
|
||||||
@ -273,6 +275,7 @@ public:
|
|||||||
char* mapped_base_address() const { return _mapped_base_address; }
|
char* mapped_base_address() const { return _mapped_base_address; }
|
||||||
bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; }
|
bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; }
|
||||||
bool has_non_jar_in_classpath() const { return _has_non_jar_in_classpath; }
|
bool has_non_jar_in_classpath() const { return _has_non_jar_in_classpath; }
|
||||||
|
bool has_aot_linked_classes() const { return _has_aot_linked_classes; }
|
||||||
bool compressed_oops() const { return _compressed_oops; }
|
bool compressed_oops() const { return _compressed_oops; }
|
||||||
bool compressed_class_pointers() const { return _compressed_class_ptrs; }
|
bool compressed_class_pointers() const { return _compressed_class_ptrs; }
|
||||||
int narrow_klass_pointer_bits() const { return _narrow_klass_pointer_bits; }
|
int narrow_klass_pointer_bits() const { return _narrow_klass_pointer_bits; }
|
||||||
@ -492,7 +495,8 @@ public:
|
|||||||
static void check_nonempty_dir_in_shared_path_table();
|
static void check_nonempty_dir_in_shared_path_table();
|
||||||
bool check_module_paths();
|
bool check_module_paths();
|
||||||
bool validate_shared_path_table();
|
bool validate_shared_path_table();
|
||||||
void validate_non_existent_class_paths();
|
bool validate_non_existent_class_paths();
|
||||||
|
bool validate_aot_class_linking();
|
||||||
static void set_shared_path_table(FileMapInfo* info) {
|
static void set_shared_path_table(FileMapInfo* info) {
|
||||||
_shared_path_table = info->header()->shared_path_table();
|
_shared_path_table = info->header()->shared_path_table();
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -166,10 +166,16 @@ public:
|
|||||||
static bool is_subgraph_root_class(InstanceKlass* ik);
|
static bool is_subgraph_root_class(InstanceKlass* ik);
|
||||||
|
|
||||||
// Scratch objects for archiving Klass::java_mirror()
|
// Scratch objects for archiving Klass::java_mirror()
|
||||||
static oop scratch_java_mirror(BasicType t) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
static oop scratch_java_mirror(BasicType t) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
||||||
static oop scratch_java_mirror(Klass* k) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
static oop scratch_java_mirror(Klass* k) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
||||||
|
static oop scratch_java_mirror(oop java_mirror) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
||||||
static bool is_archived_boot_layer_available(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
static bool is_archived_boot_layer_available(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
|
||||||
|
// Look for all hidden classes that are referenced by archived objects.
|
||||||
|
static void start_finding_required_hidden_classes() NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
static void find_required_hidden_classes_in_object(oop o) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
static void end_finding_required_hidden_classes() NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if INCLUDE_CDS_JAVA_HEAP
|
#if INCLUDE_CDS_JAVA_HEAP
|
||||||
static bool _disable_writing;
|
static bool _disable_writing;
|
||||||
@ -184,12 +190,15 @@ private:
|
|||||||
|
|
||||||
static void count_allocation(size_t size);
|
static void count_allocation(size_t size);
|
||||||
static void print_stats();
|
static void print_stats();
|
||||||
|
static void debug_trace();
|
||||||
public:
|
public:
|
||||||
static unsigned oop_hash(oop const& p);
|
static unsigned oop_hash(oop const& p);
|
||||||
static unsigned string_oop_hash(oop const& string) {
|
static unsigned string_oop_hash(oop const& string) {
|
||||||
return java_lang_String::hash_code(string);
|
return java_lang_String::hash_code(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CopyKlassSubGraphInfoToArchive;
|
||||||
|
|
||||||
class CachedOopInfo {
|
class CachedOopInfo {
|
||||||
// Used by CDSHeapVerifier.
|
// Used by CDSHeapVerifier.
|
||||||
oop _orig_referrer;
|
oop _orig_referrer;
|
||||||
@ -251,7 +260,11 @@ private:
|
|||||||
static DumpTimeKlassSubGraphInfoTable* _dump_time_subgraph_info_table;
|
static DumpTimeKlassSubGraphInfoTable* _dump_time_subgraph_info_table;
|
||||||
static RunTimeKlassSubGraphInfoTable _run_time_subgraph_info_table;
|
static RunTimeKlassSubGraphInfoTable _run_time_subgraph_info_table;
|
||||||
|
|
||||||
|
class FindRequiredHiddenClassesOopClosure;
|
||||||
|
static void find_required_hidden_classes_helper(ArchivableStaticFieldInfo fields[]);
|
||||||
|
|
||||||
static CachedOopInfo make_cached_oop_info(oop obj);
|
static CachedOopInfo make_cached_oop_info(oop obj);
|
||||||
|
static ArchivedKlassSubGraphInfoRecord* archive_subgraph_info(KlassSubGraphInfo* info);
|
||||||
static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
|
static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
|
||||||
bool is_full_module_graph);
|
bool is_full_module_graph);
|
||||||
|
|
||||||
@ -265,7 +278,7 @@ private:
|
|||||||
InstanceKlass* k, int field_offset) PRODUCT_RETURN;
|
InstanceKlass* k, int field_offset) PRODUCT_RETURN;
|
||||||
static void verify_reachable_objects_from(oop obj) PRODUCT_RETURN;
|
static void verify_reachable_objects_from(oop obj) PRODUCT_RETURN;
|
||||||
static void verify_subgraph_from(oop orig_obj) PRODUCT_RETURN;
|
static void verify_subgraph_from(oop orig_obj) PRODUCT_RETURN;
|
||||||
static void check_default_subgraph_classes();
|
static void check_special_subgraph_classes();
|
||||||
|
|
||||||
static KlassSubGraphInfo* init_subgraph_info(Klass *k, bool is_full_module_graph);
|
static KlassSubGraphInfo* init_subgraph_info(Klass *k, bool is_full_module_graph);
|
||||||
static KlassSubGraphInfo* get_subgraph_info(Klass *k);
|
static KlassSubGraphInfo* get_subgraph_info(Klass *k);
|
||||||
@ -287,12 +300,14 @@ private:
|
|||||||
|
|
||||||
static SeenObjectsTable *_seen_objects_table;
|
static SeenObjectsTable *_seen_objects_table;
|
||||||
|
|
||||||
// The "default subgraph" is the root of all archived objects that do not belong to any
|
// The "special subgraph" contains all the archived objects that are reachable
|
||||||
// of the classes defined in the <xxx>_archive_subgraph_entry_fields[] arrays:
|
// from the following roots:
|
||||||
// - interned strings
|
// - interned strings
|
||||||
// - Klass::java_mirror()
|
// - Klass::java_mirror() -- including aot-initialized mirrors such as those of Enum klasses.
|
||||||
// - ConstantPool::resolved_references()
|
// - ConstantPool::resolved_references()
|
||||||
static KlassSubGraphInfo* _default_subgraph_info;
|
// - Universe::<xxx>_exception_instance()
|
||||||
|
static KlassSubGraphInfo* _dump_time_special_subgraph; // for collecting info during dump time
|
||||||
|
static ArchivedKlassSubGraphInfoRecord* _run_time_special_subgraph; // for initializing classes during run time.
|
||||||
|
|
||||||
static GrowableArrayCHeap<oop, mtClassShared>* _pending_roots;
|
static GrowableArrayCHeap<oop, mtClassShared>* _pending_roots;
|
||||||
static GrowableArrayCHeap<OopHandle, mtClassShared>* _root_segments;
|
static GrowableArrayCHeap<OopHandle, mtClassShared>* _root_segments;
|
||||||
@ -330,7 +345,7 @@ private:
|
|||||||
static bool has_been_seen_during_subgraph_recording(oop obj);
|
static bool has_been_seen_during_subgraph_recording(oop obj);
|
||||||
static void set_has_been_seen_during_subgraph_recording(oop obj);
|
static void set_has_been_seen_during_subgraph_recording(oop obj);
|
||||||
static bool archive_object(oop obj);
|
static bool archive_object(oop obj);
|
||||||
|
static void copy_aot_initialized_mirror(Klass* orig_k, oop orig_mirror, oop m);
|
||||||
static void copy_interned_strings();
|
static void copy_interned_strings();
|
||||||
|
|
||||||
static void resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]);
|
static void resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]);
|
||||||
@ -338,6 +353,7 @@ private:
|
|||||||
static void clear_archived_roots_of(Klass* k);
|
static void clear_archived_roots_of(Klass* k);
|
||||||
static const ArchivedKlassSubGraphInfoRecord*
|
static const ArchivedKlassSubGraphInfoRecord*
|
||||||
resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAPS);
|
resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAPS);
|
||||||
|
static void resolve_or_init(const char* klass_name, bool do_init, TRAPS);
|
||||||
static void resolve_or_init(Klass* k, bool do_init, TRAPS);
|
static void resolve_or_init(Klass* k, bool do_init, TRAPS);
|
||||||
static void init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record);
|
static void init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphInfoRecord* record);
|
||||||
|
|
||||||
@ -352,8 +368,16 @@ private:
|
|||||||
static void fill_failed_loaded_region();
|
static void fill_failed_loaded_region();
|
||||||
static void mark_native_pointers(oop orig_obj);
|
static void mark_native_pointers(oop orig_obj);
|
||||||
static bool has_been_archived(oop orig_obj);
|
static bool has_been_archived(oop orig_obj);
|
||||||
|
static void prepare_resolved_references();
|
||||||
static void archive_java_mirrors();
|
static void archive_java_mirrors();
|
||||||
static void archive_strings();
|
static void archive_strings();
|
||||||
|
static void copy_special_subgraph();
|
||||||
|
|
||||||
|
class AOTInitializedClassScanner;
|
||||||
|
static void find_all_aot_initialized_classes();
|
||||||
|
static void find_all_aot_initialized_classes_helper();
|
||||||
|
static bool scan_for_aot_initialized_classes(oop obj);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void reset_archived_object_states(TRAPS);
|
static void reset_archived_object_states(TRAPS);
|
||||||
static void create_archived_object_cache() {
|
static void create_archived_object_cache() {
|
||||||
@ -371,7 +395,6 @@ private:
|
|||||||
static int archive_exception_instance(oop exception);
|
static int archive_exception_instance(oop exception);
|
||||||
static void archive_objects(ArchiveHeapInfo* heap_info);
|
static void archive_objects(ArchiveHeapInfo* heap_info);
|
||||||
static void copy_objects();
|
static void copy_objects();
|
||||||
static void copy_special_objects();
|
|
||||||
|
|
||||||
static bool archive_reachable_objects_from(int level,
|
static bool archive_reachable_objects_from(int level,
|
||||||
KlassSubGraphInfo* subgraph_info,
|
KlassSubGraphInfo* subgraph_info,
|
||||||
@ -420,6 +443,7 @@ private:
|
|||||||
static objArrayOop scratch_resolved_references(ConstantPool* src);
|
static objArrayOop scratch_resolved_references(ConstantPool* src);
|
||||||
static void add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) NOT_CDS_JAVA_HEAP_RETURN;
|
static void add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
static void init_scratch_objects(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
static void init_scratch_objects(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
static void init_box_classes(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
static bool is_heap_region(int idx) {
|
static bool is_heap_region(int idx) {
|
||||||
CDS_JAVA_HEAP_ONLY(return (idx == MetaspaceShared::hp);)
|
CDS_JAVA_HEAP_ONLY(return (idx == MetaspaceShared::hp);)
|
||||||
NOT_CDS_JAVA_HEAP_RETURN_(false);
|
NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
@ -436,7 +460,16 @@ private:
|
|||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
static bool is_a_test_class_in_unnamed_module(Klass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
static bool is_a_test_class_in_unnamed_module(Klass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static void initialize_test_class_from_archive(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void initialize_java_lang_invoke(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
static void init_classes_for_special_subgraph(Handle loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
|
|
||||||
|
static bool is_lambda_form_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static bool is_lambda_proxy_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static bool is_string_concat_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
|
static bool is_archivable_hidden_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
#if INCLUDE_CDS_JAVA_HEAP
|
#if INCLUDE_CDS_JAVA_HEAP
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
#include "cds/lambdaFormInvokers.hpp"
|
#include "cds/lambdaFormInvokers.hpp"
|
||||||
#include "cds/metaspaceShared.hpp"
|
#include "cds/metaspaceShared.hpp"
|
||||||
#include "cds/regeneratedClasses.hpp"
|
#include "cds/regeneratedClasses.hpp"
|
||||||
#include "classfile/classLoadInfo.hpp"
|
|
||||||
#include "classfile/classFileStream.hpp"
|
#include "classfile/classFileStream.hpp"
|
||||||
|
#include "classfile/classLoadInfo.hpp"
|
||||||
#include "classfile/javaClasses.inline.hpp"
|
#include "classfile/javaClasses.inline.hpp"
|
||||||
#include "classfile/klassFactory.hpp"
|
#include "classfile/klassFactory.hpp"
|
||||||
#include "classfile/symbolTable.hpp"
|
#include "classfile/symbolTable.hpp"
|
||||||
@ -96,6 +96,21 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_static_archive() && CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
// Work around JDK-8310831, as some methods in lambda form holder classes may not get generated.
|
||||||
|
log_info(cds)("Archived MethodHandles may refer to lambda form holder classes. Cannot regenerate.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_dynamic_archive() && CDSConfig::is_dumping_aot_linked_classes() &&
|
||||||
|
CDSConfig::is_using_aot_linked_classes()) {
|
||||||
|
// The base archive may have some pre-resolved CP entries that point to the lambda form holder
|
||||||
|
// classes in the base archive. If we generate new versions of these classes, those CP entries
|
||||||
|
// will be pointing to invalid classes.
|
||||||
|
log_info(cds)("Base archive already has aot-linked lambda form holder classes. Cannot regenerate.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
|
|
||||||
Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS();
|
Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS();
|
||||||
|
@ -23,16 +23,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassLinker.hpp"
|
||||||
|
#include "cds/aotConstantPoolResolver.hpp"
|
||||||
|
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||||
#include "cds/archiveBuilder.hpp"
|
#include "cds/archiveBuilder.hpp"
|
||||||
#include "cds/archiveHeapLoader.hpp"
|
#include "cds/archiveHeapLoader.hpp"
|
||||||
#include "cds/archiveHeapWriter.hpp"
|
#include "cds/archiveHeapWriter.hpp"
|
||||||
#include "cds/cds_globals.hpp"
|
#include "cds/cds_globals.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "cds/cdsProtectionDomain.hpp"
|
#include "cds/cdsProtectionDomain.hpp"
|
||||||
#include "cds/cds_globals.hpp"
|
|
||||||
#include "cds/classListParser.hpp"
|
#include "cds/classListParser.hpp"
|
||||||
#include "cds/classListWriter.hpp"
|
#include "cds/classListWriter.hpp"
|
||||||
#include "cds/classPrelinker.hpp"
|
|
||||||
#include "cds/cppVtables.hpp"
|
#include "cds/cppVtables.hpp"
|
||||||
#include "cds/dumpAllocStats.hpp"
|
#include "cds/dumpAllocStats.hpp"
|
||||||
#include "cds/dynamicArchive.hpp"
|
#include "cds/dynamicArchive.hpp"
|
||||||
@ -98,6 +99,7 @@ bool MetaspaceShared::_remapped_readwrite = false;
|
|||||||
void* MetaspaceShared::_shared_metaspace_static_top = nullptr;
|
void* MetaspaceShared::_shared_metaspace_static_top = nullptr;
|
||||||
intx MetaspaceShared::_relocation_delta;
|
intx MetaspaceShared::_relocation_delta;
|
||||||
char* MetaspaceShared::_requested_base_address;
|
char* MetaspaceShared::_requested_base_address;
|
||||||
|
Array<Method*>* MetaspaceShared::_archived_method_handle_intrinsics = nullptr;
|
||||||
bool MetaspaceShared::_use_optimized_module_handling = true;
|
bool MetaspaceShared::_use_optimized_module_handling = true;
|
||||||
|
|
||||||
// The CDS archive is divided into the following regions:
|
// The CDS archive is divided into the following regions:
|
||||||
@ -308,8 +310,12 @@ void MetaspaceShared::post_initialize(TRAPS) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extra java.lang.Strings to be added to the archive
|
||||||
static GrowableArrayCHeap<OopHandle, mtClassShared>* _extra_interned_strings = nullptr;
|
static GrowableArrayCHeap<OopHandle, mtClassShared>* _extra_interned_strings = nullptr;
|
||||||
|
// Extra Symbols to be added to the archive
|
||||||
static GrowableArrayCHeap<Symbol*, mtClassShared>* _extra_symbols = nullptr;
|
static GrowableArrayCHeap<Symbol*, mtClassShared>* _extra_symbols = nullptr;
|
||||||
|
// Methods managed by SystemDictionary::find_method_handle_intrinsic() to be added to the archive
|
||||||
|
static GrowableArray<Method*>* _pending_method_handle_intrinsics = NULL;
|
||||||
|
|
||||||
void MetaspaceShared::read_extra_data(JavaThread* current, const char* filename) {
|
void MetaspaceShared::read_extra_data(JavaThread* current, const char* filename) {
|
||||||
_extra_interned_strings = new GrowableArrayCHeap<OopHandle, mtClassShared>(10000);
|
_extra_interned_strings = new GrowableArrayCHeap<OopHandle, mtClassShared>(10000);
|
||||||
@ -360,6 +366,30 @@ void MetaspaceShared::read_extra_data(JavaThread* current, const char* filename)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MetaspaceShared::make_method_handle_intrinsics_shareable() {
|
||||||
|
for (int i = 0; i < _pending_method_handle_intrinsics->length(); i++) {
|
||||||
|
Method* m = ArchiveBuilder::current()->get_buffered_addr(_pending_method_handle_intrinsics->at(i));
|
||||||
|
m->remove_unshareable_info();
|
||||||
|
// Each method has its own constant pool (which is distinct from m->method_holder()->constants());
|
||||||
|
m->constants()->remove_unshareable_info();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetaspaceShared::write_method_handle_intrinsics() {
|
||||||
|
int len = _pending_method_handle_intrinsics->length();
|
||||||
|
_archived_method_handle_intrinsics = ArchiveBuilder::new_ro_array<Method*>(len);
|
||||||
|
int word_size = _archived_method_handle_intrinsics->size();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
Method* m = _pending_method_handle_intrinsics->at(i);
|
||||||
|
ArchiveBuilder::current()->write_pointer_in_buffer(_archived_method_handle_intrinsics->adr_at(i), m);
|
||||||
|
word_size += m->size() + m->constMethod()->size() + m->constants()->size();
|
||||||
|
if (m->constants()->cache() != nullptr) {
|
||||||
|
word_size += m->constants()->cache()->size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_info(cds)("Archived %d method handle intrinsics (%d bytes)", len, word_size * BytesPerWord);
|
||||||
|
}
|
||||||
|
|
||||||
// About "serialize" --
|
// About "serialize" --
|
||||||
//
|
//
|
||||||
// This is (probably a badly named) way to read/write a data stream of pointers and
|
// This is (probably a badly named) way to read/write a data stream of pointers and
|
||||||
@ -431,7 +461,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
|
|||||||
StringTable::serialize_shared_table_header(soc);
|
StringTable::serialize_shared_table_header(soc);
|
||||||
HeapShared::serialize_tables(soc);
|
HeapShared::serialize_tables(soc);
|
||||||
SystemDictionaryShared::serialize_dictionary_headers(soc);
|
SystemDictionaryShared::serialize_dictionary_headers(soc);
|
||||||
|
AOTLinkedClassBulkLoader::serialize(soc, true);
|
||||||
InstanceMirrorKlass::serialize_offsets(soc);
|
InstanceMirrorKlass::serialize_offsets(soc);
|
||||||
|
|
||||||
// Dump/restore well known classes (pointers)
|
// Dump/restore well known classes (pointers)
|
||||||
@ -439,6 +469,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
|
|||||||
soc->do_tag(--tag);
|
soc->do_tag(--tag);
|
||||||
|
|
||||||
CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc);)
|
CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc);)
|
||||||
|
soc->do_ptr((void**)&_archived_method_handle_intrinsics);
|
||||||
|
|
||||||
LambdaFormInvokers::serialize(soc);
|
LambdaFormInvokers::serialize(soc);
|
||||||
soc->do_tag(666);
|
soc->do_tag(666);
|
||||||
@ -526,6 +557,10 @@ public:
|
|||||||
it->push(_extra_symbols->adr_at(i));
|
it->push(_extra_symbols->adr_at(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _pending_method_handle_intrinsics->length(); i++) {
|
||||||
|
it->push(_pending_method_handle_intrinsics->adr_at(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -548,6 +583,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
|
|||||||
ArchiveBuilder::OtherROAllocMark mark;
|
ArchiveBuilder::OtherROAllocMark mark;
|
||||||
|
|
||||||
SystemDictionaryShared::write_to_archive();
|
SystemDictionaryShared::write_to_archive();
|
||||||
|
AOTClassLinker::write_to_archive();
|
||||||
|
MetaspaceShared::write_method_handle_intrinsics();
|
||||||
|
|
||||||
// Write lambform lines into archive
|
// Write lambform lines into archive
|
||||||
LambdaFormInvokers::dump_static_archive_invokers();
|
LambdaFormInvokers::dump_static_archive_invokers();
|
||||||
@ -566,13 +603,20 @@ void VM_PopulateDumpSharedSpace::doit() {
|
|||||||
|
|
||||||
DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
|
DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
|
||||||
|
|
||||||
|
_pending_method_handle_intrinsics = new (mtClassShared) GrowableArray<Method*>(256, mtClassShared);
|
||||||
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
|
// When dumping AOT-linked classes, some classes may have direct references to a method handle
|
||||||
|
// intrinsic. The easiest thing is to save all of them into the AOT cache.
|
||||||
|
SystemDictionary::get_all_method_handle_intrinsics(_pending_method_handle_intrinsics);
|
||||||
|
}
|
||||||
|
|
||||||
FileMapInfo::check_nonempty_dir_in_shared_path_table();
|
FileMapInfo::check_nonempty_dir_in_shared_path_table();
|
||||||
|
|
||||||
NOT_PRODUCT(SystemDictionary::verify();)
|
NOT_PRODUCT(SystemDictionary::verify();)
|
||||||
|
|
||||||
// Block concurrent class unloading from changing the _dumptime_table
|
// Block concurrent class unloading from changing the _dumptime_table
|
||||||
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||||||
SystemDictionaryShared::check_excluded_classes();
|
SystemDictionaryShared::find_all_archivable_classes();
|
||||||
|
|
||||||
_builder.gather_source_objs();
|
_builder.gather_source_objs();
|
||||||
_builder.reserve_buffer();
|
_builder.reserve_buffer();
|
||||||
@ -589,6 +633,7 @@ void VM_PopulateDumpSharedSpace::doit() {
|
|||||||
|
|
||||||
log_info(cds)("Make classes shareable");
|
log_info(cds)("Make classes shareable");
|
||||||
_builder.make_klasses_shareable();
|
_builder.make_klasses_shareable();
|
||||||
|
MetaspaceShared::make_method_handle_intrinsics_shareable();
|
||||||
|
|
||||||
char* early_serialized_data = dump_early_read_only_tables();
|
char* early_serialized_data = dump_early_read_only_tables();
|
||||||
char* serialized_data = dump_read_only_tables();
|
char* serialized_data = dump_read_only_tables();
|
||||||
@ -656,12 +701,12 @@ bool MetaspaceShared::link_class_for_cds(InstanceKlass* ik, TRAPS) {
|
|||||||
// cpcache to be created. Class verification is done according
|
// cpcache to be created. Class verification is done according
|
||||||
// to -Xverify setting.
|
// to -Xverify setting.
|
||||||
bool res = MetaspaceShared::try_link_class(THREAD, ik);
|
bool res = MetaspaceShared::try_link_class(THREAD, ik);
|
||||||
ClassPrelinker::dumptime_resolve_constants(ik, CHECK_(false));
|
AOTConstantPoolResolver::dumptime_resolve_constants(ik, CHECK_(false));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) {
|
void MetaspaceShared::link_shared_classes(bool jcmd_request, TRAPS) {
|
||||||
ClassPrelinker::initialize();
|
AOTClassLinker::initialize();
|
||||||
|
|
||||||
if (!jcmd_request) {
|
if (!jcmd_request) {
|
||||||
LambdaFormInvokers::regenerate_holder_classes(CHECK);
|
LambdaFormInvokers::regenerate_holder_classes(CHECK);
|
||||||
@ -709,6 +754,7 @@ void MetaspaceShared::prepare_for_dumping() {
|
|||||||
// Preload classes from a list, populate the shared spaces and dump to a
|
// Preload classes from a list, populate the shared spaces and dump to a
|
||||||
// file.
|
// file.
|
||||||
void MetaspaceShared::preload_and_dump(TRAPS) {
|
void MetaspaceShared::preload_and_dump(TRAPS) {
|
||||||
|
CDSConfig::DumperThreadMark dumper_thread_mark(THREAD);
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
StaticArchiveBuilder builder;
|
StaticArchiveBuilder builder;
|
||||||
preload_and_dump_impl(builder, THREAD);
|
preload_and_dump_impl(builder, THREAD);
|
||||||
@ -723,6 +769,15 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
|
|||||||
MetaspaceShared::writing_error("Unexpected exception, use -Xlog:cds,exceptions=trace for detail");
|
MetaspaceShared::writing_error("Unexpected exception, use -Xlog:cds,exceptions=trace for detail");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!CDSConfig::old_cds_flags_used()) {
|
||||||
|
// The JLI launcher only recognizes the "old" -Xshare:dump flag.
|
||||||
|
// When the new -XX:AOTMode=create flag is used, we can't return
|
||||||
|
// to the JLI launcher, as the launcher will fail when trying to
|
||||||
|
// run the main class, which is not what we want.
|
||||||
|
tty->print_cr("AOTCache creation is complete: %s", AOTCache);
|
||||||
|
vm_exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS_JAVA_HEAP && defined(_LP64)
|
#if INCLUDE_CDS_JAVA_HEAP && defined(_LP64)
|
||||||
@ -851,6 +906,29 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS
|
|||||||
HeapShared::reset_archived_object_states(CHECK);
|
HeapShared::reset_archived_object_states(CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
// This assert means that the MethodType and MethodTypeForm tables won't be
|
||||||
|
// updated concurrently when we are saving their contents into a side table.
|
||||||
|
assert(CDSConfig::allow_only_single_java_thread(), "Required");
|
||||||
|
|
||||||
|
JavaValue result(T_VOID);
|
||||||
|
JavaCalls::call_static(&result, vmClasses::MethodType_klass(),
|
||||||
|
vmSymbols::createArchivedObjects(),
|
||||||
|
vmSymbols::void_method_signature(),
|
||||||
|
CHECK);
|
||||||
|
|
||||||
|
// java.lang.Class::reflectionFactory cannot be archived yet. We set this field
|
||||||
|
// to null, and it will be initialized again at runtime.
|
||||||
|
log_debug(cds)("Resetting Class::reflectionFactory");
|
||||||
|
TempNewSymbol method_name = SymbolTable::new_symbol("resetArchivedStates");
|
||||||
|
Symbol* method_sig = vmSymbols::void_method_signature();
|
||||||
|
JavaCalls::call_static(&result, vmClasses::Class_klass(),
|
||||||
|
method_name, method_sig, CHECK);
|
||||||
|
|
||||||
|
// Perhaps there is a way to avoid hard-coding these names here.
|
||||||
|
// See discussion in JDK-8342481.
|
||||||
|
}
|
||||||
|
|
||||||
// Do this at the very end, when no Java code will be executed. Otherwise
|
// Do this at the very end, when no Java code will be executed. Otherwise
|
||||||
// some new strings may be added to the intern table.
|
// some new strings may be added to the intern table.
|
||||||
StringTable::allocate_shared_strings_array(CHECK);
|
StringTable::allocate_shared_strings_array(CHECK);
|
||||||
@ -1536,6 +1614,11 @@ MapArchiveResult MetaspaceShared::map_archive(FileMapInfo* mapinfo, char* mapped
|
|||||||
early_serialize(&rc);
|
early_serialize(&rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mapinfo->validate_aot_class_linking()) {
|
||||||
|
unmap_archive(mapinfo);
|
||||||
|
return MAP_ARCHIVE_OTHER_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
mapinfo->set_is_mapped(true);
|
mapinfo->set_is_mapped(true);
|
||||||
return MAP_ARCHIVE_SUCCESS;
|
return MAP_ARCHIVE_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -1596,6 +1679,18 @@ void MetaspaceShared::initialize_shared_spaces() {
|
|||||||
dynamic_mapinfo->unmap_region(MetaspaceShared::bm);
|
dynamic_mapinfo->unmap_region(MetaspaceShared::bm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogStreamHandle(Info, cds) lsh;
|
||||||
|
if (lsh.is_enabled()) {
|
||||||
|
lsh.print("Using AOT-linked classes: %s (static archive: %s aot-linked classes",
|
||||||
|
BOOL_TO_STR(CDSConfig::is_using_aot_linked_classes()),
|
||||||
|
static_mapinfo->header()->has_aot_linked_classes() ? "has" : "no");
|
||||||
|
if (dynamic_mapinfo != nullptr) {
|
||||||
|
lsh.print(", dynamic archive: %s aot-linked classes",
|
||||||
|
dynamic_mapinfo->header()->has_aot_linked_classes() ? "has" : "no");
|
||||||
|
}
|
||||||
|
lsh.print_cr(")");
|
||||||
|
}
|
||||||
|
|
||||||
// Set up LambdaFormInvokers::_lambdaform_lines for dynamic dump
|
// Set up LambdaFormInvokers::_lambdaform_lines for dynamic dump
|
||||||
if (CDSConfig::is_dumping_dynamic_archive()) {
|
if (CDSConfig::is_dumping_dynamic_archive()) {
|
||||||
// Read stored LF format lines stored in static archive
|
// Read stored LF format lines stored in static archive
|
||||||
|
@ -34,10 +34,12 @@
|
|||||||
class ArchiveBuilder;
|
class ArchiveBuilder;
|
||||||
class ArchiveHeapInfo;
|
class ArchiveHeapInfo;
|
||||||
class FileMapInfo;
|
class FileMapInfo;
|
||||||
|
class Method;
|
||||||
class outputStream;
|
class outputStream;
|
||||||
class SerializeClosure;
|
class SerializeClosure;
|
||||||
class StaticArchiveBuilder;
|
class StaticArchiveBuilder;
|
||||||
|
|
||||||
|
template<class E> class Array;
|
||||||
template<class E> class GrowableArray;
|
template<class E> class GrowableArray;
|
||||||
|
|
||||||
enum MapArchiveResult {
|
enum MapArchiveResult {
|
||||||
@ -56,6 +58,7 @@ class MetaspaceShared : AllStatic {
|
|||||||
static intx _relocation_delta;
|
static intx _relocation_delta;
|
||||||
static char* _requested_base_address;
|
static char* _requested_base_address;
|
||||||
static bool _use_optimized_module_handling;
|
static bool _use_optimized_module_handling;
|
||||||
|
static Array<Method*>* _archived_method_handle_intrinsics;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
@ -111,6 +114,9 @@ public:
|
|||||||
static void unrecoverable_writing_error(const char* message = nullptr);
|
static void unrecoverable_writing_error(const char* message = nullptr);
|
||||||
static void writing_error(const char* message = nullptr);
|
static void writing_error(const char* message = nullptr);
|
||||||
|
|
||||||
|
static void make_method_handle_intrinsics_shareable() NOT_CDS_RETURN;
|
||||||
|
static void write_method_handle_intrinsics() NOT_CDS_RETURN;
|
||||||
|
static Array<Method*>* archived_method_handle_intrinsics() { return _archived_method_handle_intrinsics; }
|
||||||
static void early_serialize(SerializeClosure* sc) NOT_CDS_RETURN;
|
static void early_serialize(SerializeClosure* sc) NOT_CDS_RETURN;
|
||||||
static void serialize(SerializeClosure* sc) NOT_CDS_RETURN;
|
static void serialize(SerializeClosure* sc) NOT_CDS_RETURN;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k->is_hidden()) {
|
if (k->is_hidden() && info.nest_host() != nullptr) {
|
||||||
_nest_host_offset = builder->any_to_offset_u4(info.nest_host());
|
_nest_host_offset = builder->any_to_offset_u4(info.nest_host());
|
||||||
}
|
}
|
||||||
if (k->has_archived_enum_objs()) {
|
if (k->has_archived_enum_objs()) {
|
||||||
|
@ -177,7 +177,11 @@ public:
|
|||||||
|
|
||||||
InstanceKlass* nest_host() {
|
InstanceKlass* nest_host() {
|
||||||
assert(!ArchiveBuilder::is_active(), "not called when dumping archive");
|
assert(!ArchiveBuilder::is_active(), "not called when dumping archive");
|
||||||
return ArchiveUtils::from_offset<InstanceKlass*>(_nest_host_offset);
|
if (_nest_host_offset == 0) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return ArchiveUtils::from_offset<InstanceKlass*>(_nest_host_offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RTLoaderConstraint* loader_constraints() {
|
RTLoaderConstraint* loader_constraints() {
|
||||||
|
@ -26,16 +26,17 @@
|
|||||||
#include "cds/cds_globals.hpp"
|
#include "cds/cds_globals.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "cds/filemap.hpp"
|
#include "cds/filemap.hpp"
|
||||||
|
#include "cds/heapShared.hpp"
|
||||||
#include "classfile/classFileStream.hpp"
|
#include "classfile/classFileStream.hpp"
|
||||||
#include "classfile/classLoader.inline.hpp"
|
#include "classfile/classLoader.inline.hpp"
|
||||||
#include "classfile/classLoaderData.inline.hpp"
|
#include "classfile/classLoaderData.inline.hpp"
|
||||||
#include "classfile/classLoaderExt.hpp"
|
#include "classfile/classLoaderExt.hpp"
|
||||||
#include "classfile/classLoadInfo.hpp"
|
#include "classfile/classLoadInfo.hpp"
|
||||||
#include "classfile/javaClasses.hpp"
|
#include "classfile/javaClasses.hpp"
|
||||||
|
#include "classfile/klassFactory.hpp"
|
||||||
#include "classfile/moduleEntry.hpp"
|
#include "classfile/moduleEntry.hpp"
|
||||||
#include "classfile/modules.hpp"
|
#include "classfile/modules.hpp"
|
||||||
#include "classfile/packageEntry.hpp"
|
#include "classfile/packageEntry.hpp"
|
||||||
#include "classfile/klassFactory.hpp"
|
|
||||||
#include "classfile/symbolTable.hpp"
|
#include "classfile/symbolTable.hpp"
|
||||||
#include "classfile/systemDictionary.hpp"
|
#include "classfile/systemDictionary.hpp"
|
||||||
#include "classfile/systemDictionaryShared.hpp"
|
#include "classfile/systemDictionaryShared.hpp"
|
||||||
@ -1273,7 +1274,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
|
|||||||
assert(stream != nullptr, "sanity");
|
assert(stream != nullptr, "sanity");
|
||||||
|
|
||||||
if (ik->is_hidden()) {
|
if (ik->is_hidden()) {
|
||||||
// We do not archive hidden classes.
|
record_hidden_class(ik);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1375,6 +1376,44 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
|
|||||||
assert(file_name != nullptr, "invariant");
|
assert(file_name != nullptr, "invariant");
|
||||||
ClassLoaderExt::record_result(checked_cast<s2>(classpath_index), ik, redefined);
|
ClassLoaderExt::record_result(checked_cast<s2>(classpath_index), ik, redefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClassLoader::record_hidden_class(InstanceKlass* ik) {
|
||||||
|
assert(ik->is_hidden(), "must be");
|
||||||
|
|
||||||
|
s2 classloader_type;
|
||||||
|
if (HeapShared::is_lambda_form_klass(ik)) {
|
||||||
|
classloader_type = ClassLoader::BOOT_LOADER;
|
||||||
|
} else {
|
||||||
|
oop loader = ik->class_loader();
|
||||||
|
|
||||||
|
if (loader == nullptr) {
|
||||||
|
classloader_type = ClassLoader::BOOT_LOADER;
|
||||||
|
} else if (SystemDictionary::is_platform_class_loader(loader)) {
|
||||||
|
classloader_type = ClassLoader::PLATFORM_LOADER;
|
||||||
|
} else if (SystemDictionary::is_system_class_loader(loader)) {
|
||||||
|
classloader_type = ClassLoader::APP_LOADER;
|
||||||
|
} else {
|
||||||
|
// This class won't be archived, so no need to update its
|
||||||
|
// classloader_type/classpath_index.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ik->set_shared_class_loader_type(classloader_type);
|
||||||
|
|
||||||
|
if (HeapShared::is_lambda_proxy_klass(ik)) {
|
||||||
|
InstanceKlass* nest_host = ik->nest_host_not_null();
|
||||||
|
ik->set_shared_classpath_index(nest_host->shared_classpath_index());
|
||||||
|
} else if (HeapShared::is_lambda_form_klass(ik)) {
|
||||||
|
ik->set_shared_classpath_index(0);
|
||||||
|
} else {
|
||||||
|
// Generated invoker classes.
|
||||||
|
if (classloader_type == ClassLoader::APP_LOADER) {
|
||||||
|
ik->set_shared_classpath_index(ClassLoaderExt::app_class_paths_start_index());
|
||||||
|
} else {
|
||||||
|
ik->set_shared_classpath_index(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // INCLUDE_CDS
|
#endif // INCLUDE_CDS
|
||||||
|
|
||||||
// Initialize the class loader's access to methods in libzip. Parse and
|
// Initialize the class loader's access to methods in libzip. Parse and
|
||||||
|
@ -140,6 +140,7 @@ public:
|
|||||||
class ClassLoader: AllStatic {
|
class ClassLoader: AllStatic {
|
||||||
public:
|
public:
|
||||||
enum ClassLoaderType {
|
enum ClassLoaderType {
|
||||||
|
OTHER = 0,
|
||||||
BOOT_LOADER = 1, /* boot loader */
|
BOOT_LOADER = 1, /* boot loader */
|
||||||
PLATFORM_LOADER = 2, /* PlatformClassLoader */
|
PLATFORM_LOADER = 2, /* PlatformClassLoader */
|
||||||
APP_LOADER = 3 /* AppClassLoader */
|
APP_LOADER = 3 /* AppClassLoader */
|
||||||
@ -385,6 +386,7 @@ class ClassLoader: AllStatic {
|
|||||||
static char* uri_to_path(const char* uri);
|
static char* uri_to_path(const char* uri);
|
||||||
static void record_result(JavaThread* current, InstanceKlass* ik,
|
static void record_result(JavaThread* current, InstanceKlass* ik,
|
||||||
const ClassFileStream* stream, bool redefined);
|
const ClassFileStream* stream, bool redefined);
|
||||||
|
static void record_hidden_class(InstanceKlass* ik);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static char* lookup_vm_options();
|
static char* lookup_vm_options();
|
||||||
|
@ -867,6 +867,7 @@ int java_lang_Class::_name_offset;
|
|||||||
int java_lang_Class::_source_file_offset;
|
int java_lang_Class::_source_file_offset;
|
||||||
int java_lang_Class::_classData_offset;
|
int java_lang_Class::_classData_offset;
|
||||||
int java_lang_Class::_classRedefinedCount_offset;
|
int java_lang_Class::_classRedefinedCount_offset;
|
||||||
|
int java_lang_Class::_reflectionData_offset;
|
||||||
|
|
||||||
bool java_lang_Class::_offsets_computed = false;
|
bool java_lang_Class::_offsets_computed = false;
|
||||||
GrowableArray<Klass*>* java_lang_Class::_fixup_mirror_list = nullptr;
|
GrowableArray<Klass*>* java_lang_Class::_fixup_mirror_list = nullptr;
|
||||||
@ -1303,6 +1304,11 @@ void java_lang_Class::set_class_data(oop java_class, oop class_data) {
|
|||||||
java_class->obj_field_put(_classData_offset, class_data);
|
java_class->obj_field_put(_classData_offset, class_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void java_lang_Class::set_reflection_data(oop java_class, oop reflection_data) {
|
||||||
|
assert(_reflectionData_offset != 0, "must be set");
|
||||||
|
java_class->obj_field_put(_reflectionData_offset, reflection_data);
|
||||||
|
}
|
||||||
|
|
||||||
void java_lang_Class::set_class_loader(oop java_class, oop loader) {
|
void java_lang_Class::set_class_loader(oop java_class, oop loader) {
|
||||||
assert(_class_loader_offset != 0, "offsets should have been initialized");
|
assert(_class_loader_offset != 0, "offsets should have been initialized");
|
||||||
java_class->obj_field_put(_class_loader_offset, loader);
|
java_class->obj_field_put(_class_loader_offset, loader);
|
||||||
@ -1493,6 +1499,7 @@ oop java_lang_Class::primitive_mirror(BasicType t) {
|
|||||||
macro(_module_offset, k, "module", module_signature, false); \
|
macro(_module_offset, k, "module", module_signature, false); \
|
||||||
macro(_name_offset, k, "name", string_signature, false); \
|
macro(_name_offset, k, "name", string_signature, false); \
|
||||||
macro(_classData_offset, k, "classData", object_signature, false); \
|
macro(_classData_offset, k, "classData", object_signature, false); \
|
||||||
|
macro(_reflectionData_offset, k, "reflectionData", java_lang_ref_SoftReference_signature, false); \
|
||||||
macro(_signers_offset, k, "signers", object_array_signature, false);
|
macro(_signers_offset, k, "signers", object_array_signature, false);
|
||||||
|
|
||||||
void java_lang_Class::compute_offsets() {
|
void java_lang_Class::compute_offsets() {
|
||||||
@ -5479,20 +5486,18 @@ void JavaClasses::serialize_offsets(SerializeClosure* soc) {
|
|||||||
bool JavaClasses::is_supported_for_archiving(oop obj) {
|
bool JavaClasses::is_supported_for_archiving(oop obj) {
|
||||||
Klass* klass = obj->klass();
|
Klass* klass = obj->klass();
|
||||||
|
|
||||||
if (klass == vmClasses::ClassLoader_klass() || // ClassLoader::loader_data is malloc'ed.
|
if (!CDSConfig::is_dumping_invokedynamic()) {
|
||||||
// The next 3 classes are used to implement java.lang.invoke, and are not used directly in
|
// These are supported by CDS only when CDSConfig::is_dumping_invokedynamic() is enabled.
|
||||||
// regular Java code. The implementation of java.lang.invoke uses generated hidden classes
|
if (klass == vmClasses::ResolvedMethodName_klass() ||
|
||||||
// (e.g., as referenced by ResolvedMethodName::vmholder) that are not yet supported by CDS.
|
klass == vmClasses::MemberName_klass() ||
|
||||||
// So for now we cannot not support these classes for archiving.
|
klass == vmClasses::Context_klass()) {
|
||||||
//
|
return false;
|
||||||
// These objects typically are not referenced by static fields, but rather by resolved
|
}
|
||||||
// constant pool entries, so excluding them shouldn't affect the archiving of static fields.
|
}
|
||||||
klass == vmClasses::ResolvedMethodName_klass() ||
|
|
||||||
klass == vmClasses::MemberName_klass() ||
|
if (klass->is_subclass_of(vmClasses::Reference_klass())) {
|
||||||
klass == vmClasses::Context_klass() ||
|
// It's problematic to archive Reference objects. One of the reasons is that
|
||||||
// It's problematic to archive Reference objects. One of the reasons is that
|
// Reference::discovered may pull in unwanted objects (see JDK-8284336)
|
||||||
// Reference::discovered may pull in unwanted objects (see JDK-8284336)
|
|
||||||
klass->is_subclass_of(vmClasses::Reference_klass())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,6 +254,7 @@ class java_lang_Class : AllStatic {
|
|||||||
static int _source_file_offset;
|
static int _source_file_offset;
|
||||||
static int _classData_offset;
|
static int _classData_offset;
|
||||||
static int _classRedefinedCount_offset;
|
static int _classRedefinedCount_offset;
|
||||||
|
static int _reflectionData_offset;
|
||||||
|
|
||||||
static bool _offsets_computed;
|
static bool _offsets_computed;
|
||||||
|
|
||||||
@ -321,6 +322,7 @@ class java_lang_Class : AllStatic {
|
|||||||
static objArrayOop signers(oop java_class);
|
static objArrayOop signers(oop java_class);
|
||||||
static oop class_data(oop java_class);
|
static oop class_data(oop java_class);
|
||||||
static void set_class_data(oop java_class, oop classData);
|
static void set_class_data(oop java_class, oop classData);
|
||||||
|
static void set_reflection_data(oop java_class, oop reflection_data);
|
||||||
|
|
||||||
static int component_mirror_offset() { return _component_mirror_offset; }
|
static int component_mirror_offset() { return _component_mirror_offset; }
|
||||||
|
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
#include "services/diagnosticCommand.hpp"
|
#include "services/diagnosticCommand.hpp"
|
||||||
#include "services/finalizerService.hpp"
|
#include "services/finalizerService.hpp"
|
||||||
#include "services/threadService.hpp"
|
#include "services/threadService.hpp"
|
||||||
|
#include "utilities/growableArray.hpp"
|
||||||
#include "utilities/macros.hpp"
|
#include "utilities/macros.hpp"
|
||||||
#include "utilities/utf8.hpp"
|
#include "utilities/utf8.hpp"
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
@ -134,19 +135,6 @@ oop SystemDictionary::java_platform_loader() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SystemDictionary::compute_java_loaders(TRAPS) {
|
void SystemDictionary::compute_java_loaders(TRAPS) {
|
||||||
if (_java_system_loader.is_empty()) {
|
|
||||||
oop system_loader = get_system_class_loader_impl(CHECK);
|
|
||||||
_java_system_loader = OopHandle(Universe::vm_global(), system_loader);
|
|
||||||
} else {
|
|
||||||
// It must have been restored from the archived module graph
|
|
||||||
assert(CDSConfig::is_using_archive(), "must be");
|
|
||||||
assert(CDSConfig::is_using_full_module_graph(), "must be");
|
|
||||||
DEBUG_ONLY(
|
|
||||||
oop system_loader = get_system_class_loader_impl(CHECK);
|
|
||||||
assert(_java_system_loader.resolve() == system_loader, "must be");
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_java_platform_loader.is_empty()) {
|
if (_java_platform_loader.is_empty()) {
|
||||||
oop platform_loader = get_platform_class_loader_impl(CHECK);
|
oop platform_loader = get_platform_class_loader_impl(CHECK);
|
||||||
_java_platform_loader = OopHandle(Universe::vm_global(), platform_loader);
|
_java_platform_loader = OopHandle(Universe::vm_global(), platform_loader);
|
||||||
@ -158,6 +146,19 @@ void SystemDictionary::compute_java_loaders(TRAPS) {
|
|||||||
oop platform_loader = get_platform_class_loader_impl(CHECK);
|
oop platform_loader = get_platform_class_loader_impl(CHECK);
|
||||||
assert(_java_platform_loader.resolve() == platform_loader, "must be");
|
assert(_java_platform_loader.resolve() == platform_loader, "must be");
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_java_system_loader.is_empty()) {
|
||||||
|
oop system_loader = get_system_class_loader_impl(CHECK);
|
||||||
|
_java_system_loader = OopHandle(Universe::vm_global(), system_loader);
|
||||||
|
} else {
|
||||||
|
// It must have been restored from the archived module graph
|
||||||
|
assert(CDSConfig::is_using_archive(), "must be");
|
||||||
|
assert(CDSConfig::is_using_full_module_graph(), "must be");
|
||||||
|
DEBUG_ONLY(
|
||||||
|
oop system_loader = get_system_class_loader_impl(CHECK);
|
||||||
|
assert(_java_system_loader.resolve() == system_loader, "must be");
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1718,6 +1719,23 @@ void SystemDictionary::update_dictionary(JavaThread* current,
|
|||||||
mu1.notify_all();
|
mu1.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if INCLUDE_CDS
|
||||||
|
// Indicate that loader_data has initiated the loading of class k, which
|
||||||
|
// has already been defined by a parent loader.
|
||||||
|
// This API should be used only by AOTLinkedClassBulkLoader
|
||||||
|
void SystemDictionary::add_to_initiating_loader(JavaThread* current,
|
||||||
|
InstanceKlass* k,
|
||||||
|
ClassLoaderData* loader_data) {
|
||||||
|
assert(CDSConfig::is_using_aot_linked_classes(), "must be");
|
||||||
|
assert_locked_or_safepoint(SystemDictionary_lock);
|
||||||
|
Symbol* name = k->name();
|
||||||
|
Dictionary* dictionary = loader_data->dictionary();
|
||||||
|
assert(k->is_loaded(), "must be");
|
||||||
|
assert(k->class_loader_data() != loader_data, "only for classes defined by a parent loader");
|
||||||
|
assert(dictionary->find_class(current, name) == nullptr, "sanity");
|
||||||
|
dictionary->add_klass(current, name, k);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Try to find a class name using the loader constraints. The
|
// Try to find a class name using the loader constraints. The
|
||||||
// loader constraints might know about a class that isn't fully loaded
|
// loader constraints might know about a class that isn't fully loaded
|
||||||
@ -2033,6 +2051,52 @@ Method* SystemDictionary::find_method_handle_intrinsic(vmIntrinsicID iid,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if INCLUDE_CDS
|
||||||
|
void SystemDictionary::get_all_method_handle_intrinsics(GrowableArray<Method*>* methods) {
|
||||||
|
assert(SafepointSynchronize::is_at_safepoint(), "must be");
|
||||||
|
auto do_method = [&] (InvokeMethodKey& key, Method*& m) {
|
||||||
|
methods->append(m);
|
||||||
|
};
|
||||||
|
_invoke_method_intrinsic_table->iterate_all(do_method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemDictionary::restore_archived_method_handle_intrinsics() {
|
||||||
|
if (UseSharedSpaces) {
|
||||||
|
EXCEPTION_MARK;
|
||||||
|
restore_archived_method_handle_intrinsics_impl(THREAD);
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
// This is probably caused by OOM -- other parts of the CDS archive have direct pointers to
|
||||||
|
// the archived method handle intrinsics, so we can't really recover from this failure.
|
||||||
|
vm_exit_during_initialization(err_msg("Failed to restore archived method handle intrinsics. Try to increase heap size."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemDictionary::restore_archived_method_handle_intrinsics_impl(TRAPS) {
|
||||||
|
Array<Method*>* list = MetaspaceShared::archived_method_handle_intrinsics();
|
||||||
|
for (int i = 0; i < list->length(); i++) {
|
||||||
|
methodHandle m(THREAD, list->at(i));
|
||||||
|
Method::restore_archived_method_handle_intrinsic(m, CHECK);
|
||||||
|
m->constants()->restore_unshareable_info(CHECK);
|
||||||
|
if (!Arguments::is_interpreter_only() || m->intrinsic_id() == vmIntrinsics::_linkToNative) {
|
||||||
|
AdapterHandlerLibrary::create_native_wrapper(m);
|
||||||
|
if (!m->has_compiled_code()) {
|
||||||
|
ResourceMark rm(THREAD);
|
||||||
|
vm_exit_during_initialization(err_msg("Failed to initialize method %s", m->external_name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no need to grab the InvokeMethodIntrinsicTable_lock, as we are still very early in
|
||||||
|
// VM start-up -- in init_globals2() -- so we are still running a single Java thread. It's not
|
||||||
|
// possible to have a contention.
|
||||||
|
const int iid_as_int = vmIntrinsics::as_int(m->intrinsic_id());
|
||||||
|
InvokeMethodKey key(m->signature(), iid_as_int);
|
||||||
|
bool created = _invoke_method_intrinsic_table->put(key, m());
|
||||||
|
assert(created, "unexpected contention");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // INCLUDE_CDS
|
||||||
|
|
||||||
// Helper for unpacking the return value from linkMethod and linkCallSite.
|
// Helper for unpacking the return value from linkMethod and linkCallSite.
|
||||||
static Method* unpack_method_and_appendix(Handle mname,
|
static Method* unpack_method_and_appendix(Handle mname,
|
||||||
Klass* accessing_klass,
|
Klass* accessing_klass,
|
||||||
|
@ -75,7 +75,10 @@ class GCTimer;
|
|||||||
class EventClassLoad;
|
class EventClassLoad;
|
||||||
class Symbol;
|
class Symbol;
|
||||||
|
|
||||||
|
template <class E> class GrowableArray;
|
||||||
|
|
||||||
class SystemDictionary : AllStatic {
|
class SystemDictionary : AllStatic {
|
||||||
|
friend class AOTLinkedClassBulkLoader;
|
||||||
friend class BootstrapInfo;
|
friend class BootstrapInfo;
|
||||||
friend class vmClasses;
|
friend class vmClasses;
|
||||||
friend class VMStructs;
|
friend class VMStructs;
|
||||||
@ -239,6 +242,9 @@ public:
|
|||||||
Symbol* signature,
|
Symbol* signature,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
|
||||||
|
static void get_all_method_handle_intrinsics(GrowableArray<Method*>* methods) NOT_CDS_RETURN;
|
||||||
|
static void restore_archived_method_handle_intrinsics() NOT_CDS_RETURN;
|
||||||
|
|
||||||
// compute java_mirror (java.lang.Class instance) for a type ("I", "[[B", "LFoo;", etc.)
|
// compute java_mirror (java.lang.Class instance) for a type ("I", "[[B", "LFoo;", etc.)
|
||||||
// Either the accessing_klass or the CL/PD can be non-null, but not both.
|
// Either the accessing_klass or the CL/PD can be non-null, but not both.
|
||||||
static Handle find_java_mirror_for_type(Symbol* signature,
|
static Handle find_java_mirror_for_type(Symbol* signature,
|
||||||
@ -293,6 +299,9 @@ public:
|
|||||||
const char* message);
|
const char* message);
|
||||||
static const char* find_nest_host_error(const constantPoolHandle& pool, int which);
|
static const char* find_nest_host_error(const constantPoolHandle& pool, int which);
|
||||||
|
|
||||||
|
static void add_to_initiating_loader(JavaThread* current, InstanceKlass* k,
|
||||||
|
ClassLoaderData* loader_data) NOT_CDS_RETURN;
|
||||||
|
|
||||||
static OopHandle _java_system_loader;
|
static OopHandle _java_system_loader;
|
||||||
static OopHandle _java_platform_loader;
|
static OopHandle _java_platform_loader;
|
||||||
|
|
||||||
@ -331,6 +340,7 @@ private:
|
|||||||
Handle protection_domain, TRAPS);
|
Handle protection_domain, TRAPS);
|
||||||
// Second part of load_shared_class
|
// Second part of load_shared_class
|
||||||
static void load_shared_class_misc(InstanceKlass* ik, ClassLoaderData* loader_data) NOT_CDS_RETURN;
|
static void load_shared_class_misc(InstanceKlass* ik, ClassLoaderData* loader_data) NOT_CDS_RETURN;
|
||||||
|
static void restore_archived_method_handle_intrinsics_impl(TRAPS) NOT_CDS_RETURN;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Used by SystemDictionaryShared
|
// Used by SystemDictionaryShared
|
||||||
|
@ -27,12 +27,13 @@
|
|||||||
#include "cds/archiveHeapLoader.hpp"
|
#include "cds/archiveHeapLoader.hpp"
|
||||||
#include "cds/archiveUtils.hpp"
|
#include "cds/archiveUtils.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
|
#include "cds/cdsProtectionDomain.hpp"
|
||||||
#include "cds/classListParser.hpp"
|
#include "cds/classListParser.hpp"
|
||||||
#include "cds/classListWriter.hpp"
|
#include "cds/classListWriter.hpp"
|
||||||
|
#include "cds/dumpTimeClassInfo.inline.hpp"
|
||||||
#include "cds/dynamicArchive.hpp"
|
#include "cds/dynamicArchive.hpp"
|
||||||
#include "cds/filemap.hpp"
|
#include "cds/filemap.hpp"
|
||||||
#include "cds/cdsProtectionDomain.hpp"
|
#include "cds/heapShared.hpp"
|
||||||
#include "cds/dumpTimeClassInfo.inline.hpp"
|
|
||||||
#include "cds/metaspaceShared.hpp"
|
#include "cds/metaspaceShared.hpp"
|
||||||
#include "cds/runTimeClassInfo.hpp"
|
#include "cds/runTimeClassInfo.hpp"
|
||||||
#include "classfile/classFileStream.hpp"
|
#include "classfile/classFileStream.hpp"
|
||||||
@ -204,6 +205,15 @@ DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SystemDictionaryShared::mark_required_hidden_class(InstanceKlass* k) {
|
||||||
|
assert(k->is_hidden(), "sanity");
|
||||||
|
DumpTimeClassInfo* info = _dumptime_table->get(k);
|
||||||
|
ResourceMark rm;
|
||||||
|
if (info != nullptr) {
|
||||||
|
info->set_is_required_hidden_class();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
|
bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||||
if (MetaspaceShared::is_in_shared_metaspace(k)) {
|
if (MetaspaceShared::is_in_shared_metaspace(k)) {
|
||||||
// We have reached a super type that's already in the base archive. Treat it
|
// We have reached a super type that's already in the base archive. Treat it
|
||||||
@ -287,9 +297,13 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
|||||||
if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) {
|
if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) {
|
||||||
if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) {
|
if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) {
|
||||||
// This class is dynamically generated by the JDK
|
// This class is dynamically generated by the JDK
|
||||||
ResourceMark rm;
|
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||||
log_info(cds)("Skipping %s because it is dynamically generated", k->name()->as_C_string());
|
k->set_shared_classpath_index(0);
|
||||||
return true; // exclude without warning
|
} else {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_info(cds)("Skipping %s because it is dynamically generated", k->name()->as_C_string());
|
||||||
|
return true; // exclude without warning
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// These are classes loaded from unsupported locations (such as those loaded by JVMTI native
|
// These are classes loaded from unsupported locations (such as those loaded by JVMTI native
|
||||||
// agent during dump time).
|
// agent during dump time).
|
||||||
@ -326,9 +340,8 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k->is_hidden() && !is_registered_lambda_proxy_class(k)) {
|
if (k->is_hidden() && !should_hidden_class_be_archived(k)) {
|
||||||
ResourceMark rm;
|
log_info(cds)("Skipping %s: Hidden class", k->name()->as_C_string());
|
||||||
log_debug(cds)("Skipping %s: Hidden class", k->name()->as_C_string());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +601,11 @@ void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) {
|
|||||||
guarantee(!info->is_excluded(), "Should not attempt to archive excluded class %s", name);
|
guarantee(!info->is_excluded(), "Should not attempt to archive excluded class %s", name);
|
||||||
if (is_builtin(k)) {
|
if (is_builtin(k)) {
|
||||||
if (k->is_hidden()) {
|
if (k->is_hidden()) {
|
||||||
assert(is_registered_lambda_proxy_class(k), "unexpected hidden class %s", name);
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
assert(should_hidden_class_be_archived(k), "unexpected hidden class %s", name);
|
||||||
|
} else {
|
||||||
|
assert(is_registered_lambda_proxy_class(k), "unexpected hidden class %s", name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
guarantee(!k->is_shared_unregistered_class(),
|
guarantee(!k->is_shared_unregistered_class(),
|
||||||
"Class loader type must be set for BUILTIN class %s", name);
|
"Class loader type must be set for BUILTIN class %s", name);
|
||||||
@ -640,7 +657,93 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void SystemDictionaryShared::check_excluded_classes() {
|
void SystemDictionaryShared::scan_constant_pool(InstanceKlass* k) {
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
k->constants()->find_required_hidden_classes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemDictionaryShared::should_hidden_class_be_archived(InstanceKlass* k) {
|
||||||
|
assert(k->is_hidden(), "sanity");
|
||||||
|
if (is_registered_lambda_proxy_class(k)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
DumpTimeClassInfo* info = _dumptime_table->get(k);
|
||||||
|
if (info != nullptr && info->is_required_hidden_class()) {
|
||||||
|
guarantee(HeapShared::is_archivable_hidden_klass(k), "required hidden class must be archivable");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the class should be excluded. This can be called by
|
||||||
|
// AOTConstantPoolResolver before or after we enter the CDS safepoint.
|
||||||
|
// When called before the safepoint, we need to link the class so that
|
||||||
|
// it can be checked by check_for_exclusion().
|
||||||
|
bool SystemDictionaryShared::should_be_excluded(Klass* k) {
|
||||||
|
assert(CDSConfig::is_dumping_archive(), "sanity");
|
||||||
|
assert(CDSConfig::current_thread_is_vm_or_dumper(), "sanity");
|
||||||
|
|
||||||
|
if (k->is_objArray_klass()) {
|
||||||
|
return should_be_excluded(ObjArrayKlass::cast(k)->bottom_klass());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!k->is_instance_klass()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||||
|
|
||||||
|
if (!SafepointSynchronize::is_at_safepoint()) {
|
||||||
|
if (!ik->is_linked()) {
|
||||||
|
// check_for_exclusion() below doesn't link unlinked classes. We come
|
||||||
|
// here only when we are trying to aot-link constant pool entries, so
|
||||||
|
// we'd better link the class.
|
||||||
|
JavaThread* THREAD = JavaThread::current();
|
||||||
|
ik->link_class(THREAD);
|
||||||
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
|
CLEAR_PENDING_EXCEPTION;
|
||||||
|
return true; // linking failed -- let's exclude it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
DumpTimeClassInfo* p = get_info_locked(ik);
|
||||||
|
if (p->is_excluded()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return check_for_exclusion(ik, p);
|
||||||
|
} else {
|
||||||
|
// No need to check for is_linked() as all eligible classes should have
|
||||||
|
// already been linked in MetaspaceShared::link_class_for_cds().
|
||||||
|
// Can't take the lock as we are in safepoint.
|
||||||
|
DumpTimeClassInfo* p = _dumptime_table->get(ik);
|
||||||
|
if (p->is_excluded()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return check_for_exclusion(ik, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemDictionaryShared::find_all_archivable_classes() {
|
||||||
|
HeapShared::start_finding_required_hidden_classes();
|
||||||
|
find_all_archivable_classes_impl();
|
||||||
|
HeapShared::end_finding_required_hidden_classes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all the classes in _dumptime_table, marking the ones that must be
|
||||||
|
// excluded from the archive. Those that are not excluded will be archivable.
|
||||||
|
//
|
||||||
|
// (a) Non-hidden classes are easy. They are only check by the rules in
|
||||||
|
// SystemDictionaryShared::check_for_exclusion().
|
||||||
|
// (b) For hidden classes, we only archive those that are required (i.e., they are
|
||||||
|
// referenced by Java objects (such as CallSites) that are reachable from
|
||||||
|
// ConstantPools). This needs help from HeapShared.
|
||||||
|
void SystemDictionaryShared::find_all_archivable_classes_impl() {
|
||||||
assert(!class_loading_may_happen(), "class loading must be disabled");
|
assert(!class_loading_may_happen(), "class loading must be disabled");
|
||||||
assert_lock_strong(DumpTimeTable_lock);
|
assert_lock_strong(DumpTimeTable_lock);
|
||||||
|
|
||||||
@ -653,10 +756,56 @@ void SystemDictionaryShared::check_excluded_classes() {
|
|||||||
dup_checker.mark_duplicated_classes();
|
dup_checker.mark_duplicated_classes();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto check_for_exclusion = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
|
ResourceMark rm;
|
||||||
SystemDictionaryShared::check_for_exclusion(k, &info);
|
|
||||||
|
// First, scan all non-hidden classes
|
||||||
|
auto check_non_hidden = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
|
||||||
|
if (!k->is_hidden()) {
|
||||||
|
SystemDictionaryShared::check_for_exclusion(k, &info);
|
||||||
|
if (!info.is_excluded() && !info.has_scanned_constant_pool()) {
|
||||||
|
scan_constant_pool(k);
|
||||||
|
info.set_has_scanned_constant_pool();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
_dumptime_table->iterate_all_live_classes(check_for_exclusion);
|
_dumptime_table->iterate_all_live_classes(check_non_hidden);
|
||||||
|
|
||||||
|
// Then, scan all the hidden classes that have been marked as required to
|
||||||
|
// discover more hidden classes. Stop when we cannot make progress anymore.
|
||||||
|
bool made_progress;
|
||||||
|
do {
|
||||||
|
made_progress = false;
|
||||||
|
auto check_hidden = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
|
||||||
|
if (k->is_hidden() && should_hidden_class_be_archived(k)) {
|
||||||
|
SystemDictionaryShared::check_for_exclusion(k, &info);
|
||||||
|
if (info.is_excluded()) {
|
||||||
|
guarantee(!info.is_required_hidden_class(), "A required hidden class cannot be marked as excluded");
|
||||||
|
} else if (!info.has_scanned_constant_pool()) {
|
||||||
|
scan_constant_pool(k);
|
||||||
|
info.set_has_scanned_constant_pool();
|
||||||
|
// The CP entries in k *MAY* refer to other hidden classes, so scan
|
||||||
|
// every hidden class again.
|
||||||
|
made_progress = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_dumptime_table->iterate_all_live_classes(check_hidden);
|
||||||
|
} while (made_progress);
|
||||||
|
|
||||||
|
// Now, all hidden classes that have not yet been scanned must be marked as excluded
|
||||||
|
auto exclude_remaining_hidden = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
|
||||||
|
if (k->is_hidden()) {
|
||||||
|
SystemDictionaryShared::check_for_exclusion(k, &info);
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
if (should_hidden_class_be_archived(k)) {
|
||||||
|
guarantee(!info.is_excluded(), "Must be");
|
||||||
|
} else {
|
||||||
|
guarantee(info.is_excluded(), "Must be");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_dumptime_table->iterate_all_live_classes(exclude_remaining_hidden);
|
||||||
_dumptime_table->update_counts();
|
_dumptime_table->update_counts();
|
||||||
|
|
||||||
cleanup_lambda_proxy_class_dictionary();
|
cleanup_lambda_proxy_class_dictionary();
|
||||||
@ -766,6 +915,11 @@ void SystemDictionaryShared::add_lambda_proxy_class(InstanceKlass* caller_ik,
|
|||||||
Method* member_method,
|
Method* member_method,
|
||||||
Symbol* instantiated_method_type,
|
Symbol* instantiated_method_type,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
// The lambda proxy classes will be stored as part of aot-resolved constant pool entries.
|
||||||
|
// There's no need to remember them in a separate table.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(caller_ik->class_loader() == lambda_ik->class_loader(), "mismatched class loader");
|
assert(caller_ik->class_loader() == lambda_ik->class_loader(), "mismatched class loader");
|
||||||
assert(caller_ik->class_loader_data() == lambda_ik->class_loader_data(), "mismatched class loader data");
|
assert(caller_ik->class_loader_data() == lambda_ik->class_loader_data(), "mismatched class loader data");
|
||||||
@ -1188,7 +1342,7 @@ public:
|
|||||||
// In static dump, info._proxy_klasses->at(0) is already relocated to point to the archived class
|
// In static dump, info._proxy_klasses->at(0) is already relocated to point to the archived class
|
||||||
// (not the original class).
|
// (not the original class).
|
||||||
//
|
//
|
||||||
// The following check has been moved to SystemDictionaryShared::check_excluded_classes(), which
|
// The following check has been moved to SystemDictionaryShared::find_all_archivable_classes(), which
|
||||||
// happens before the classes are copied.
|
// happens before the classes are copied.
|
||||||
//
|
//
|
||||||
// if (SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) {
|
// if (SystemDictionaryShared::is_excluded_class(info._proxy_klasses->at(0))) {
|
||||||
|
@ -188,6 +188,7 @@ private:
|
|||||||
static DumpTimeClassInfo* get_info(InstanceKlass* k);
|
static DumpTimeClassInfo* get_info(InstanceKlass* k);
|
||||||
static DumpTimeClassInfo* get_info_locked(InstanceKlass* k);
|
static DumpTimeClassInfo* get_info_locked(InstanceKlass* k);
|
||||||
|
|
||||||
|
static void find_all_archivable_classes_impl();
|
||||||
static void write_dictionary(RunTimeSharedDictionary* dictionary,
|
static void write_dictionary(RunTimeSharedDictionary* dictionary,
|
||||||
bool is_builtin);
|
bool is_builtin);
|
||||||
static void write_lambda_proxy_class_dictionary(LambdaProxyClassDictionary* dictionary);
|
static void write_lambda_proxy_class_dictionary(LambdaProxyClassDictionary* dictionary);
|
||||||
@ -199,10 +200,12 @@ private:
|
|||||||
static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
||||||
static bool has_been_redefined(InstanceKlass* k);
|
static bool has_been_redefined(InstanceKlass* k);
|
||||||
static InstanceKlass* retrieve_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info) NOT_CDS_RETURN_(nullptr);
|
static InstanceKlass* retrieve_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info) NOT_CDS_RETURN_(nullptr);
|
||||||
|
static void scan_constant_pool(InstanceKlass* k);
|
||||||
DEBUG_ONLY(static bool _class_loading_may_happen;)
|
DEBUG_ONLY(static bool _class_loading_may_happen;)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static bool should_hidden_class_be_archived(InstanceKlass* k);
|
||||||
|
static void mark_required_hidden_class(InstanceKlass* k);
|
||||||
static bool is_hidden_lambda_proxy(InstanceKlass* ik);
|
static bool is_hidden_lambda_proxy(InstanceKlass* ik);
|
||||||
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
|
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
|
||||||
static bool has_archived_enum_objs(InstanceKlass* ik);
|
static bool has_archived_enum_objs(InstanceKlass* ik);
|
||||||
@ -288,7 +291,8 @@ public:
|
|||||||
}
|
}
|
||||||
static bool add_unregistered_class(Thread* current, InstanceKlass* k);
|
static bool add_unregistered_class(Thread* current, InstanceKlass* k);
|
||||||
|
|
||||||
static void check_excluded_classes();
|
static void find_all_archivable_classes();
|
||||||
|
static bool should_be_excluded(Klass* k);
|
||||||
static bool check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
|
static bool check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
|
||||||
static void validate_before_archiving(InstanceKlass* k);
|
static void validate_before_archiving(InstanceKlass* k);
|
||||||
static bool is_excluded_class(InstanceKlass* k);
|
static bool is_excluded_class(InstanceKlass* k);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -175,6 +175,7 @@
|
|||||||
do_klass(Short_klass, java_lang_Short ) \
|
do_klass(Short_klass, java_lang_Short ) \
|
||||||
do_klass(Integer_klass, java_lang_Integer ) \
|
do_klass(Integer_klass, java_lang_Integer ) \
|
||||||
do_klass(Long_klass, java_lang_Long ) \
|
do_klass(Long_klass, java_lang_Long ) \
|
||||||
|
do_klass(Void_klass, java_lang_Void ) \
|
||||||
\
|
\
|
||||||
/* force inline of iterators */ \
|
/* force inline of iterators */ \
|
||||||
do_klass(Iterator_klass, java_util_Iterator ) \
|
do_klass(Iterator_klass, java_util_Iterator ) \
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||||
#include "cds/archiveHeapLoader.hpp"
|
#include "cds/archiveHeapLoader.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "classfile/classLoader.hpp"
|
#include "classfile/classLoader.hpp"
|
||||||
@ -210,6 +211,9 @@ void vmClasses::resolve_all(TRAPS) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
InstanceStackChunkKlass::init_offset_of_stack();
|
InstanceStackChunkKlass::init_offset_of_stack();
|
||||||
|
if (CDSConfig::is_using_aot_linked_classes()) {
|
||||||
|
AOTLinkedClassBulkLoader::load_javabase_classes(THREAD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
|
@ -89,6 +89,7 @@ class SerializeClosure;
|
|||||||
template(java_lang_Integer_IntegerCache, "java/lang/Integer$IntegerCache") \
|
template(java_lang_Integer_IntegerCache, "java/lang/Integer$IntegerCache") \
|
||||||
template(java_lang_Long, "java/lang/Long") \
|
template(java_lang_Long, "java/lang/Long") \
|
||||||
template(java_lang_Long_LongCache, "java/lang/Long$LongCache") \
|
template(java_lang_Long_LongCache, "java/lang/Long$LongCache") \
|
||||||
|
template(java_lang_Void, "java/lang/Void") \
|
||||||
\
|
\
|
||||||
template(jdk_internal_vm_vector_VectorSupport, "jdk/internal/vm/vector/VectorSupport") \
|
template(jdk_internal_vm_vector_VectorSupport, "jdk/internal/vm/vector/VectorSupport") \
|
||||||
template(jdk_internal_vm_vector_VectorPayload, "jdk/internal/vm/vector/VectorSupport$VectorPayload") \
|
template(jdk_internal_vm_vector_VectorPayload, "jdk/internal/vm/vector/VectorSupport$VectorPayload") \
|
||||||
@ -309,6 +310,8 @@ class SerializeClosure;
|
|||||||
template(jdk_internal_vm_annotation_JvmtiHideEvents_signature, "Ljdk/internal/vm/annotation/JvmtiHideEvents;") \
|
template(jdk_internal_vm_annotation_JvmtiHideEvents_signature, "Ljdk/internal/vm/annotation/JvmtiHideEvents;") \
|
||||||
template(jdk_internal_vm_annotation_JvmtiMountTransition_signature, "Ljdk/internal/vm/annotation/JvmtiMountTransition;") \
|
template(jdk_internal_vm_annotation_JvmtiMountTransition_signature, "Ljdk/internal/vm/annotation/JvmtiMountTransition;") \
|
||||||
\
|
\
|
||||||
|
template(java_lang_ref_SoftReference_signature, "Ljava/lang/ref/SoftReference;") \
|
||||||
|
\
|
||||||
/* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \
|
/* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \
|
||||||
template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \
|
template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \
|
||||||
template(java_lang_invoke_ConstantCallSite, "java/lang/invoke/ConstantCallSite") \
|
template(java_lang_invoke_ConstantCallSite, "java/lang/invoke/ConstantCallSite") \
|
||||||
@ -726,6 +729,7 @@ class SerializeClosure;
|
|||||||
JFR_TEMPLATES(template) \
|
JFR_TEMPLATES(template) \
|
||||||
\
|
\
|
||||||
/* CDS */ \
|
/* CDS */ \
|
||||||
|
template(createArchivedObjects, "createArchivedObjects") \
|
||||||
template(dumpSharedArchive, "dumpSharedArchive") \
|
template(dumpSharedArchive, "dumpSharedArchive") \
|
||||||
template(dumpSharedArchive_signature, "(ZLjava/lang/String;)Ljava/lang/String;") \
|
template(dumpSharedArchive_signature, "(ZLjava/lang/String;)Ljava/lang/String;") \
|
||||||
template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \
|
template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \
|
||||||
@ -739,6 +743,7 @@ class SerializeClosure;
|
|||||||
template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \
|
template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \
|
||||||
template(java_util_concurrent_ConcurrentHashMap, "java/util/concurrent/ConcurrentHashMap") \
|
template(java_util_concurrent_ConcurrentHashMap, "java/util/concurrent/ConcurrentHashMap") \
|
||||||
template(java_util_ArrayList, "java/util/ArrayList") \
|
template(java_util_ArrayList, "java/util/ArrayList") \
|
||||||
|
template(runtimeSetup, "runtimeSetup") \
|
||||||
template(toFileURL_name, "toFileURL") \
|
template(toFileURL_name, "toFileURL") \
|
||||||
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
|
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
|
||||||
template(url_array_classloader_void_signature, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V") \
|
template(url_array_classloader_void_signature, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V") \
|
||||||
|
@ -949,6 +949,15 @@ void InterpreterRuntime::resolve_invokehandle(JavaThread* current) {
|
|||||||
pool->cache()->set_method_handle(method_index, info);
|
pool->cache()->set_method_handle(method_index, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterpreterRuntime::cds_resolve_invokehandle(int raw_index,
|
||||||
|
constantPoolHandle& pool, TRAPS) {
|
||||||
|
const Bytecodes::Code bytecode = Bytecodes::_invokehandle;
|
||||||
|
CallInfo info;
|
||||||
|
LinkResolver::resolve_invoke(info, Handle(), pool, raw_index, bytecode, CHECK);
|
||||||
|
|
||||||
|
pool->cache()->set_method_handle(raw_index, info);
|
||||||
|
}
|
||||||
|
|
||||||
// First time execution: Resolve symbols, create a permanent CallSite object.
|
// First time execution: Resolve symbols, create a permanent CallSite object.
|
||||||
void InterpreterRuntime::resolve_invokedynamic(JavaThread* current) {
|
void InterpreterRuntime::resolve_invokedynamic(JavaThread* current) {
|
||||||
LastFrameAccessor last_frame(current);
|
LastFrameAccessor last_frame(current);
|
||||||
@ -968,6 +977,14 @@ void InterpreterRuntime::resolve_invokedynamic(JavaThread* current) {
|
|||||||
pool->cache()->set_dynamic_call(info, index);
|
pool->cache()->set_dynamic_call(info, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterpreterRuntime::cds_resolve_invokedynamic(int raw_index,
|
||||||
|
constantPoolHandle& pool, TRAPS) {
|
||||||
|
const Bytecodes::Code bytecode = Bytecodes::_invokedynamic;
|
||||||
|
CallInfo info;
|
||||||
|
LinkResolver::resolve_invoke(info, Handle(), pool, raw_index, bytecode, CHECK);
|
||||||
|
pool->cache()->set_dynamic_call(info, raw_index);
|
||||||
|
}
|
||||||
|
|
||||||
// This function is the interface to the assembly code. It returns the resolved
|
// This function is the interface to the assembly code. It returns the resolved
|
||||||
// cpCache entry. This doesn't safepoint, but the helper routines safepoint.
|
// cpCache entry. This doesn't safepoint, but the helper routines safepoint.
|
||||||
// This function will check for redefinition!
|
// This function will check for redefinition!
|
||||||
|
@ -92,12 +92,15 @@ class InterpreterRuntime: AllStatic {
|
|||||||
|
|
||||||
static void resolve_from_cache(JavaThread* current, Bytecodes::Code bytecode);
|
static void resolve_from_cache(JavaThread* current, Bytecodes::Code bytecode);
|
||||||
|
|
||||||
// Used by ClassPrelinker
|
// Used by AOTConstantPoolResolver
|
||||||
static void resolve_get_put(Bytecodes::Code bytecode, int field_index,
|
static void resolve_get_put(Bytecodes::Code bytecode, int field_index,
|
||||||
methodHandle& m, constantPoolHandle& pool, bool initialize_holder, TRAPS);
|
methodHandle& m, constantPoolHandle& pool, bool initialize_holder, TRAPS);
|
||||||
static void cds_resolve_invoke(Bytecodes::Code bytecode, int method_index,
|
static void cds_resolve_invoke(Bytecodes::Code bytecode, int method_index,
|
||||||
constantPoolHandle& pool, TRAPS);
|
constantPoolHandle& pool, TRAPS);
|
||||||
|
static void cds_resolve_invokehandle(int raw_index,
|
||||||
|
constantPoolHandle& pool, TRAPS);
|
||||||
|
static void cds_resolve_invokedynamic(int raw_index,
|
||||||
|
constantPoolHandle& pool, TRAPS);
|
||||||
private:
|
private:
|
||||||
// Statics & fields
|
// Statics & fields
|
||||||
static void resolve_get_put(JavaThread* current, Bytecodes::Code bytecode);
|
static void resolve_get_put(JavaThread* current, Bytecodes::Code bytecode);
|
||||||
|
@ -38,6 +38,7 @@ class outputStream;
|
|||||||
LOG_TAG(age) \
|
LOG_TAG(age) \
|
||||||
LOG_TAG(alloc) \
|
LOG_TAG(alloc) \
|
||||||
LOG_TAG(annotation) \
|
LOG_TAG(annotation) \
|
||||||
|
LOG_TAG(aot) \
|
||||||
LOG_TAG(arguments) \
|
LOG_TAG(arguments) \
|
||||||
LOG_TAG(array) \
|
LOG_TAG(array) \
|
||||||
LOG_TAG(attach) \
|
LOG_TAG(attach) \
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "memory/iterator.hpp"
|
#include "memory/iterator.hpp"
|
||||||
|
|
||||||
|
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||||
#include "classfile/classLoaderData.hpp"
|
#include "classfile/classLoaderData.hpp"
|
||||||
#include "code/nmethod.hpp"
|
#include "code/nmethod.hpp"
|
||||||
#include "oops/access.inline.hpp"
|
#include "oops/access.inline.hpp"
|
||||||
@ -51,7 +52,11 @@ inline void ClaimMetadataVisitingOopIterateClosure::do_cld(ClassLoaderData* cld)
|
|||||||
|
|
||||||
inline void ClaimMetadataVisitingOopIterateClosure::do_klass(Klass* k) {
|
inline void ClaimMetadataVisitingOopIterateClosure::do_klass(Klass* k) {
|
||||||
ClassLoaderData* cld = k->class_loader_data();
|
ClassLoaderData* cld = k->class_loader_data();
|
||||||
ClaimMetadataVisitingOopIterateClosure::do_cld(cld);
|
if (cld != nullptr) {
|
||||||
|
ClaimMetadataVisitingOopIterateClosure::do_cld(cld);
|
||||||
|
} else {
|
||||||
|
assert(AOTLinkedClassBulkLoader::is_pending_aot_linked_class(k), "sanity");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ClaimMetadataVisitingOopIterateClosure::do_nmethod(nmethod* nm) {
|
inline void ClaimMetadataVisitingOopIterateClosure::do_nmethod(nmethod* nm) {
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "cds/archiveHeapWriter.hpp"
|
#include "cds/aotConstantPoolResolver.hpp"
|
||||||
#include "cds/archiveHeapLoader.hpp"
|
|
||||||
#include "cds/archiveBuilder.hpp"
|
#include "cds/archiveBuilder.hpp"
|
||||||
|
#include "cds/archiveHeapLoader.hpp"
|
||||||
|
#include "cds/archiveHeapWriter.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "cds/classPrelinker.hpp"
|
|
||||||
#include "cds/heapShared.hpp"
|
#include "cds/heapShared.hpp"
|
||||||
#include "classfile/classLoader.hpp"
|
#include "classfile/classLoader.hpp"
|
||||||
#include "classfile/classLoaderData.hpp"
|
#include "classfile/classLoaderData.hpp"
|
||||||
@ -35,6 +35,7 @@
|
|||||||
#include "classfile/metadataOnStackMark.hpp"
|
#include "classfile/metadataOnStackMark.hpp"
|
||||||
#include "classfile/stringTable.hpp"
|
#include "classfile/stringTable.hpp"
|
||||||
#include "classfile/systemDictionary.hpp"
|
#include "classfile/systemDictionary.hpp"
|
||||||
|
#include "classfile/systemDictionaryShared.hpp"
|
||||||
#include "classfile/vmClasses.hpp"
|
#include "classfile/vmClasses.hpp"
|
||||||
#include "classfile/vmSymbols.hpp"
|
#include "classfile/vmSymbols.hpp"
|
||||||
#include "code/codeCache.hpp"
|
#include "code/codeCache.hpp"
|
||||||
@ -283,6 +284,44 @@ void ConstantPool::klass_at_put(int class_index, Klass* k) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS_JAVA_HEAP
|
#if INCLUDE_CDS_JAVA_HEAP
|
||||||
|
template <typename Function>
|
||||||
|
void ConstantPool::iterate_archivable_resolved_references(Function function) {
|
||||||
|
objArrayOop rr = resolved_references();
|
||||||
|
if (rr != nullptr && cache() != nullptr && CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
Array<ResolvedIndyEntry>* indy_entries = cache()->resolved_indy_entries();
|
||||||
|
if (indy_entries != nullptr) {
|
||||||
|
for (int i = 0; i < indy_entries->length(); i++) {
|
||||||
|
ResolvedIndyEntry *rie = indy_entries->adr_at(i);
|
||||||
|
if (rie->is_resolved() && AOTConstantPoolResolver::is_resolution_deterministic(this, rie->constant_pool_index())) {
|
||||||
|
int rr_index = rie->resolved_references_index();
|
||||||
|
assert(resolved_reference_at(rr_index) != nullptr, "must exist");
|
||||||
|
function(rr_index);
|
||||||
|
|
||||||
|
// Save the BSM as well (sometimes the JIT looks up the BSM it for replay)
|
||||||
|
int indy_cp_index = rie->constant_pool_index();
|
||||||
|
int bsm_mh_cp_index = bootstrap_method_ref_index_at(indy_cp_index);
|
||||||
|
int bsm_rr_index = cp_to_object_index(bsm_mh_cp_index);
|
||||||
|
assert(resolved_reference_at(bsm_rr_index) != nullptr, "must exist");
|
||||||
|
function(bsm_rr_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<ResolvedMethodEntry>* method_entries = cache()->resolved_method_entries();
|
||||||
|
if (method_entries != nullptr) {
|
||||||
|
for (int i = 0; i < method_entries->length(); i++) {
|
||||||
|
ResolvedMethodEntry* rme = method_entries->adr_at(i);
|
||||||
|
if (rme->is_resolved(Bytecodes::_invokehandle) && rme->has_appendix() &&
|
||||||
|
cache()->can_archive_resolved_method(this, rme)) {
|
||||||
|
int rr_index = rme->resolved_references_index();
|
||||||
|
assert(resolved_reference_at(rr_index) != nullptr, "must exist");
|
||||||
|
function(rr_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the _resolved_reference array after removing unarchivable items from it.
|
// Returns the _resolved_reference array after removing unarchivable items from it.
|
||||||
// Returns null if this class is not supported, or _resolved_reference doesn't exist.
|
// Returns null if this class is not supported, or _resolved_reference doesn't exist.
|
||||||
objArrayOop ConstantPool::prepare_resolved_references_for_archiving() {
|
objArrayOop ConstantPool::prepare_resolved_references_for_archiving() {
|
||||||
@ -300,8 +339,15 @@ objArrayOop ConstantPool::prepare_resolved_references_for_archiving() {
|
|||||||
|
|
||||||
objArrayOop rr = resolved_references();
|
objArrayOop rr = resolved_references();
|
||||||
if (rr != nullptr) {
|
if (rr != nullptr) {
|
||||||
|
ResourceMark rm;
|
||||||
int rr_len = rr->length();
|
int rr_len = rr->length();
|
||||||
|
GrowableArray<bool> keep_resolved_refs(rr_len, rr_len, false);
|
||||||
|
|
||||||
ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this);
|
ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this);
|
||||||
|
src_cp->iterate_archivable_resolved_references([&](int rr_index) {
|
||||||
|
keep_resolved_refs.at_put(rr_index, true);
|
||||||
|
});
|
||||||
|
|
||||||
objArrayOop scratch_rr = HeapShared::scratch_resolved_references(src_cp);
|
objArrayOop scratch_rr = HeapShared::scratch_resolved_references(src_cp);
|
||||||
Array<u2>* ref_map = reference_map();
|
Array<u2>* ref_map = reference_map();
|
||||||
int ref_map_len = ref_map == nullptr ? 0 : ref_map->length();
|
int ref_map_len = ref_map == nullptr ? 0 : ref_map->length();
|
||||||
@ -316,8 +362,13 @@ objArrayOop ConstantPool::prepare_resolved_references_for_archiving() {
|
|||||||
if (!ArchiveHeapWriter::is_string_too_large_to_archive(obj)) {
|
if (!ArchiveHeapWriter::is_string_too_large_to_archive(obj)) {
|
||||||
scratch_rr->obj_at_put(i, obj);
|
scratch_rr->obj_at_put(i, obj);
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keep_resolved_refs.at(i)) {
|
||||||
|
scratch_rr->obj_at_put(i, obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return scratch_rr;
|
return scratch_rr;
|
||||||
@ -325,6 +376,32 @@ objArrayOop ConstantPool::prepare_resolved_references_for_archiving() {
|
|||||||
return rr;
|
return rr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConstantPool::find_required_hidden_classes() {
|
||||||
|
if (_cache == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassLoaderData* loader_data = pool_holder()->class_loader_data();
|
||||||
|
if (loader_data == nullptr) {
|
||||||
|
// These are custom loader classes from the preimage
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SystemDictionaryShared::is_builtin_loader(loader_data)) {
|
||||||
|
// Archiving resolved references for classes from non-builtin loaders
|
||||||
|
// is not yet supported.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
objArrayOop rr = resolved_references();
|
||||||
|
if (rr != nullptr) {
|
||||||
|
iterate_archivable_resolved_references([&](int rr_index) {
|
||||||
|
oop obj = rr->obj_at(rr_index);
|
||||||
|
HeapShared::find_required_hidden_classes_in_object(obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConstantPool::add_dumped_interned_strings() {
|
void ConstantPool::add_dumped_interned_strings() {
|
||||||
objArrayOop rr = resolved_references();
|
objArrayOop rr = resolved_references();
|
||||||
if (rr != nullptr) {
|
if (rr != nullptr) {
|
||||||
@ -349,6 +426,11 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
|
|||||||
assert(is_constantPool(), "ensure C++ vtable is restored");
|
assert(is_constantPool(), "ensure C++ vtable is restored");
|
||||||
assert(on_stack(), "should always be set for shared constant pools");
|
assert(on_stack(), "should always be set for shared constant pools");
|
||||||
assert(is_shared(), "should always be set for shared constant pools");
|
assert(is_shared(), "should always be set for shared constant pools");
|
||||||
|
if (is_for_method_handle_intrinsic()) {
|
||||||
|
// See the same check in remove_unshareable_info() below.
|
||||||
|
assert(cache() == NULL, "must not have cpCache");
|
||||||
|
return;
|
||||||
|
}
|
||||||
assert(_cache != nullptr, "constant pool _cache should not be null");
|
assert(_cache != nullptr, "constant pool _cache should not be null");
|
||||||
|
|
||||||
// Only create the new resolved references array if it hasn't been attempted before
|
// Only create the new resolved references array if it hasn't been attempted before
|
||||||
@ -388,6 +470,14 @@ void ConstantPool::remove_unshareable_info() {
|
|||||||
// we always set _on_stack to true to avoid having to change _flags during runtime.
|
// we always set _on_stack to true to avoid having to change _flags during runtime.
|
||||||
_flags |= (_on_stack | _is_shared);
|
_flags |= (_on_stack | _is_shared);
|
||||||
|
|
||||||
|
if (is_for_method_handle_intrinsic()) {
|
||||||
|
// This CP was created by Method::make_method_handle_intrinsic() and has nothing
|
||||||
|
// that need to be removed/restored. It has no cpCache since the intrinsic methods
|
||||||
|
// don't have any bytecodes.
|
||||||
|
assert(cache() == NULL, "must not have cpCache");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// resolved_references(): remember its length. If it cannot be restored
|
// resolved_references(): remember its length. If it cannot be restored
|
||||||
// from the archived heap objects at run time, we need to dynamically allocate it.
|
// from the archived heap objects at run time, we need to dynamically allocate it.
|
||||||
if (cache() != nullptr) {
|
if (cache() != nullptr) {
|
||||||
@ -482,7 +572,7 @@ void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) {
|
|||||||
can_archive = false;
|
can_archive = false;
|
||||||
} else {
|
} else {
|
||||||
ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this);
|
ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this);
|
||||||
can_archive = ClassPrelinker::is_resolution_deterministic(src_cp, cp_index);
|
can_archive = AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!can_archive) {
|
if (!can_archive) {
|
||||||
@ -502,7 +592,7 @@ void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) {
|
|||||||
(!k->is_instance_klass() || pool_holder()->is_subtype_of(k)) ? "" : " (not supertype)");
|
(!k->is_instance_klass() || pool_holder()->is_subtype_of(k)) ? "" : " (not supertype)");
|
||||||
} else {
|
} else {
|
||||||
Symbol* name = klass_name_at(cp_index);
|
Symbol* name = klass_name_at(cp_index);
|
||||||
log.print(" %s", name->as_C_string());
|
log.print(" => %s", name->as_C_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,9 +838,7 @@ int ConstantPool::to_cp_index(int index, Bytecodes::Code code) {
|
|||||||
case Bytecodes::_fast_invokevfinal: // Bytecode interpreter uses this
|
case Bytecodes::_fast_invokevfinal: // Bytecode interpreter uses this
|
||||||
return resolved_method_entry_at(index)->constant_pool_index();
|
return resolved_method_entry_at(index)->constant_pool_index();
|
||||||
default:
|
default:
|
||||||
tty->print_cr("Unexpected bytecode: %d", code);
|
fatal("Unexpected bytecode: %s", Bytecodes::name(code));
|
||||||
ShouldNotReachHere(); // All cases should have been handled
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class ConstantPool : public Metadata {
|
|||||||
friend class JVMCIVMStructs;
|
friend class JVMCIVMStructs;
|
||||||
friend class BytecodeInterpreter; // Directly extracts a klass in the pool for fast instanceof/checkcast
|
friend class BytecodeInterpreter; // Directly extracts a klass in the pool for fast instanceof/checkcast
|
||||||
friend class Universe; // For null constructor
|
friend class Universe; // For null constructor
|
||||||
friend class ClassPrelinker; // CDS
|
friend class AOTConstantPoolResolver;
|
||||||
private:
|
private:
|
||||||
// If you add a new field that points to any metaspace object, you
|
// If you add a new field that points to any metaspace object, you
|
||||||
// must add this field to ConstantPool::metaspace_pointers_do().
|
// must add this field to ConstantPool::metaspace_pointers_do().
|
||||||
@ -109,7 +109,8 @@ class ConstantPool : public Metadata {
|
|||||||
_has_preresolution = 1, // Flags
|
_has_preresolution = 1, // Flags
|
||||||
_on_stack = 2,
|
_on_stack = 2,
|
||||||
_is_shared = 4,
|
_is_shared = 4,
|
||||||
_has_dynamic_constant = 8
|
_has_dynamic_constant = 8,
|
||||||
|
_is_for_method_handle_intrinsic = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
u2 _flags; // old fashioned bit twiddling
|
u2 _flags; // old fashioned bit twiddling
|
||||||
@ -216,6 +217,9 @@ class ConstantPool : public Metadata {
|
|||||||
bool has_dynamic_constant() const { return (_flags & _has_dynamic_constant) != 0; }
|
bool has_dynamic_constant() const { return (_flags & _has_dynamic_constant) != 0; }
|
||||||
void set_has_dynamic_constant() { _flags |= _has_dynamic_constant; }
|
void set_has_dynamic_constant() { _flags |= _has_dynamic_constant; }
|
||||||
|
|
||||||
|
bool is_for_method_handle_intrinsic() const { return (_flags & _is_for_method_handle_intrinsic) != 0; }
|
||||||
|
void set_is_for_method_handle_intrinsic() { _flags |= _is_for_method_handle_intrinsic; }
|
||||||
|
|
||||||
// Klass holding pool
|
// Klass holding pool
|
||||||
InstanceKlass* pool_holder() const { return _pool_holder; }
|
InstanceKlass* pool_holder() const { return _pool_holder; }
|
||||||
void set_pool_holder(InstanceKlass* k) { _pool_holder = k; }
|
void set_pool_holder(InstanceKlass* k) { _pool_holder = k; }
|
||||||
@ -679,12 +683,14 @@ class ConstantPool : public Metadata {
|
|||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
// CDS support
|
// CDS support
|
||||||
objArrayOop prepare_resolved_references_for_archiving() NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
objArrayOop prepare_resolved_references_for_archiving() NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
|
||||||
|
void find_required_hidden_classes() NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
void add_dumped_interned_strings() NOT_CDS_JAVA_HEAP_RETURN;
|
void add_dumped_interned_strings() NOT_CDS_JAVA_HEAP_RETURN;
|
||||||
void remove_unshareable_info();
|
void remove_unshareable_info();
|
||||||
void restore_unshareable_info(TRAPS);
|
void restore_unshareable_info(TRAPS);
|
||||||
private:
|
private:
|
||||||
void remove_unshareable_entries();
|
void remove_unshareable_entries();
|
||||||
void remove_resolved_klass_if_non_deterministic(int cp_index);
|
void remove_resolved_klass_if_non_deterministic(int cp_index);
|
||||||
|
template <typename Function> void iterate_archivable_resolved_references(Function function);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotConstantPoolResolver.hpp"
|
||||||
#include "cds/archiveBuilder.hpp"
|
#include "cds/archiveBuilder.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "cds/classPrelinker.hpp"
|
|
||||||
#include "cds/heapShared.hpp"
|
#include "cds/heapShared.hpp"
|
||||||
#include "classfile/resolutionErrors.hpp"
|
#include "classfile/resolutionErrors.hpp"
|
||||||
#include "classfile/systemDictionary.hpp"
|
#include "classfile/systemDictionary.hpp"
|
||||||
@ -401,9 +401,7 @@ void ConstantPoolCache::remove_unshareable_info() {
|
|||||||
assert(CDSConfig::is_dumping_archive(), "sanity");
|
assert(CDSConfig::is_dumping_archive(), "sanity");
|
||||||
|
|
||||||
if (_resolved_indy_entries != nullptr) {
|
if (_resolved_indy_entries != nullptr) {
|
||||||
for (int i = 0; i < _resolved_indy_entries->length(); i++) {
|
remove_resolved_indy_entries_if_non_deterministic();
|
||||||
resolved_indy_entry_at(i)->remove_unshareable_info();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (_resolved_field_entries != nullptr) {
|
if (_resolved_field_entries != nullptr) {
|
||||||
remove_resolved_field_entries_if_non_deterministic();
|
remove_resolved_field_entries_if_non_deterministic();
|
||||||
@ -422,7 +420,7 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() {
|
|||||||
bool archived = false;
|
bool archived = false;
|
||||||
bool resolved = rfi->is_resolved(Bytecodes::_getfield) ||
|
bool resolved = rfi->is_resolved(Bytecodes::_getfield) ||
|
||||||
rfi->is_resolved(Bytecodes::_putfield);
|
rfi->is_resolved(Bytecodes::_putfield);
|
||||||
if (resolved && ClassPrelinker::is_resolution_deterministic(src_cp, cp_index)) {
|
if (resolved && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
|
||||||
rfi->mark_and_relocate();
|
rfi->mark_and_relocate();
|
||||||
archived = true;
|
archived = true;
|
||||||
} else {
|
} else {
|
||||||
@ -436,11 +434,10 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() {
|
|||||||
Symbol* klass_name = cp->klass_name_at(klass_cp_index);
|
Symbol* klass_name = cp->klass_name_at(klass_cp_index);
|
||||||
Symbol* name = cp->uncached_name_ref_at(cp_index);
|
Symbol* name = cp->uncached_name_ref_at(cp_index);
|
||||||
Symbol* signature = cp->uncached_signature_ref_at(cp_index);
|
Symbol* signature = cp->uncached_signature_ref_at(cp_index);
|
||||||
log.print("%s field CP entry [%3d]: %s %s %s.%s:%s",
|
log.print("%s field CP entry [%3d]: %s => %s.%s:%s",
|
||||||
(archived ? "archived" : "reverted"),
|
(archived ? "archived" : "reverted"),
|
||||||
cp_index,
|
cp_index,
|
||||||
cp->pool_holder()->name()->as_C_string(),
|
cp->pool_holder()->name()->as_C_string(),
|
||||||
(archived ? "=>" : " "),
|
|
||||||
klass_name->as_C_string(), name->as_C_string(), signature->as_C_string());
|
klass_name->as_C_string(), name->as_C_string(), signature->as_C_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,13 +454,13 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() {
|
|||||||
bool archived = false;
|
bool archived = false;
|
||||||
bool resolved = rme->is_resolved(Bytecodes::_invokevirtual) ||
|
bool resolved = rme->is_resolved(Bytecodes::_invokevirtual) ||
|
||||||
rme->is_resolved(Bytecodes::_invokespecial) ||
|
rme->is_resolved(Bytecodes::_invokespecial) ||
|
||||||
rme->is_resolved(Bytecodes::_invokeinterface);
|
rme->is_resolved(Bytecodes::_invokeinterface) ||
|
||||||
|
rme->is_resolved(Bytecodes::_invokehandle);
|
||||||
|
|
||||||
// Just for safety -- this should not happen, but do not archive if we ever see this.
|
// Just for safety -- this should not happen, but do not archive if we ever see this.
|
||||||
resolved &= !(rme->is_resolved(Bytecodes::_invokehandle) ||
|
resolved &= !(rme->is_resolved(Bytecodes::_invokestatic));
|
||||||
rme->is_resolved(Bytecodes::_invokestatic));
|
|
||||||
|
|
||||||
if (resolved && can_archive_resolved_method(rme)) {
|
if (resolved && can_archive_resolved_method(src_cp, rme)) {
|
||||||
rme->mark_and_relocate(src_cp);
|
rme->mark_and_relocate(src_cp);
|
||||||
archived = true;
|
archived = true;
|
||||||
} else {
|
} else {
|
||||||
@ -495,7 +492,41 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConstantPoolCache::can_archive_resolved_method(ResolvedMethodEntry* method_entry) {
|
void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() {
|
||||||
|
ConstantPool* cp = constant_pool();
|
||||||
|
ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(cp);
|
||||||
|
for (int i = 0; i < _resolved_indy_entries->length(); i++) {
|
||||||
|
ResolvedIndyEntry* rei = _resolved_indy_entries->adr_at(i);
|
||||||
|
int cp_index = rei->constant_pool_index();
|
||||||
|
bool archived = false;
|
||||||
|
bool resolved = rei->is_resolved();
|
||||||
|
if (resolved && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
|
||||||
|
rei->mark_and_relocate();
|
||||||
|
archived = true;
|
||||||
|
} else {
|
||||||
|
rei->remove_unshareable_info();
|
||||||
|
}
|
||||||
|
if (resolved) {
|
||||||
|
LogStreamHandle(Trace, cds, resolve) log;
|
||||||
|
if (log.is_enabled()) {
|
||||||
|
ResourceMark rm;
|
||||||
|
int bsm = cp->bootstrap_method_ref_index_at(cp_index);
|
||||||
|
int bsm_ref = cp->method_handle_index_at(bsm);
|
||||||
|
Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref);
|
||||||
|
Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref);
|
||||||
|
Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref));
|
||||||
|
log.print("%s indy CP entry [%3d]: %s (%d)",
|
||||||
|
(archived ? "archived" : "reverted"),
|
||||||
|
cp_index, cp->pool_holder()->name()->as_C_string(), i);
|
||||||
|
log.print(" %s %s.%s:%s", (archived ? "=>" : " "), bsm_klass->as_C_string(),
|
||||||
|
bsm_name->as_C_string(), bsm_signature->as_C_string());
|
||||||
|
}
|
||||||
|
ArchiveBuilder::alloc_stats()->record_indy_cp_entry(archived, resolved && !archived);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry) {
|
||||||
InstanceKlass* pool_holder = constant_pool()->pool_holder();
|
InstanceKlass* pool_holder = constant_pool()->pool_holder();
|
||||||
if (!(pool_holder->is_shared_boot_class() || pool_holder->is_shared_platform_class() ||
|
if (!(pool_holder->is_shared_boot_class() || pool_holder->is_shared_platform_class() ||
|
||||||
pool_holder->is_shared_app_class())) {
|
pool_holder->is_shared_app_class())) {
|
||||||
@ -520,10 +551,9 @@ bool ConstantPoolCache::can_archive_resolved_method(ResolvedMethodEntry* method_
|
|||||||
}
|
}
|
||||||
|
|
||||||
int cp_index = method_entry->constant_pool_index();
|
int cp_index = method_entry->constant_pool_index();
|
||||||
ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(constant_pool());
|
|
||||||
assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity");
|
assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity");
|
||||||
|
|
||||||
if (!ClassPrelinker::is_resolution_deterministic(src_cp, cp_index)) {
|
if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,11 +561,16 @@ bool ConstantPoolCache::can_archive_resolved_method(ResolvedMethodEntry* method_
|
|||||||
method_entry->is_resolved(Bytecodes::_invokevirtual) ||
|
method_entry->is_resolved(Bytecodes::_invokevirtual) ||
|
||||||
method_entry->is_resolved(Bytecodes::_invokespecial)) {
|
method_entry->is_resolved(Bytecodes::_invokespecial)) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (method_entry->is_resolved(Bytecodes::_invokehandle)) {
|
||||||
|
if (CDSConfig::is_dumping_invokedynamic()) {
|
||||||
|
// invokehandle depends on archived MethodType and LambdaForms.
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// invokestatic and invokehandle are not supported yet.
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif // INCLUDE_CDS
|
#endif // INCLUDE_CDS
|
||||||
|
|
||||||
|
@ -224,8 +224,9 @@ class ConstantPoolCache: public MetaspaceObj {
|
|||||||
|
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
void remove_resolved_field_entries_if_non_deterministic();
|
void remove_resolved_field_entries_if_non_deterministic();
|
||||||
|
void remove_resolved_indy_entries_if_non_deterministic();
|
||||||
void remove_resolved_method_entries_if_non_deterministic();
|
void remove_resolved_method_entries_if_non_deterministic();
|
||||||
bool can_archive_resolved_method(ResolvedMethodEntry* method_entry);
|
bool can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// RedefineClasses support
|
// RedefineClasses support
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotClassInitializer.hpp"
|
||||||
#include "cds/archiveUtils.hpp"
|
#include "cds/archiveUtils.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "cds/cdsEnumKlass.hpp"
|
#include "cds/cdsEnumKlass.hpp"
|
||||||
@ -734,6 +735,18 @@ bool InstanceKlass::is_sealed() const {
|
|||||||
_permitted_subclasses != Universe::the_empty_short_array();
|
_permitted_subclasses != Universe::the_empty_short_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JLS 8.9: An enum class is either implicitly final and derives
|
||||||
|
// from java.lang.Enum, or else is implicitly sealed to its
|
||||||
|
// anonymous subclasses. This query detects both kinds.
|
||||||
|
// It does not validate the finality or
|
||||||
|
// sealing conditions: it merely checks for a super of Enum.
|
||||||
|
// This is sufficient for recognizing well-formed enums.
|
||||||
|
bool InstanceKlass::is_enum_subclass() const {
|
||||||
|
InstanceKlass* s = java_super();
|
||||||
|
return (s == vmClasses::Enum_klass() ||
|
||||||
|
(s != nullptr && s->java_super() == vmClasses::Enum_klass()));
|
||||||
|
}
|
||||||
|
|
||||||
bool InstanceKlass::should_be_initialized() const {
|
bool InstanceKlass::should_be_initialized() const {
|
||||||
return !is_initialized();
|
return !is_initialized();
|
||||||
}
|
}
|
||||||
@ -791,6 +804,68 @@ void InstanceKlass::initialize(TRAPS) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
void InstanceKlass::assert_no_clinit_will_run_for_aot_initialized_class() const {
|
||||||
|
assert(has_aot_initialized_mirror(), "must be");
|
||||||
|
|
||||||
|
InstanceKlass* s = java_super();
|
||||||
|
if (s != nullptr) {
|
||||||
|
DEBUG_ONLY(ResourceMark rm);
|
||||||
|
assert(s->is_initialized(), "super class %s of aot-inited class %s must have been initialized",
|
||||||
|
s->external_name(), external_name());
|
||||||
|
s->assert_no_clinit_will_run_for_aot_initialized_class();
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<InstanceKlass*>* interfaces = local_interfaces();
|
||||||
|
int len = interfaces->length();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
InstanceKlass* intf = interfaces->at(i);
|
||||||
|
if (!intf->is_initialized()) {
|
||||||
|
ResourceMark rm;
|
||||||
|
// Note: an interface needs to be marked as is_initialized() only if
|
||||||
|
// - it has a <clinit>
|
||||||
|
// - it has declared a default method.
|
||||||
|
assert(!intf->interface_needs_clinit_execution_as_super(/*also_check_supers*/false),
|
||||||
|
"uninitialized super interface %s of aot-inited class %s must not have <clinit>",
|
||||||
|
intf->external_name(), external_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if INCLUDE_CDS
|
||||||
|
void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) {
|
||||||
|
assert(has_aot_initialized_mirror(), "must be");
|
||||||
|
assert(CDSConfig::is_loading_heap(), "must be");
|
||||||
|
assert(CDSConfig::is_using_aot_linked_classes(), "must be");
|
||||||
|
assert_no_clinit_will_run_for_aot_initialized_class();
|
||||||
|
|
||||||
|
if (is_initialized()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_is_enabled(Info, cds, init)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_info(cds, init)("%s (aot-inited)", external_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
link_class(CHECK);
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
{
|
||||||
|
Handle h_init_lock(THREAD, init_lock());
|
||||||
|
ObjectLocker ol(h_init_lock, THREAD);
|
||||||
|
assert(!is_initialized(), "sanity");
|
||||||
|
assert(!is_being_initialized(), "sanity");
|
||||||
|
assert(!is_in_error_state(), "sanity");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
set_init_thread(THREAD);
|
||||||
|
AOTClassInitializer::call_runtime_setup(THREAD, this);
|
||||||
|
set_initialization_state_and_notify(fully_initialized, CHECK);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool InstanceKlass::verify_code(TRAPS) {
|
bool InstanceKlass::verify_code(TRAPS) {
|
||||||
// 1) Verify the bytecodes
|
// 1) Verify the bytecodes
|
||||||
@ -1578,7 +1653,10 @@ void InstanceKlass::call_class_initializer(TRAPS) {
|
|||||||
|
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
// This is needed to ensure the consistency of the archived heap objects.
|
// This is needed to ensure the consistency of the archived heap objects.
|
||||||
if (has_archived_enum_objs()) {
|
if (has_aot_initialized_mirror() && CDSConfig::is_loading_heap()) {
|
||||||
|
AOTClassInitializer::call_runtime_setup(THREAD, this);
|
||||||
|
return;
|
||||||
|
} else if (has_archived_enum_objs()) {
|
||||||
assert(is_shared(), "must be");
|
assert(is_shared(), "must be");
|
||||||
bool initialized = CDSEnumKlass::initialize_enum_klass(this, CHECK);
|
bool initialized = CDSEnumKlass::initialize_enum_klass(this, CHECK);
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
@ -1607,6 +1685,47 @@ void InstanceKlass::call_class_initializer(TRAPS) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a class that implements this interface is initialized, is the JVM required
|
||||||
|
// to first execute a <clinit> method declared in this interface,
|
||||||
|
// or (if also_check_supers==true) any of the super types of this interface?
|
||||||
|
//
|
||||||
|
// JVMS 5.5. Initialization, step 7: Next, if C is a class rather than
|
||||||
|
// an interface, then let SC be its superclass and let SI1, ..., SIn
|
||||||
|
// be all superinterfaces of C (whether direct or indirect) that
|
||||||
|
// declare at least one non-abstract, non-static method.
|
||||||
|
//
|
||||||
|
// So when an interface is initialized, it does not look at its
|
||||||
|
// supers. But a proper class will ensure that all of its supers have
|
||||||
|
// run their <clinit> methods, except that it disregards interfaces
|
||||||
|
// that lack a non-static concrete method (i.e., a default method).
|
||||||
|
// Therefore, you should probably call this method only when the
|
||||||
|
// current class is a super of some proper class, not an interface.
|
||||||
|
bool InstanceKlass::interface_needs_clinit_execution_as_super(bool also_check_supers) const {
|
||||||
|
assert(is_interface(), "must be");
|
||||||
|
|
||||||
|
if (!has_nonstatic_concrete_methods()) {
|
||||||
|
// quick check: no nonstatic concrete methods are declared by this or any super interfaces
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JVMS 5.5. Initialization
|
||||||
|
// ...If C is an interface that declares a non-abstract,
|
||||||
|
// non-static method, the initialization of a class that
|
||||||
|
// implements C directly or indirectly.
|
||||||
|
if (declares_nonstatic_concrete_methods() && class_initializer() != nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (also_check_supers) {
|
||||||
|
Array<InstanceKlass*>* all_ifs = transitive_interfaces();
|
||||||
|
for (int i = 0; i < all_ifs->length(); ++i) {
|
||||||
|
InstanceKlass* super_intf = all_ifs->at(i);
|
||||||
|
if (super_intf->declares_nonstatic_concrete_methods() && super_intf->class_initializer() != nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceKlass::mask_for(const methodHandle& method, int bci,
|
void InstanceKlass::mask_for(const methodHandle& method, int bci,
|
||||||
InterpreterOopMap* entry_for) {
|
InterpreterOopMap* entry_for) {
|
||||||
@ -2497,6 +2616,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it->push(&_nest_host);
|
||||||
it->push(&_nest_members);
|
it->push(&_nest_members);
|
||||||
it->push(&_permitted_subclasses);
|
it->push(&_permitted_subclasses);
|
||||||
it->push(&_record_components);
|
it->push(&_record_components);
|
||||||
@ -2560,8 +2680,12 @@ void InstanceKlass::remove_unshareable_info() {
|
|||||||
_methods_jmethod_ids = nullptr;
|
_methods_jmethod_ids = nullptr;
|
||||||
_jni_ids = nullptr;
|
_jni_ids = nullptr;
|
||||||
_oop_map_cache = nullptr;
|
_oop_map_cache = nullptr;
|
||||||
// clear _nest_host to ensure re-load at runtime
|
if (CDSConfig::is_dumping_invokedynamic() && HeapShared::is_lambda_proxy_klass(this)) {
|
||||||
_nest_host = nullptr;
|
// keep _nest_host
|
||||||
|
} else {
|
||||||
|
// clear _nest_host to ensure re-load at runtime
|
||||||
|
_nest_host = nullptr;
|
||||||
|
}
|
||||||
init_shared_package_entry();
|
init_shared_package_entry();
|
||||||
_dep_context_last_cleaned = 0;
|
_dep_context_last_cleaned = 0;
|
||||||
|
|
||||||
@ -2700,6 +2824,18 @@ bool InstanceKlass::can_be_verified_at_dumptime() const {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int InstanceKlass::shared_class_loader_type() const {
|
||||||
|
if (is_shared_boot_class()) {
|
||||||
|
return ClassLoader::BOOT_LOADER;
|
||||||
|
} else if (is_shared_platform_class()) {
|
||||||
|
return ClassLoader::PLATFORM_LOADER;
|
||||||
|
} else if (is_shared_app_class()) {
|
||||||
|
return ClassLoader::APP_LOADER;
|
||||||
|
} else {
|
||||||
|
return ClassLoader::OTHER;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // INCLUDE_CDS
|
#endif // INCLUDE_CDS
|
||||||
|
|
||||||
#if INCLUDE_JVMTI
|
#if INCLUDE_JVMTI
|
||||||
@ -2907,6 +3043,10 @@ ModuleEntry* InstanceKlass::module() const {
|
|||||||
return class_loader_data()->unnamed_module();
|
return class_loader_data()->unnamed_module();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InstanceKlass::in_javabase_module() const {
|
||||||
|
return module()->name() == vmSymbols::java_base();
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceKlass::set_package(ClassLoaderData* loader_data, PackageEntry* pkg_entry, TRAPS) {
|
void InstanceKlass::set_package(ClassLoaderData* loader_data, PackageEntry* pkg_entry, TRAPS) {
|
||||||
|
|
||||||
// ensure java/ packages only loaded by boot or platform builtin loaders
|
// ensure java/ packages only loaded by boot or platform builtin loaders
|
||||||
|
@ -323,6 +323,7 @@ class InstanceKlass: public Klass {
|
|||||||
void set_shared_loading_failed() { _misc_flags.set_shared_loading_failed(true); }
|
void set_shared_loading_failed() { _misc_flags.set_shared_loading_failed(true); }
|
||||||
|
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
|
int shared_class_loader_type() const;
|
||||||
void set_shared_class_loader_type(s2 loader_type) { _misc_flags.set_shared_class_loader_type(loader_type); }
|
void set_shared_class_loader_type(s2 loader_type) { _misc_flags.set_shared_class_loader_type(loader_type); }
|
||||||
void assign_class_loader_type() { _misc_flags.assign_class_loader_type(_class_loader_data); }
|
void assign_class_loader_type() { _misc_flags.assign_class_loader_type(_class_loader_data); }
|
||||||
#endif
|
#endif
|
||||||
@ -429,6 +430,9 @@ class InstanceKlass: public Klass {
|
|||||||
}
|
}
|
||||||
bool is_record() const;
|
bool is_record() const;
|
||||||
|
|
||||||
|
// test for enum class (or possibly an anonymous subclass within a sealed enum)
|
||||||
|
bool is_enum_subclass() const;
|
||||||
|
|
||||||
// permitted subclasses
|
// permitted subclasses
|
||||||
Array<u2>* permitted_subclasses() const { return _permitted_subclasses; }
|
Array<u2>* permitted_subclasses() const { return _permitted_subclasses; }
|
||||||
void set_permitted_subclasses(Array<u2>* s) { _permitted_subclasses = s; }
|
void set_permitted_subclasses(Array<u2>* s) { _permitted_subclasses = s; }
|
||||||
@ -475,6 +479,7 @@ public:
|
|||||||
// package
|
// package
|
||||||
PackageEntry* package() const { return _package_entry; }
|
PackageEntry* package() const { return _package_entry; }
|
||||||
ModuleEntry* module() const;
|
ModuleEntry* module() const;
|
||||||
|
bool in_javabase_module() const;
|
||||||
bool in_unnamed_package() const { return (_package_entry == nullptr); }
|
bool in_unnamed_package() const { return (_package_entry == nullptr); }
|
||||||
void set_package(ClassLoaderData* loader_data, PackageEntry* pkg_entry, TRAPS);
|
void set_package(ClassLoaderData* loader_data, PackageEntry* pkg_entry, TRAPS);
|
||||||
// If the package for the InstanceKlass is in the boot loader's package entry
|
// If the package for the InstanceKlass is in the boot loader's package entry
|
||||||
@ -531,12 +536,15 @@ public:
|
|||||||
|
|
||||||
// initialization (virtuals from Klass)
|
// initialization (virtuals from Klass)
|
||||||
bool should_be_initialized() const; // means that initialize should be called
|
bool should_be_initialized() const; // means that initialize should be called
|
||||||
|
void initialize_with_aot_initialized_mirror(TRAPS);
|
||||||
|
void assert_no_clinit_will_run_for_aot_initialized_class() const NOT_DEBUG_RETURN;
|
||||||
void initialize(TRAPS);
|
void initialize(TRAPS);
|
||||||
void link_class(TRAPS);
|
void link_class(TRAPS);
|
||||||
bool link_class_or_fail(TRAPS); // returns false on failure
|
bool link_class_or_fail(TRAPS); // returns false on failure
|
||||||
void rewrite_class(TRAPS);
|
void rewrite_class(TRAPS);
|
||||||
void link_methods(TRAPS);
|
void link_methods(TRAPS);
|
||||||
Method* class_initializer() const;
|
Method* class_initializer() const;
|
||||||
|
bool interface_needs_clinit_execution_as_super(bool also_check_supers=true) const;
|
||||||
|
|
||||||
// reference type
|
// reference type
|
||||||
ReferenceType reference_type() const { return (ReferenceType)_reference_type; }
|
ReferenceType reference_type() const { return (ReferenceType)_reference_type; }
|
||||||
|
@ -195,7 +195,12 @@ private:
|
|||||||
_has_archived_enum_objs = 1 << 4,
|
_has_archived_enum_objs = 1 << 4,
|
||||||
// This class was not loaded from a classfile in the module image
|
// This class was not loaded from a classfile in the module image
|
||||||
// or classpath.
|
// or classpath.
|
||||||
_is_generated_shared_class = 1 << 5
|
_is_generated_shared_class = 1 << 5,
|
||||||
|
// archived mirror already initialized by AOT-cache assembly: no further need to call <clinit>
|
||||||
|
_has_aot_initialized_mirror = 1 << 6,
|
||||||
|
// If this class has been aot-inititalized, do we need to call its runtimeSetup()
|
||||||
|
// method during the production run?
|
||||||
|
_is_runtime_setup_required = 1 << 7,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -377,6 +382,23 @@ protected:
|
|||||||
NOT_CDS(return false;)
|
NOT_CDS(return false;)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_has_aot_initialized_mirror() {
|
||||||
|
CDS_ONLY(_shared_class_flags |= _has_aot_initialized_mirror;)
|
||||||
|
}
|
||||||
|
bool has_aot_initialized_mirror() const {
|
||||||
|
CDS_ONLY(return (_shared_class_flags & _has_aot_initialized_mirror) != 0;)
|
||||||
|
NOT_CDS(return false;)
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_is_runtime_setup_required() {
|
||||||
|
assert(has_aot_initialized_mirror(), "sanity");
|
||||||
|
CDS_ONLY(_shared_class_flags |= _is_runtime_setup_required;)
|
||||||
|
}
|
||||||
|
bool is_runtime_setup_required() const {
|
||||||
|
CDS_ONLY(return (_shared_class_flags & _is_runtime_setup_required) != 0;)
|
||||||
|
NOT_CDS(return false;)
|
||||||
|
}
|
||||||
|
|
||||||
bool is_shared() const { // shadows MetaspaceObj::is_shared)()
|
bool is_shared() const { // shadows MetaspaceObj::is_shared)()
|
||||||
CDS_ONLY(return (_shared_class_flags & _is_shared_class) != 0;)
|
CDS_ONLY(return (_shared_class_flags & _is_shared_class) != 0;)
|
||||||
NOT_CDS(return false;)
|
NOT_CDS(return false;)
|
||||||
|
@ -1432,6 +1432,7 @@ methodHandle Method::make_method_handle_intrinsic(vmIntrinsics::ID iid,
|
|||||||
cp->symbol_at_put(_imcp_invoke_name, name);
|
cp->symbol_at_put(_imcp_invoke_name, name);
|
||||||
cp->symbol_at_put(_imcp_invoke_signature, signature);
|
cp->symbol_at_put(_imcp_invoke_signature, signature);
|
||||||
cp->set_has_preresolution();
|
cp->set_has_preresolution();
|
||||||
|
cp->set_is_for_method_handle_intrinsic();
|
||||||
|
|
||||||
// decide on access bits: public or not?
|
// decide on access bits: public or not?
|
||||||
int flags_bits = (JVM_ACC_NATIVE | JVM_ACC_SYNTHETIC | JVM_ACC_FINAL);
|
int flags_bits = (JVM_ACC_NATIVE | JVM_ACC_SYNTHETIC | JVM_ACC_FINAL);
|
||||||
@ -1480,6 +1481,16 @@ methodHandle Method::make_method_handle_intrinsic(vmIntrinsics::ID iid,
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if INCLUDE_CDS
|
||||||
|
void Method::restore_archived_method_handle_intrinsic(methodHandle m, TRAPS) {
|
||||||
|
m->link_method(m, CHECK);
|
||||||
|
|
||||||
|
if (m->intrinsic_id() == vmIntrinsics::_linkToNative) {
|
||||||
|
m->set_interpreter_entry(m->adapter()->get_i2c_entry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Klass* Method::check_non_bcp_klass(Klass* klass) {
|
Klass* Method::check_non_bcp_klass(Klass* klass) {
|
||||||
if (klass != nullptr && klass->class_loader() != nullptr) {
|
if (klass != nullptr && klass->class_loader() != nullptr) {
|
||||||
if (klass->is_objArray_klass())
|
if (klass->is_objArray_klass())
|
||||||
|
@ -122,6 +122,7 @@ class Method : public Metadata {
|
|||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
void remove_unshareable_info();
|
void remove_unshareable_info();
|
||||||
void restore_unshareable_info(TRAPS);
|
void restore_unshareable_info(TRAPS);
|
||||||
|
static void restore_archived_method_handle_intrinsic(methodHandle m, TRAPS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// accessors for instance variables
|
// accessors for instance variables
|
||||||
|
@ -2838,7 +2838,7 @@ static void thread_entry(JavaThread* thread, TRAPS) {
|
|||||||
|
|
||||||
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
|
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
if (CDSConfig::is_dumping_static_archive()) {
|
if (CDSConfig::allow_only_single_java_thread()) {
|
||||||
// During java -Xshare:dump, if we allow multiple Java threads to
|
// During java -Xshare:dump, if we allow multiple Java threads to
|
||||||
// execute in parallel, symbols and classes may be loaded in
|
// execute in parallel, symbols and classes may be loaded in
|
||||||
// random orders which will make the resulting CDS archive
|
// random orders which will make the resulting CDS archive
|
||||||
|
@ -1075,6 +1075,16 @@ bool JvmtiExport::has_early_class_hook_env() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JvmtiExport::has_early_vmstart_env() {
|
||||||
|
JvmtiEnvIterator it;
|
||||||
|
for (JvmtiEnv* env = it.first(); env != nullptr; env = it.next(env)) {
|
||||||
|
if (env->early_vmstart_env()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool JvmtiExport::_should_post_class_file_load_hook = false;
|
bool JvmtiExport::_should_post_class_file_load_hook = false;
|
||||||
|
|
||||||
// This flag is read by C2 during VM internal objects allocation
|
// This flag is read by C2 during VM internal objects allocation
|
||||||
|
@ -371,6 +371,7 @@ class JvmtiExport : public AllStatic {
|
|||||||
}
|
}
|
||||||
static bool is_early_phase() NOT_JVMTI_RETURN_(false);
|
static bool is_early_phase() NOT_JVMTI_RETURN_(false);
|
||||||
static bool has_early_class_hook_env() NOT_JVMTI_RETURN_(false);
|
static bool has_early_class_hook_env() NOT_JVMTI_RETURN_(false);
|
||||||
|
static bool has_early_vmstart_env() NOT_JVMTI_RETURN_(false);
|
||||||
// Return true if the class was modified by the hook.
|
// Return true if the class was modified by the hook.
|
||||||
static bool post_class_file_load_hook(Symbol* h_name, Handle class_loader,
|
static bool post_class_file_load_hook(Symbol* h_name, Handle class_loader,
|
||||||
Handle h_protection_domain,
|
Handle h_protection_domain,
|
||||||
|
@ -2534,19 +2534,23 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
|||||||
// -Xshare:dump
|
// -Xshare:dump
|
||||||
} else if (match_option(option, "-Xshare:dump")) {
|
} else if (match_option(option, "-Xshare:dump")) {
|
||||||
CDSConfig::enable_dumping_static_archive();
|
CDSConfig::enable_dumping_static_archive();
|
||||||
|
CDSConfig::set_old_cds_flags_used();
|
||||||
// -Xshare:on
|
// -Xshare:on
|
||||||
} else if (match_option(option, "-Xshare:on")) {
|
} else if (match_option(option, "-Xshare:on")) {
|
||||||
UseSharedSpaces = true;
|
UseSharedSpaces = true;
|
||||||
RequireSharedSpaces = true;
|
RequireSharedSpaces = true;
|
||||||
|
CDSConfig::set_old_cds_flags_used();
|
||||||
// -Xshare:auto || -XX:ArchiveClassesAtExit=<archive file>
|
// -Xshare:auto || -XX:ArchiveClassesAtExit=<archive file>
|
||||||
} else if (match_option(option, "-Xshare:auto")) {
|
} else if (match_option(option, "-Xshare:auto")) {
|
||||||
UseSharedSpaces = true;
|
UseSharedSpaces = true;
|
||||||
RequireSharedSpaces = false;
|
RequireSharedSpaces = false;
|
||||||
xshare_auto_cmd_line = true;
|
xshare_auto_cmd_line = true;
|
||||||
|
CDSConfig::set_old_cds_flags_used();
|
||||||
// -Xshare:off
|
// -Xshare:off
|
||||||
} else if (match_option(option, "-Xshare:off")) {
|
} else if (match_option(option, "-Xshare:off")) {
|
||||||
UseSharedSpaces = false;
|
UseSharedSpaces = false;
|
||||||
RequireSharedSpaces = false;
|
RequireSharedSpaces = false;
|
||||||
|
CDSConfig::set_old_cds_flags_used();
|
||||||
// -Xverify
|
// -Xverify
|
||||||
} else if (match_option(option, "-Xverify", &tail)) {
|
} else if (match_option(option, "-Xverify", &tail)) {
|
||||||
if (strcmp(tail, ":all") == 0 || strcmp(tail, "") == 0) {
|
if (strcmp(tail, ":all") == 0 || strcmp(tail, "") == 0) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -310,6 +310,17 @@ JVMFlag::Error JVMFlagAccess::set_impl(JVMFlag* flag, void* value, JVMFlagOrigin
|
|||||||
JVMFlag::Error JVMFlagAccess::set_ccstr(JVMFlag* flag, ccstr* value, JVMFlagOrigin origin) {
|
JVMFlag::Error JVMFlagAccess::set_ccstr(JVMFlag* flag, ccstr* value, JVMFlagOrigin origin) {
|
||||||
if (flag == nullptr) return JVMFlag::INVALID_FLAG;
|
if (flag == nullptr) return JVMFlag::INVALID_FLAG;
|
||||||
if (!flag->is_ccstr()) return JVMFlag::WRONG_FORMAT;
|
if (!flag->is_ccstr()) return JVMFlag::WRONG_FORMAT;
|
||||||
|
const JVMTypedFlagLimit<ccstr>* constraint = (const JVMTypedFlagLimit<ccstr>*)JVMFlagLimit::get_constraint(flag);
|
||||||
|
if (constraint != nullptr && constraint->phase() <= JVMFlagLimit::validating_phase()) {
|
||||||
|
bool verbose = JVMFlagLimit::verbose_checks_needed() | (origin == JVMFlagOrigin::ERGONOMIC);
|
||||||
|
JVMFlag::Error err = ((JVMFlagConstraintFunc_ccstr)constraint->constraint_func())(*value, verbose);
|
||||||
|
if (err != JVMFlag::SUCCESS) {
|
||||||
|
if (origin == JVMFlagOrigin::ERGONOMIC) {
|
||||||
|
fatal("FLAG_SET_ERGO cannot be used to set an invalid value for %s", flag->name());
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
ccstr old_value = flag->get_ccstr();
|
ccstr old_value = flag->get_ccstr();
|
||||||
trace_flag_changed<ccstr, EventStringFlagChanged>(flag, old_value, *value, origin);
|
trace_flag_changed<ccstr, EventStringFlagChanged>(flag, old_value, *value, origin);
|
||||||
char* new_value = nullptr;
|
char* new_value = nullptr;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -32,6 +32,21 @@
|
|||||||
#include "runtime/task.hpp"
|
#include "runtime/task.hpp"
|
||||||
#include "utilities/powerOfTwo.hpp"
|
#include "utilities/powerOfTwo.hpp"
|
||||||
|
|
||||||
|
JVMFlag::Error AOTModeConstraintFunc(ccstr value, bool verbose) {
|
||||||
|
if (strcmp(value, "off") != 0 &&
|
||||||
|
strcmp(value, "record") != 0 &&
|
||||||
|
strcmp(value, "create") != 0 &&
|
||||||
|
strcmp(value, "auto") != 0 &&
|
||||||
|
strcmp(value, "on") != 0) {
|
||||||
|
JVMFlag::printError(verbose,
|
||||||
|
"Unrecognized value %s for AOTMode. Must be one of the following: "
|
||||||
|
"off, record, create, auto, on\n",
|
||||||
|
value);
|
||||||
|
return JVMFlag::VIOLATES_CONSTRAINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JVMFlag::SUCCESS;
|
||||||
|
}
|
||||||
JVMFlag::Error ObjectAlignmentInBytesConstraintFunc(int value, bool verbose) {
|
JVMFlag::Error ObjectAlignmentInBytesConstraintFunc(int value, bool verbose) {
|
||||||
if (!is_power_of_2(value)) {
|
if (!is_power_of_2(value)) {
|
||||||
JVMFlag::printError(verbose,
|
JVMFlag::printError(verbose,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -34,6 +34,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define RUNTIME_CONSTRAINTS(f) \
|
#define RUNTIME_CONSTRAINTS(f) \
|
||||||
|
f(ccstr, AOTModeConstraintFunc) \
|
||||||
f(int, ObjectAlignmentInBytesConstraintFunc) \
|
f(int, ObjectAlignmentInBytesConstraintFunc) \
|
||||||
f(int, ContendedPaddingWidthConstraintFunc) \
|
f(int, ContendedPaddingWidthConstraintFunc) \
|
||||||
f(int, PerfDataSamplingIntervalFunc) \
|
f(int, PerfDataSamplingIntervalFunc) \
|
||||||
|
@ -24,12 +24,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||||
#include "cds/cds_globals.hpp"
|
#include "cds/cds_globals.hpp"
|
||||||
#include "cds/cdsConfig.hpp"
|
#include "cds/cdsConfig.hpp"
|
||||||
|
#include "cds/heapShared.hpp"
|
||||||
#include "cds/metaspaceShared.hpp"
|
#include "cds/metaspaceShared.hpp"
|
||||||
#include "classfile/classLoader.hpp"
|
#include "classfile/classLoader.hpp"
|
||||||
#include "classfile/javaClasses.hpp"
|
#include "classfile/javaClasses.hpp"
|
||||||
#include "classfile/javaThreadStatus.hpp"
|
#include "classfile/javaThreadStatus.hpp"
|
||||||
|
#include "classfile/symbolTable.hpp"
|
||||||
#include "classfile/systemDictionary.hpp"
|
#include "classfile/systemDictionary.hpp"
|
||||||
#include "classfile/vmClasses.hpp"
|
#include "classfile/vmClasses.hpp"
|
||||||
#include "classfile/vmSymbols.hpp"
|
#include "classfile/vmSymbols.hpp"
|
||||||
@ -350,12 +353,15 @@ void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
|
|||||||
initialize_class(vmSymbols::java_lang_System(), CHECK);
|
initialize_class(vmSymbols::java_lang_System(), CHECK);
|
||||||
// The VM creates & returns objects of this class. Make sure it's initialized.
|
// The VM creates & returns objects of this class. Make sure it's initialized.
|
||||||
initialize_class(vmSymbols::java_lang_Class(), CHECK);
|
initialize_class(vmSymbols::java_lang_Class(), CHECK);
|
||||||
|
|
||||||
initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK);
|
initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK);
|
||||||
Handle thread_group = create_initial_thread_group(CHECK);
|
Handle thread_group = create_initial_thread_group(CHECK);
|
||||||
Universe::set_main_thread_group(thread_group());
|
Universe::set_main_thread_group(thread_group());
|
||||||
initialize_class(vmSymbols::java_lang_Thread(), CHECK);
|
initialize_class(vmSymbols::java_lang_Thread(), CHECK);
|
||||||
create_initial_thread(thread_group, main_thread, CHECK);
|
create_initial_thread(thread_group, main_thread, CHECK);
|
||||||
|
|
||||||
|
HeapShared::init_box_classes(CHECK);
|
||||||
|
|
||||||
// The VM creates objects of this class.
|
// The VM creates objects of this class.
|
||||||
initialize_class(vmSymbols::java_lang_Module(), CHECK);
|
initialize_class(vmSymbols::java_lang_Module(), CHECK);
|
||||||
|
|
||||||
@ -403,6 +409,7 @@ void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
|
|||||||
initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK);
|
initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK);
|
||||||
initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK);
|
initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK);
|
||||||
initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK);
|
initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK);
|
||||||
|
initialize_class(vmSymbols::java_lang_InternalError(), CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Threads::initialize_jsr292_core_classes(TRAPS) {
|
void Threads::initialize_jsr292_core_classes(TRAPS) {
|
||||||
@ -412,6 +419,10 @@ void Threads::initialize_jsr292_core_classes(TRAPS) {
|
|||||||
initialize_class(vmSymbols::java_lang_invoke_ResolvedMethodName(), CHECK);
|
initialize_class(vmSymbols::java_lang_invoke_ResolvedMethodName(), CHECK);
|
||||||
initialize_class(vmSymbols::java_lang_invoke_MemberName(), CHECK);
|
initialize_class(vmSymbols::java_lang_invoke_MemberName(), CHECK);
|
||||||
initialize_class(vmSymbols::java_lang_invoke_MethodHandleNatives(), CHECK);
|
initialize_class(vmSymbols::java_lang_invoke_MethodHandleNatives(), CHECK);
|
||||||
|
|
||||||
|
if (UseSharedSpaces) {
|
||||||
|
HeapShared::initialize_java_lang_invoke(CHECK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
||||||
@ -737,6 +748,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (CDSConfig::is_using_aot_linked_classes()) {
|
||||||
|
AOTLinkedClassBulkLoader::finish_loading_javabase_classes(CHECK_JNI_ERR);
|
||||||
|
SystemDictionary::restore_archived_method_handle_intrinsics();
|
||||||
|
}
|
||||||
|
|
||||||
// Start string deduplication thread if requested.
|
// Start string deduplication thread if requested.
|
||||||
if (StringDedup::is_enabled()) {
|
if (StringDedup::is_enabled()) {
|
||||||
StringDedup::start();
|
StringDedup::start();
|
||||||
@ -752,6 +768,13 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
|
|||||||
// loaded until phase 2 completes
|
// loaded until phase 2 completes
|
||||||
call_initPhase2(CHECK_JNI_ERR);
|
call_initPhase2(CHECK_JNI_ERR);
|
||||||
|
|
||||||
|
if (CDSConfig::is_using_aot_linked_classes()) {
|
||||||
|
AOTLinkedClassBulkLoader::load_non_javabase_classes(THREAD);
|
||||||
|
}
|
||||||
|
#ifndef PRODUCT
|
||||||
|
HeapShared::initialize_test_class_from_archive(THREAD);
|
||||||
|
#endif
|
||||||
|
|
||||||
JFR_ONLY(Jfr::on_create_vm_2();)
|
JFR_ONLY(Jfr::on_create_vm_2();)
|
||||||
|
|
||||||
// Always call even when there are not JVMTI environments yet, since environments
|
// Always call even when there are not JVMTI environments yet, since environments
|
||||||
|
@ -225,6 +225,11 @@ public final class Class<T> implements java.io.Serializable,
|
|||||||
|
|
||||||
private static native void registerNatives();
|
private static native void registerNatives();
|
||||||
static {
|
static {
|
||||||
|
runtimeSetup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from JVM when loading an AOT cache
|
||||||
|
private static void runtimeSetup() {
|
||||||
registerNatives();
|
registerNatives();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3425,6 +3430,15 @@ public final class Class<T> implements java.io.Serializable,
|
|||||||
}
|
}
|
||||||
private static ReflectionFactory reflectionFactory;
|
private static ReflectionFactory reflectionFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When CDS is enabled, the Class class may be aot-initialized. However,
|
||||||
|
* we can't archive reflectionFactory, so we reset it to null, so it
|
||||||
|
* will be allocated again at runtime.
|
||||||
|
*/
|
||||||
|
private static void resetArchivedStates() {
|
||||||
|
reflectionFactory = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the elements of this enum class or null if this
|
* Returns the elements of this enum class or null if this
|
||||||
* Class object does not represent an enum class.
|
* Class object does not represent an enum class.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -38,6 +38,7 @@ import static java.lang.invoke.LambdaForm.BasicType.*;
|
|||||||
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
|
import static java.lang.invoke.MethodHandleImpl.Intrinsic;
|
||||||
import static java.lang.invoke.MethodHandleImpl.NF_loop;
|
import static java.lang.invoke.MethodHandleImpl.NF_loop;
|
||||||
import static java.lang.invoke.MethodHandleImpl.makeIntrinsic;
|
import static java.lang.invoke.MethodHandleImpl.makeIntrinsic;
|
||||||
|
import static java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE;
|
||||||
|
|
||||||
/** Transforms on LFs.
|
/** Transforms on LFs.
|
||||||
* A lambda-form editor can derive new LFs from its base LF.
|
* A lambda-form editor can derive new LFs from its base LF.
|
||||||
@ -89,12 +90,17 @@ class LambdaFormEditor {
|
|||||||
* Tightly coupled with the TransformKey class, which is used to lookup existing
|
* Tightly coupled with the TransformKey class, which is used to lookup existing
|
||||||
* Transforms.
|
* Transforms.
|
||||||
*/
|
*/
|
||||||
private static final class Transform extends SoftReference<LambdaForm> {
|
private static final class Transform {
|
||||||
|
final Object cache;
|
||||||
final long packedBytes;
|
final long packedBytes;
|
||||||
final byte[] fullBytes;
|
final byte[] fullBytes;
|
||||||
|
|
||||||
private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
|
private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
|
||||||
super(result);
|
if (USE_SOFT_CACHE) {
|
||||||
|
cache = new SoftReference<LambdaForm>(result);
|
||||||
|
} else {
|
||||||
|
cache = result;
|
||||||
|
}
|
||||||
this.packedBytes = packedBytes;
|
this.packedBytes = packedBytes;
|
||||||
this.fullBytes = fullBytes;
|
this.fullBytes = fullBytes;
|
||||||
}
|
}
|
||||||
@ -135,6 +141,15 @@ class LambdaFormEditor {
|
|||||||
}
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
public LambdaForm get() {
|
||||||
|
if (cache instanceof LambdaForm lf) {
|
||||||
|
return lf;
|
||||||
|
} else {
|
||||||
|
return ((SoftReference<LambdaForm>)cache).get();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -28,9 +28,11 @@ package java.lang.invoke;
|
|||||||
import jdk.internal.misc.VM;
|
import jdk.internal.misc.VM;
|
||||||
import jdk.internal.ref.CleanerFactory;
|
import jdk.internal.ref.CleanerFactory;
|
||||||
import sun.invoke.util.Wrapper;
|
import sun.invoke.util.Wrapper;
|
||||||
|
import sun.security.action.GetPropertyAction;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles.Lookup;
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||||
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
|
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
|
||||||
@ -690,4 +692,23 @@ class MethodHandleNatives {
|
|||||||
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
|
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
|
||||||
symbolicRefClass.isInterface()); // Mdef implements Msym
|
symbolicRefClass.isInterface()); // Mdef implements Msym
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--- AOTCache support
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In normal execution, this is set to true, so that LambdaFormEditor and MethodTypeForm will
|
||||||
|
* use soft references to allow class unloading.
|
||||||
|
*
|
||||||
|
* When dumping the AOTCache, this is set to false so that no cached heap objects will
|
||||||
|
* contain soft references (which are not yet supported by AOTCache - see JDK-8341587). AOTCache
|
||||||
|
* only stores LambdaFormEditors and MethodTypeForms for classes in the boot/platform/app loaders.
|
||||||
|
* Such classes will never be unloaded, so it's OK to use hard references.
|
||||||
|
*/
|
||||||
|
static final boolean USE_SOFT_CACHE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Properties props = GetPropertyAction.privilegedGetProperties();
|
||||||
|
USE_SOFT_CACHE = Boolean.parseBoolean(
|
||||||
|
props.getProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "true"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ import java.lang.constant.MethodTypeDesc;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -40,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
|
|
||||||
import jdk.internal.util.ReferencedKeySet;
|
import jdk.internal.util.ReferencedKeySet;
|
||||||
import jdk.internal.util.ReferenceKey;
|
import jdk.internal.util.ReferenceKey;
|
||||||
|
import jdk.internal.misc.CDS;
|
||||||
import jdk.internal.vm.annotation.Stable;
|
import jdk.internal.vm.annotation.Stable;
|
||||||
import sun.invoke.util.BytecodeDescriptor;
|
import sun.invoke.util.BytecodeDescriptor;
|
||||||
import sun.invoke.util.VerifyType;
|
import sun.invoke.util.VerifyType;
|
||||||
@ -391,6 +394,17 @@ class MethodType
|
|||||||
ptypes = NO_PTYPES; trusted = true;
|
ptypes = NO_PTYPES; trusted = true;
|
||||||
}
|
}
|
||||||
MethodType primordialMT = new MethodType(rtype, ptypes);
|
MethodType primordialMT = new MethodType(rtype, ptypes);
|
||||||
|
if (archivedMethodTypes != null) {
|
||||||
|
// If this JVM process reads from archivedMethodTypes, it never
|
||||||
|
// modifies the table. So there's no need for synchronization.
|
||||||
|
// See copyInternTable() below.
|
||||||
|
assert CDS.isUsingArchive();
|
||||||
|
MethodType mt = archivedMethodTypes.get(primordialMT);
|
||||||
|
if (mt != null) {
|
||||||
|
return mt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MethodType mt = internTable.get(primordialMT);
|
MethodType mt = internTable.get(primordialMT);
|
||||||
if (mt != null)
|
if (mt != null)
|
||||||
return mt;
|
return mt;
|
||||||
@ -409,7 +423,9 @@ class MethodType
|
|||||||
mt.form = MethodTypeForm.findForm(mt);
|
mt.form = MethodTypeForm.findForm(mt);
|
||||||
return internTable.intern(mt);
|
return internTable.intern(mt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final @Stable MethodType[] objectOnlyTypes = new MethodType[20];
|
private static final @Stable MethodType[] objectOnlyTypes = new MethodType[20];
|
||||||
|
private static @Stable HashMap<MethodType,MethodType> archivedMethodTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
|
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
|
||||||
@ -1380,4 +1396,30 @@ s.writeObject(this.parameterArray());
|
|||||||
wrapAlt = null;
|
wrapAlt = null;
|
||||||
return mt;
|
return mt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HashMap<MethodType,MethodType> copyInternTable() {
|
||||||
|
HashMap<MethodType,MethodType> copy = new HashMap<>();
|
||||||
|
|
||||||
|
for (Iterator<MethodType> i = internTable.iterator(); i.hasNext(); ) {
|
||||||
|
MethodType t = i.next();
|
||||||
|
copy.put(t, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called from C code, at the very end of Java code execution
|
||||||
|
// during the AOT cache assembly phase.
|
||||||
|
static void createArchivedObjects() {
|
||||||
|
// After the archivedMethodTypes field is assigned, this table
|
||||||
|
// is never modified. So we don't need synchronization when reading from
|
||||||
|
// it (which happens only in a future JVM process, never in the current process).
|
||||||
|
//
|
||||||
|
// @implNote CDS.isDumpingStaticArchive() is mutually exclusive with
|
||||||
|
// CDS.isUsingArchive(); at most one of them can return true for any given JVM
|
||||||
|
// process.
|
||||||
|
assert CDS.isDumpingStaticArchive();
|
||||||
|
archivedMethodTypes = copyInternTable();
|
||||||
|
internTable.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,6 +30,7 @@ import sun.invoke.util.Wrapper;
|
|||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
|
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
|
||||||
|
import static java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared information for a group of method types, which differ
|
* Shared information for a group of method types, which differ
|
||||||
@ -51,7 +52,7 @@ final class MethodTypeForm {
|
|||||||
final MethodType basicType; // the canonical erasure, with primitives simplified
|
final MethodType basicType; // the canonical erasure, with primitives simplified
|
||||||
|
|
||||||
// Cached adapter information:
|
// Cached adapter information:
|
||||||
final SoftReference<MethodHandle>[] methodHandles;
|
private final Object[] methodHandles;
|
||||||
|
|
||||||
// Indexes into methodHandles:
|
// Indexes into methodHandles:
|
||||||
static final int
|
static final int
|
||||||
@ -61,7 +62,7 @@ final class MethodTypeForm {
|
|||||||
MH_LIMIT = 3;
|
MH_LIMIT = 3;
|
||||||
|
|
||||||
// Cached lambda form information, for basic types only:
|
// Cached lambda form information, for basic types only:
|
||||||
final SoftReference<LambdaForm>[] lambdaForms;
|
private final Object[] lambdaForms;
|
||||||
|
|
||||||
// Indexes into lambdaForms:
|
// Indexes into lambdaForms:
|
||||||
static final int
|
static final int
|
||||||
@ -109,39 +110,55 @@ final class MethodTypeForm {
|
|||||||
return basicType;
|
return basicType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public MethodHandle cachedMethodHandle(int which) {
|
public MethodHandle cachedMethodHandle(int which) {
|
||||||
SoftReference<MethodHandle> entry = methodHandles[which];
|
Object entry = methodHandles[which];
|
||||||
return (entry != null) ? entry.get() : null;
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
} else if (entry instanceof MethodHandle mh) {
|
||||||
|
return mh;
|
||||||
|
} else {
|
||||||
|
return ((SoftReference<MethodHandle>)entry).get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
|
public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
|
||||||
// Simulate a CAS, to avoid racy duplication of results.
|
// Simulate a CAS, to avoid racy duplication of results.
|
||||||
SoftReference<MethodHandle> entry = methodHandles[which];
|
MethodHandle prev = cachedMethodHandle(which);
|
||||||
if (entry != null) {
|
if (prev != null) {
|
||||||
MethodHandle prev = entry.get();
|
return prev;
|
||||||
if (prev != null) {
|
}
|
||||||
return prev;
|
if (USE_SOFT_CACHE) {
|
||||||
}
|
methodHandles[which] = new SoftReference<>(mh);
|
||||||
|
} else {
|
||||||
|
methodHandles[which] = mh;
|
||||||
}
|
}
|
||||||
methodHandles[which] = new SoftReference<>(mh);
|
|
||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public LambdaForm cachedLambdaForm(int which) {
|
public LambdaForm cachedLambdaForm(int which) {
|
||||||
SoftReference<LambdaForm> entry = lambdaForms[which];
|
Object entry = lambdaForms[which];
|
||||||
return (entry != null) ? entry.get() : null;
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
} else if (entry instanceof LambdaForm lf) {
|
||||||
|
return lf;
|
||||||
|
} else {
|
||||||
|
return ((SoftReference<LambdaForm>)entry).get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
|
public synchronized LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
|
||||||
// Simulate a CAS, to avoid racy duplication of results.
|
// Simulate a CAS, to avoid racy duplication of results.
|
||||||
SoftReference<LambdaForm> entry = lambdaForms[which];
|
LambdaForm prev = cachedLambdaForm(which);
|
||||||
if (entry != null) {
|
if (prev != null) {
|
||||||
LambdaForm prev = entry.get();
|
return prev;
|
||||||
if (prev != null) {
|
}
|
||||||
return prev;
|
if (USE_SOFT_CACHE) {
|
||||||
}
|
lambdaForms[which] = new SoftReference<>(form);
|
||||||
|
} else {
|
||||||
|
lambdaForms[which] = form;
|
||||||
}
|
}
|
||||||
lambdaForms[which] = new SoftReference<>(form);
|
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,8 +208,8 @@ final class MethodTypeForm {
|
|||||||
|
|
||||||
this.primitiveCount = primitiveCount;
|
this.primitiveCount = primitiveCount;
|
||||||
this.parameterSlotCount = (short)pslotCount;
|
this.parameterSlotCount = (short)pslotCount;
|
||||||
this.lambdaForms = new SoftReference[LF_LIMIT];
|
this.lambdaForms = new Object[LF_LIMIT];
|
||||||
this.methodHandles = new SoftReference[MH_LIMIT];
|
this.methodHandles = new Object[MH_LIMIT];
|
||||||
} else {
|
} else {
|
||||||
this.basicType = MethodType.methodType(basicReturnType, basicPtypes, true);
|
this.basicType = MethodType.methodType(basicReturnType, basicPtypes, true);
|
||||||
// fill in rest of data from the basic type:
|
// fill in rest of data from the basic type:
|
||||||
|
@ -1080,6 +1080,8 @@ public final class StringConcatFactory {
|
|||||||
* without copying.
|
* without copying.
|
||||||
*/
|
*/
|
||||||
private static final class InlineHiddenClassStrategy {
|
private static final class InlineHiddenClassStrategy {
|
||||||
|
// The CLASS_NAME prefix must be the same as used by HeapShared::is_string_concat_klass()
|
||||||
|
// in the HotSpot code.
|
||||||
static final String CLASS_NAME = "java.lang.String$$StringConcat";
|
static final String CLASS_NAME = "java.lang.String$$StringConcat";
|
||||||
static final String METHOD_NAME = "concat";
|
static final String METHOD_NAME = "concat";
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ import java.util.function.ToLongFunction;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import jdk.internal.misc.Unsafe;
|
import jdk.internal.misc.Unsafe;
|
||||||
import jdk.internal.util.ArraysSupport;
|
import jdk.internal.util.ArraysSupport;
|
||||||
|
import jdk.internal.vm.annotation.Stable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A hash table supporting full concurrency of retrievals and
|
* A hash table supporting full concurrency of retrievals and
|
||||||
@ -595,7 +596,16 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
|
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
|
||||||
|
|
||||||
/** Number of CPUS, to place bounds on some sizings */
|
/** Number of CPUS, to place bounds on some sizings */
|
||||||
static final int NCPU = Runtime.getRuntime().availableProcessors();
|
static @Stable int NCPU;
|
||||||
|
|
||||||
|
static {
|
||||||
|
runtimeSetup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from JVM when loading an AOT cache.
|
||||||
|
private static void runtimeSetup() {
|
||||||
|
NCPU = Runtime.getRuntime().availableProcessors();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialized pseudo-fields, provided only for jdk7 compatibility.
|
* Serialized pseudo-fields, provided only for jdk7 compatibility.
|
||||||
|
@ -57,6 +57,11 @@ public final class Unsafe {
|
|||||||
|
|
||||||
private static native void registerNatives();
|
private static native void registerNatives();
|
||||||
static {
|
static {
|
||||||
|
runtimeSetup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from JVM when loading an AOT cache
|
||||||
|
private static void runtimeSetup() {
|
||||||
registerNatives();
|
registerNatives();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
test/hotspot/jtreg/ProblemList-AotJdk.txt
Normal file
18
test/hotspot/jtreg/ProblemList-AotJdk.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
runtime/modules/PatchModule/PatchModuleClassList.java 0000000 generic-all
|
||||||
|
runtime/NMT/NMTWithCDS.java 0000000 generic-all
|
||||||
|
runtime/symbols/TestSharedArchiveConfigFile.java 0000000 generic-all
|
||||||
|
|
||||||
|
gc/arguments/TestSerialHeapSizeFlags.java 0000000 generic-all
|
||||||
|
gc/TestAllocateHeapAtMultiple.java 0000000 generic-all
|
||||||
|
gc/TestAllocateHeapAt.java 0000000 generic-all
|
||||||
|
|
||||||
|
# use -Xshare
|
||||||
|
serviceability/sa/ClhsdbCDSJstackPrintAll.java 0000000 generic-all
|
||||||
|
serviceability/sa/ClhsdbCDSCore.java 0000000 generic-all
|
||||||
|
serviceability/sa/CDSJMapClstats.java 0000000 generic-all
|
||||||
|
compiler/intrinsics/klass/TestIsPrimitive.java 0000000 generic-all
|
||||||
|
|
||||||
|
# This test is incompatible with AOTClassLinking.
|
||||||
|
# It has the assumption about unresolved Integer.
|
||||||
|
# However when AOTClassLinking is enabled, Integer is always resolved at JVM start-up.
|
||||||
|
compiler/ciReplay/TestInliningProtectionDomain.java 0000000 generic-all
|
@ -74,6 +74,7 @@ requires.properties= \
|
|||||||
vm.rtm.compiler \
|
vm.rtm.compiler \
|
||||||
vm.cds \
|
vm.cds \
|
||||||
vm.cds.custom.loaders \
|
vm.cds.custom.loaders \
|
||||||
|
vm.cds.supports.aot.class.linking \
|
||||||
vm.cds.write.archived.java.heap \
|
vm.cds.write.archived.java.heap \
|
||||||
vm.continuations \
|
vm.continuations \
|
||||||
vm.jvmti \
|
vm.jvmti \
|
||||||
|
@ -56,6 +56,7 @@ hotspot_runtime_no_cds = \
|
|||||||
hotspot_runtime_non_cds_mode = \
|
hotspot_runtime_non_cds_mode = \
|
||||||
runtime \
|
runtime \
|
||||||
-runtime/cds/CheckSharingWithDefaultArchive.java \
|
-runtime/cds/CheckSharingWithDefaultArchive.java \
|
||||||
|
-runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java \
|
||||||
-runtime/cds/appcds/dynamicArchive/DynamicSharedSymbols.java \
|
-runtime/cds/appcds/dynamicArchive/DynamicSharedSymbols.java \
|
||||||
-runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchive.java \
|
-runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchive.java \
|
||||||
-runtime/cds/appcds/jcmd
|
-runtime/cds/appcds/jcmd
|
||||||
@ -430,6 +431,7 @@ hotspot_cds_only = \
|
|||||||
|
|
||||||
hotspot_appcds_dynamic = \
|
hotspot_appcds_dynamic = \
|
||||||
runtime/cds/appcds/ \
|
runtime/cds/appcds/ \
|
||||||
|
-runtime/cds/appcds/applications \
|
||||||
-runtime/cds/appcds/cacheObject \
|
-runtime/cds/appcds/cacheObject \
|
||||||
-runtime/cds/appcds/complexURI \
|
-runtime/cds/appcds/complexURI \
|
||||||
-runtime/cds/appcds/customLoader \
|
-runtime/cds/appcds/customLoader \
|
||||||
@ -447,10 +449,12 @@ hotspot_appcds_dynamic = \
|
|||||||
-runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.java \
|
-runtime/cds/appcds/jvmti/redefineClasses/OldClassAndRedefineClass.java \
|
||||||
-runtime/cds/appcds/lambdaForm/DefaultClassListLFInvokers.java \
|
-runtime/cds/appcds/lambdaForm/DefaultClassListLFInvokers.java \
|
||||||
-runtime/cds/appcds/methodHandles \
|
-runtime/cds/appcds/methodHandles \
|
||||||
|
-runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java \
|
||||||
-runtime/cds/appcds/sharedStrings \
|
-runtime/cds/appcds/sharedStrings \
|
||||||
-runtime/cds/appcds/resolvedConstants \
|
-runtime/cds/appcds/resolvedConstants \
|
||||||
-runtime/cds/appcds/ArchiveRelocationTest.java \
|
-runtime/cds/appcds/ArchiveRelocationTest.java \
|
||||||
-runtime/cds/appcds/BadBSM.java \
|
-runtime/cds/appcds/BadBSM.java \
|
||||||
|
-runtime/cds/appcds/CommandLineFlagCombo.java \
|
||||||
-runtime/cds/appcds/DumpClassList.java \
|
-runtime/cds/appcds/DumpClassList.java \
|
||||||
-runtime/cds/appcds/DumpClassListWithLF.java \
|
-runtime/cds/appcds/DumpClassListWithLF.java \
|
||||||
-runtime/cds/appcds/DumpRuntimeClassesTest.java \
|
-runtime/cds/appcds/DumpRuntimeClassesTest.java \
|
||||||
@ -518,6 +522,61 @@ hotspot_cds_epsilongc = \
|
|||||||
runtime/cds/appcds/jigsaw \
|
runtime/cds/appcds/jigsaw \
|
||||||
runtime/cds/appcds/loaderConstraints
|
runtime/cds/appcds/loaderConstraints
|
||||||
|
|
||||||
|
# Run CDS tests with -XX:+AOTClassLinking. This should include most CDS tests, except for
|
||||||
|
# those that rely on redefining classes that are already archived.
|
||||||
|
hotspot_aot_classlinking = \
|
||||||
|
runtime/cds \
|
||||||
|
-runtime/cds/appcds/aotClassLinking \
|
||||||
|
-runtime/cds/appcds/BadBSM.java \
|
||||||
|
-runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
|
||||||
|
-runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
|
||||||
|
-runtime/cds/appcds/CDSandJFR.java \
|
||||||
|
-runtime/cds/appcds/customLoader/HelloCustom_JFR.java \
|
||||||
|
-runtime/cds/appcds/customLoader/ParallelTestMultiFP.java \
|
||||||
|
-runtime/cds/appcds/customLoader/ParallelTestSingleFP.java \
|
||||||
|
-runtime/cds/appcds/customLoader/SameNameInTwoLoadersTest.java \
|
||||||
|
-runtime/cds/appcds/DumpClassListWithLF.java \
|
||||||
|
-runtime/cds/appcds/dynamicArchive/ModulePath.java \
|
||||||
|
-runtime/cds/appcds/dynamicArchive/LambdaInBaseArchive.java \
|
||||||
|
-runtime/cds/appcds/dynamicArchive/LambdasInTwoArchives.java \
|
||||||
|
-runtime/cds/appcds/HelloExtTest.java \
|
||||||
|
-runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java \
|
||||||
|
-runtime/cds/appcds/javaldr/GCDuringDump.java \
|
||||||
|
-runtime/cds/appcds/javaldr/LockDuringDump.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/JigsawOptionsCombo.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/AddOpens.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/AddModules.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/JvmtiAddPath.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/MainModuleOnly.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/overridetests/OverrideTests.java \
|
||||||
|
-runtime/cds/appcds/jigsaw/RedefineClassesInModuleGraph.java \
|
||||||
|
-runtime/cds/appcds/JvmtiAddPath.java \
|
||||||
|
-runtime/cds/appcds/jvmti \
|
||||||
|
-runtime/cds/appcds/LambdaProxyClasslist.java \
|
||||||
|
-runtime/cds/appcds/loaderConstraints/LoaderConstraintsTest.java \
|
||||||
|
-runtime/cds/appcds/redefineClass \
|
||||||
|
-runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java \
|
||||||
|
-runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java \
|
||||||
|
-runtime/cds/appcds/resolvedConstants/ResolvedConstants.java \
|
||||||
|
-runtime/cds/appcds/RewriteBytecodesTest.java \
|
||||||
|
-runtime/cds/appcds/SpecifySysLoaderProp.java \
|
||||||
|
-runtime/cds/appcds/StaticArchiveWithLambda.java \
|
||||||
|
-runtime/cds/appcds/TestEpsilonGCWithCDS.java \
|
||||||
|
-runtime/cds/appcds/TestParallelGCWithCDS.java \
|
||||||
|
-runtime/cds/appcds/TestSerialGCWithCDS.java \
|
||||||
|
-runtime/cds/appcds/TestZGCWithCDS.java \
|
||||||
|
-runtime/cds/appcds/TestWithProfiler.java \
|
||||||
|
-runtime/cds/serviceability/ReplaceCriticalClassesForSubgraphs.java \
|
||||||
|
-runtime/cds/serviceability/ReplaceCriticalClasses.java \
|
||||||
|
-runtime/cds/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java \
|
||||||
|
-runtime/cds/serviceability/transformRelatedClasses/TransformSuperAndSubClasses.java \
|
||||||
|
-runtime/cds/serviceability/transformRelatedClasses/TransformSuperSubTwoPckgs.java
|
||||||
|
|
||||||
# needs -nativepath:<output>/images/test/hotspot/jtreg/native/
|
# needs -nativepath:<output>/images/test/hotspot/jtreg/native/
|
||||||
hotspot_metaspace = \
|
hotspot_metaspace = \
|
||||||
gtest/MetaspaceGtests.java \
|
gtest/MetaspaceGtests.java \
|
||||||
@ -537,6 +596,10 @@ tier1_runtime_appcds_exclude = \
|
|||||||
runtime/cds/appcds/ \
|
runtime/cds/appcds/ \
|
||||||
-:tier1_runtime_appcds
|
-:tier1_runtime_appcds
|
||||||
|
|
||||||
|
tier1_runtime_no_cds = \
|
||||||
|
:tier1_runtime \
|
||||||
|
-runtime/cds
|
||||||
|
|
||||||
# This group should be executed with "jtreg -Dtest.cds.run.with.jfr=true ..."
|
# This group should be executed with "jtreg -Dtest.cds.run.with.jfr=true ..."
|
||||||
# to test interaction between AppCDS and JFR. It also has the side effect of
|
# to test interaction between AppCDS and JFR. It also has the side effect of
|
||||||
# testing JVMTI ClassFileLoadHook.
|
# testing JVMTI ClassFileLoadHook.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -67,8 +67,23 @@ import jdk.test.lib.cds.CDSTestUtils;
|
|||||||
import jdk.test.lib.cds.CDSOptions;
|
import jdk.test.lib.cds.CDSOptions;
|
||||||
import jdk.test.lib.Platform;
|
import jdk.test.lib.Platform;
|
||||||
import jdk.test.lib.process.OutputAnalyzer;
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jtreg.SkippedException;
|
||||||
|
|
||||||
public class SharedBaseAddress {
|
public class SharedBaseAddress {
|
||||||
|
static final boolean skipUncompressedOopsTests;
|
||||||
|
static boolean checkSkipUncompressedOopsTests(String prop) {
|
||||||
|
String opts = System.getProperty(prop);
|
||||||
|
return opts.contains("+AOTClassLinking") &&
|
||||||
|
opts.matches(".*[+]Use[A-Za-z]+GC.*") && !opts.contains("+UseG1GC");
|
||||||
|
}
|
||||||
|
static {
|
||||||
|
// AOTClassLinking requires the ability to load archived heap objects. However,
|
||||||
|
// due to JDK-8341371, only G1GC supports loading archived heap objects
|
||||||
|
// with uncompressed oops.
|
||||||
|
skipUncompressedOopsTests =
|
||||||
|
checkSkipUncompressedOopsTests("test.vm.opts") ||
|
||||||
|
checkSkipUncompressedOopsTests("test.java.opts");
|
||||||
|
}
|
||||||
|
|
||||||
// shared base address test table for {32, 64}bit VM
|
// shared base address test table for {32, 64}bit VM
|
||||||
private static final String[] testTableShared = {
|
private static final String[] testTableShared = {
|
||||||
@ -100,6 +115,10 @@ public class SharedBaseAddress {
|
|||||||
int end = args[0].equals("0") ? mid : testTable.length;
|
int end = args[0].equals("0") ? mid : testTable.length;
|
||||||
boolean provoke = (args.length > 1 && args[1].equals("provoke"));
|
boolean provoke = (args.length > 1 && args[1].equals("provoke"));
|
||||||
|
|
||||||
|
if (provoke && skipUncompressedOopsTests) {
|
||||||
|
throw new SkippedException("Test skipped due to JDK-8341371");
|
||||||
|
}
|
||||||
|
|
||||||
// provoke == true: we want to increase the chance that mapping the generated archive at the designated base
|
// provoke == true: we want to increase the chance that mapping the generated archive at the designated base
|
||||||
// succeeds, to test Klass pointer encoding at that weird location. We do this by sizing heap + class space
|
// succeeds, to test Klass pointer encoding at that weird location. We do this by sizing heap + class space
|
||||||
// small, and by switching off compressed oops.
|
// small, and by switching off compressed oops.
|
||||||
|
203
test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java
Normal file
203
test/hotspot/jtreg/runtime/cds/appcds/AOTFlags.java
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary "AOT" aliases for traditional CDS command-line options
|
||||||
|
* @requires vm.cds
|
||||||
|
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
||||||
|
* @build Hello
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello
|
||||||
|
* @run driver AOTFlags
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
public class AOTFlags {
|
||||||
|
static String appJar = ClassFileInstaller.getJarPath("hello.jar");
|
||||||
|
static String aotConfigFile = "hello.aotconfig";
|
||||||
|
static String aotCacheFile = "hello.aot";
|
||||||
|
static String helloClass = "Hello";
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
positiveTests();
|
||||||
|
negativeTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void positiveTests() throws Exception {
|
||||||
|
// (1) Training Run
|
||||||
|
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTMode=record",
|
||||||
|
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train");
|
||||||
|
out.shouldContain("Hello World");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
|
||||||
|
// (2) Assembly Phase
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTMode=create",
|
||||||
|
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||||
|
"-XX:AOTCache=" + aotCacheFile,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-cp", appJar);
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "asm");
|
||||||
|
out.shouldContain("Dumping shared data to file:");
|
||||||
|
out.shouldMatch("cds.*hello[.]aot");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
|
||||||
|
// (3) Production Run with AOTCache
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTCache=" + aotCacheFile,
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||||
|
out.shouldContain("Opened archive hello.aot.");
|
||||||
|
out.shouldContain("Hello World");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
|
||||||
|
// (4) AOTMode=off
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTCache=" + aotCacheFile,
|
||||||
|
"--show-version",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-XX:AOTMode=off",
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||||
|
out.shouldNotContain(", sharing");
|
||||||
|
out.shouldNotContain("Opened archive hello.aot.");
|
||||||
|
out.shouldContain("Hello World");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
|
||||||
|
// (5) AOTMode=auto
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTCache=" + aotCacheFile,
|
||||||
|
"--show-version",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-XX:AOTMode=auto",
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||||
|
out.shouldContain(", sharing");
|
||||||
|
out.shouldContain("Opened archive hello.aot.");
|
||||||
|
out.shouldContain("Hello World");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
|
||||||
|
// (5) AOTMode=on
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTCache=" + aotCacheFile,
|
||||||
|
"--show-version",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-XX:AOTMode=on",
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "prod");
|
||||||
|
out.shouldContain(", sharing");
|
||||||
|
out.shouldContain("Opened archive hello.aot.");
|
||||||
|
out.shouldContain("Hello World");
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void negativeTests() throws Exception {
|
||||||
|
// (1) Mixing old and new options
|
||||||
|
String mixOldNewErrSuffix = " cannot be used at the same time with -Xshare:on, -Xshare:auto, "
|
||||||
|
+ "-Xshare:off, -Xshare:dump, DumpLoadedClassList, SharedClassListFile, "
|
||||||
|
+ "or SharedArchiveFile";
|
||||||
|
|
||||||
|
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-Xshare:off",
|
||||||
|
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("Option AOTConfiguration" + mixOldNewErrSuffix);
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:SharedArchiveFile=" + aotCacheFile,
|
||||||
|
"-XX:AOTCache=" + aotCacheFile,
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("Option AOTCache" + mixOldNewErrSuffix);
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
|
||||||
|
// (2) Use AOTConfiguration without AOTMode
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("AOTConfiguration can only be used with -XX:AOTMode=record or -XX:AOTMode=create");
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
|
||||||
|
// (3) Use AOTMode without AOTConfiguration
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTMode=record",
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("-XX:AOTMode=record cannot be used without setting AOTConfiguration");
|
||||||
|
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTMode=create",
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("-XX:AOTMode=create cannot be used without setting AOTConfiguration");
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
|
||||||
|
// (4) Bad AOTMode
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTMode=foo",
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("Unrecognized value foo for AOTMode. Must be one of the following: off, record, create, auto, on");
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
|
||||||
|
// (5) AOTCache specified with -XX:AOTMode=record
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTMode=record",
|
||||||
|
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||||
|
"-XX:AOTCache=" + aotCacheFile,
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("AOTCache must not be specified when using -XX:AOTMode=record");
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
|
||||||
|
// (5) AOTCache not specified with -XX:AOTMode=create
|
||||||
|
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||||
|
"-XX:AOTMode=create",
|
||||||
|
"-XX:AOTConfiguration=" + aotConfigFile,
|
||||||
|
"-cp", appJar, helloClass);
|
||||||
|
|
||||||
|
out = CDSTestUtils.executeAndLog(pb, "neg");
|
||||||
|
out.shouldContain("AOTCache must be specified when using -XX:AOTMode=create");
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -190,13 +190,20 @@ public class ClassPathAttr {
|
|||||||
Files.copy(Paths.get(cp), Paths.get(nonExistPath),
|
Files.copy(Paths.get(cp), Paths.get(nonExistPath),
|
||||||
StandardCopyOption.REPLACE_EXISTING);
|
StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
TestCommon.run(
|
CDSTestUtils.Result result = TestCommon.run(
|
||||||
"-Xlog:class+path",
|
"-Xlog:class+path",
|
||||||
"-cp", cp,
|
"-cp", cp,
|
||||||
"CpAttr6")
|
"CpAttr6");
|
||||||
.assertNormalExit(output -> {
|
if (CDSTestUtils.isAOTClassLinkingEnabled()) {
|
||||||
output.shouldMatch("Archived non-system classes are disabled because the file .*cpattrX.jar exists");
|
result.assertAbnormalExit(output -> {
|
||||||
});
|
output.shouldMatch("CDS archive has aot-linked classes. It cannot be used because the file .*cpattrX.jar exists");
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
result.assertNormalExit(output -> {
|
||||||
|
output.shouldMatch("Archived non-system classes are disabled because the file .*cpattrX.jar exists");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testClassPathAttrJarOnCP() throws Exception {
|
static void testClassPathAttrJarOnCP() throws Exception {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -64,7 +64,11 @@ public class LambdaContainsOldInf {
|
|||||||
OutputAnalyzer output = CDSTestUtils.createArchiveAndCheck(opts);
|
OutputAnalyzer output = CDSTestUtils.createArchiveAndCheck(opts);
|
||||||
TestCommon.checkExecReturn(output, 0, true,
|
TestCommon.checkExecReturn(output, 0, true,
|
||||||
"Skipping OldProvider: Old class has been linked");
|
"Skipping OldProvider: Old class has been linked");
|
||||||
output.shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda.*0x.*:.*Old.class.has.been.linked");
|
if (CDSTestUtils.isAOTClassLinkingEnabled()) {
|
||||||
|
output.shouldMatch("Cannot aot-resolve Lambda proxy because OldProvider is excluded");
|
||||||
|
} else {
|
||||||
|
output.shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda.*0x.*:.*Old.class.has.been.linked");
|
||||||
|
}
|
||||||
|
|
||||||
// run with archive
|
// run with archive
|
||||||
CDSOptions runOpts = (new CDSOptions())
|
CDSOptions runOpts = (new CDSOptions())
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -68,7 +68,11 @@ public class LambdaWithOldClass {
|
|||||||
.addSuffix(mainClass);
|
.addSuffix(mainClass);
|
||||||
OutputAnalyzer output = CDSTestUtils.runWithArchive(runOpts);
|
OutputAnalyzer output = CDSTestUtils.runWithArchive(runOpts);
|
||||||
output.shouldContain("[class,load] LambdaWithOldClassApp source: shared objects file")
|
output.shouldContain("[class,load] LambdaWithOldClassApp source: shared objects file")
|
||||||
.shouldMatch(".class.load. LambdaWithOldClassApp[$][$]Lambda.*/0x.*source:.*shared objects file")
|
|
||||||
.shouldHaveExitValue(0);
|
.shouldHaveExitValue(0);
|
||||||
|
if (!CDSTestUtils.isAOTClassLinkingEnabled()) {
|
||||||
|
// With AOTClassLinking, we don't archive any lambda with old classes in the method
|
||||||
|
// signatures.
|
||||||
|
output.shouldMatch(".class.load. LambdaWithOldClassApp[$][$]Lambda.*/0x.*source:.*shared objects file");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -43,6 +43,11 @@ public class LambdaWithUseImplMethodHandle {
|
|||||||
|
|
||||||
// See pkg2/Child.jcod for details about the condition that triggers JDK-8290417
|
// See pkg2/Child.jcod for details about the condition that triggers JDK-8290417
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
test(false);
|
||||||
|
test(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test(boolean aotClassLinking) throws Exception {
|
||||||
String appJar = ClassFileInstaller.getJarPath("test.jar");
|
String appJar = ClassFileInstaller.getJarPath("test.jar");
|
||||||
String mainClass = "LambdaWithUseImplMethodHandleApp";
|
String mainClass = "LambdaWithUseImplMethodHandleApp";
|
||||||
String expectedMsg = "Called BaseWithProtectedMethod::protectedMethod";
|
String expectedMsg = "Called BaseWithProtectedMethod::protectedMethod";
|
||||||
@ -57,6 +62,9 @@ public class LambdaWithUseImplMethodHandle {
|
|||||||
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
|
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
|
||||||
"-cp", appJar)
|
"-cp", appJar)
|
||||||
.setArchiveName(archiveName);
|
.setArchiveName(archiveName);
|
||||||
|
if (aotClassLinking) {
|
||||||
|
opts.addPrefix("-XX:+AOTClassLinking");
|
||||||
|
}
|
||||||
CDSTestUtils.createArchiveAndCheck(opts);
|
CDSTestUtils.createArchiveAndCheck(opts);
|
||||||
|
|
||||||
// run with archive
|
// run with archive
|
||||||
|
@ -148,6 +148,7 @@ public class TestParallelGCWithCDS {
|
|||||||
} else {
|
} else {
|
||||||
String pattern = "((Too small maximum heap)" +
|
String pattern = "((Too small maximum heap)" +
|
||||||
"|(GC triggered before VM initialization completed)" +
|
"|(GC triggered before VM initialization completed)" +
|
||||||
|
"|(CDS archive has aot-linked classes but the archived heap objects cannot be loaded)" +
|
||||||
"|(Initial heap size set to a larger value than the maximum heap size)" +
|
"|(Initial heap size set to a larger value than the maximum heap size)" +
|
||||||
"|(java.lang.OutOfMemoryError)" +
|
"|(java.lang.OutOfMemoryError)" +
|
||||||
"|(Error: A JNI error has occurred, please check your installation and try again))";
|
"|(Error: A JNI error has occurred, please check your installation and try again))";
|
||||||
|
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @requires vm.cds
|
||||||
|
* @requires vm.cds.supports.aot.class.linking
|
||||||
|
* @requires vm.flagless
|
||||||
|
* @summary Disable CDS when incompatible options related to AOTClassLinking are used
|
||||||
|
* @library /test/jdk/lib/testlibrary
|
||||||
|
* /test/lib
|
||||||
|
* /test/hotspot/jtreg/runtime/cds/appcds
|
||||||
|
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
||||||
|
* @build Hello
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello
|
||||||
|
* @run driver AOTClassLinkingVMOptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
|
||||||
|
public class AOTClassLinkingVMOptions {
|
||||||
|
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||||
|
|
||||||
|
static int testCaseNum = 0;
|
||||||
|
static void testCase(String s) {
|
||||||
|
testCaseNum++;
|
||||||
|
System.out.println("Test case " + testCaseNum + ": " + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
TestCommon.testDump(appJar, TestCommon.list("Hello"),
|
||||||
|
"-XX:+AOTClassLinking");
|
||||||
|
|
||||||
|
testCase("Archived full module graph must be enabled at runtime");
|
||||||
|
TestCommon.run("-cp", appJar, "-Djdk.module.validation=1", "Hello")
|
||||||
|
.assertAbnormalExit("CDS archive has aot-linked classes." +
|
||||||
|
" It cannot be used when archived full module graph is not used");
|
||||||
|
|
||||||
|
testCase("Cannot use -Djava.system.class.loader");
|
||||||
|
TestCommon.run("-cp", appJar, "-Djava.system.class.loader=dummy", "Hello")
|
||||||
|
.assertAbnormalExit("CDS archive has aot-linked classes." +
|
||||||
|
" It cannot be used when the java.system.class.loader property is specified.");
|
||||||
|
|
||||||
|
testCase("Cannot use a different main module");
|
||||||
|
TestCommon.run("-cp", appJar, "-Xlog:cds", "-m", "jdk.compiler/com.sun.tools.javac.Main")
|
||||||
|
.assertAbnormalExit("CDS archive has aot-linked classes." +
|
||||||
|
" It cannot be used when archived full module graph is not used.");
|
||||||
|
testCase("Cannot use security manager");
|
||||||
|
TestCommon.run("-cp", appJar, "-Xlog:cds", "-Djava.security.manager=allow")
|
||||||
|
.assertAbnormalExit("CDS archive has aot-linked classes." +
|
||||||
|
" It cannot be used with -Djava.security.manager=allow.");
|
||||||
|
TestCommon.run("-cp", appJar, "-Xlog:cds", "-Djava.security.manager=default")
|
||||||
|
.assertAbnormalExit("CDS archive has aot-linked classes." +
|
||||||
|
" It cannot be used with -Djava.security.manager=default.");
|
||||||
|
|
||||||
|
// NOTE: tests for ClassFileLoadHook + AOTClassLinking is in
|
||||||
|
// ../jvmti/ClassFileLoadHookTest.java
|
||||||
|
|
||||||
|
boolean dynamicMode = Boolean.getBoolean("test.dynamic.cds.archive");
|
||||||
|
if (!dynamicMode) {
|
||||||
|
// These tests need to dump the full module graph, which is not possible with
|
||||||
|
// dynamic dump.
|
||||||
|
modulePathTests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modulePathTests() throws Exception {
|
||||||
|
CDSModulePathUtils.init();
|
||||||
|
|
||||||
|
testCase("Cannot use mis-matched module path");
|
||||||
|
String goodModulePath = CDSModulePathUtils.getModulesDir().toString();
|
||||||
|
TestCommon.testDump(null, CDSModulePathUtils.getAppClasses(),
|
||||||
|
"--module-path", goodModulePath,
|
||||||
|
"-XX:+AOTClassLinking",
|
||||||
|
"-m", CDSModulePathUtils.MAIN_MODULE);
|
||||||
|
TestCommon.run("-Xlog:cds",
|
||||||
|
"--module-path", goodModulePath,
|
||||||
|
"-m", CDSModulePathUtils.MAIN_MODULE)
|
||||||
|
.assertNormalExit("Using AOT-linked classes: true");
|
||||||
|
|
||||||
|
TestCommon.run("-Xlog:cds",
|
||||||
|
"--module-path", goodModulePath + "/bad",
|
||||||
|
"-m", CDSModulePathUtils.MAIN_MODULE)
|
||||||
|
.assertAbnormalExit("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used.");
|
||||||
|
|
||||||
|
testCase("Cannot use mis-matched --add-modules");
|
||||||
|
TestCommon.testDump(null, CDSModulePathUtils.getAppClasses(),
|
||||||
|
"--module-path", goodModulePath,
|
||||||
|
"-XX:+AOTClassLinking",
|
||||||
|
"--add-modules", CDSModulePathUtils.MAIN_MODULE);
|
||||||
|
TestCommon.run("-Xlog:cds",
|
||||||
|
"--module-path", goodModulePath,
|
||||||
|
"--add-modules", CDSModulePathUtils.MAIN_MODULE,
|
||||||
|
CDSModulePathUtils.MAIN_CLASS)
|
||||||
|
.assertNormalExit("Using AOT-linked classes: true");
|
||||||
|
|
||||||
|
TestCommon.run("-Xlog:cds",
|
||||||
|
"--module-path", goodModulePath + "/bad",
|
||||||
|
"--add-modules", CDSModulePathUtils.TEST_MODULE,
|
||||||
|
CDSModulePathUtils.MAIN_CLASS)
|
||||||
|
.assertAbnormalExit("Mismatched --add-modules module name(s)",
|
||||||
|
"CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: enhance and move this class to jdk.test.lib.cds.CDSModulePathUtils
|
||||||
|
|
||||||
|
class CDSModulePathUtils {
|
||||||
|
private static String TEST_SRC = System.getProperty("test.root");
|
||||||
|
private static Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir());
|
||||||
|
private static Path SRC_DIR = Paths.get(TEST_SRC, "runtime/cds/appcds/jigsaw/modulepath/src");
|
||||||
|
private static Path MODS_DIR = Paths.get("mods");
|
||||||
|
|
||||||
|
public static String MAIN_MODULE = "com.bars";
|
||||||
|
public static String TEST_MODULE = "com.foos";
|
||||||
|
|
||||||
|
public static String MAIN_CLASS = "com.bars.Main";
|
||||||
|
public static String TEST_CLASS = "com.foos.Test";
|
||||||
|
private static String appClasses[] = {MAIN_CLASS, TEST_CLASS};
|
||||||
|
|
||||||
|
private static Path modulesDir;
|
||||||
|
|
||||||
|
// This directory contains all the modular jar files
|
||||||
|
// $USER_DIR/modules/com.bars.jar
|
||||||
|
// $USER_DIR/modules/com.foos.jar
|
||||||
|
static Path getModulesDir() {
|
||||||
|
return modulesDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String[] getAppClasses() {
|
||||||
|
return appClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init() throws Exception {
|
||||||
|
JarBuilder.compileModule(SRC_DIR.resolve(TEST_MODULE),
|
||||||
|
MODS_DIR.resolve(TEST_MODULE),
|
||||||
|
null);
|
||||||
|
JarBuilder.compileModule(SRC_DIR.resolve(MAIN_MODULE),
|
||||||
|
MODS_DIR.resolve(MAIN_MODULE),
|
||||||
|
MODS_DIR.toString());
|
||||||
|
|
||||||
|
String PATH_LIBS = "modules";
|
||||||
|
modulesDir = Files.createTempDirectory(USER_DIR, PATH_LIBS);
|
||||||
|
Path mainJar = modulesDir.resolve(MAIN_MODULE + ".jar");
|
||||||
|
Path testJar = modulesDir.resolve(TEST_MODULE + ".jar");
|
||||||
|
|
||||||
|
// modylibs contains both modules com.foos.jar, com.bars.jar
|
||||||
|
// build com.foos.jar
|
||||||
|
String classes = MODS_DIR.resolve(TEST_MODULE).toString();
|
||||||
|
JarBuilder.createModularJar(testJar.toString(), classes, TEST_CLASS);
|
||||||
|
|
||||||
|
// build com.bars.jar
|
||||||
|
classes = MODS_DIR.resolve(MAIN_MODULE).toString();
|
||||||
|
JarBuilder.createModularJar(mainJar.toString(), classes, MAIN_CLASS);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// AOT-linked classes are loaded during VM bootstrap by the C++ class AOTLinkedClassBulkLoader.
|
||||||
|
// Make sure that the Module, Package, CodeSource and ProtectionDomain of these classes are
|
||||||
|
// set up properly.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test id=static
|
||||||
|
* @requires vm.cds.supports.aot.class.linking
|
||||||
|
* @library /test/jdk/lib/testlibrary /test/lib
|
||||||
|
* @build InitiatingLoaderTester
|
||||||
|
* @build BulkLoaderTest
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester
|
||||||
|
* @run driver BulkLoaderTest STATIC
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test id=dynamic
|
||||||
|
* @requires vm.cds.supports.aot.class.linking
|
||||||
|
* @library /test/jdk/lib/testlibrary /test/lib
|
||||||
|
* @build InitiatingLoaderTester
|
||||||
|
* @build BulkLoaderTest
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester
|
||||||
|
* @run driver BulkLoaderTest DYNAMIC
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.StackWalker.StackFrame;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.Set;
|
||||||
|
import jdk.test.lib.cds.CDSAppTester;
|
||||||
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
|
public class BulkLoaderTest {
|
||||||
|
static final String appJar = ClassFileInstaller.getJarPath("BulkLoaderTestApp.jar");
|
||||||
|
static final String mainClass = "BulkLoaderTestApp";
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
Tester t = new Tester();
|
||||||
|
|
||||||
|
// Run with archived FMG loaded
|
||||||
|
t.run(args);
|
||||||
|
|
||||||
|
// Run with an extra classpath -- archived FMG can still load.
|
||||||
|
{
|
||||||
|
String extraVmArgs[] = {
|
||||||
|
"-cp",
|
||||||
|
appJar + File.pathSeparator + "foobar.jar"
|
||||||
|
};
|
||||||
|
OutputAnalyzer out = t.productionRun(extraVmArgs);
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run without archived FMG -- fail to load
|
||||||
|
{
|
||||||
|
String extraVmArgs[] = {
|
||||||
|
"-Xshare:on",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"-Djdk.module.showModuleResolution=true"
|
||||||
|
};
|
||||||
|
t.setCheckExitValue(false);
|
||||||
|
OutputAnalyzer out = t.productionRun(extraVmArgs);
|
||||||
|
out.shouldHaveExitValue(1);
|
||||||
|
out.shouldContain("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used.");
|
||||||
|
t.setCheckExitValue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Tester extends CDSAppTester {
|
||||||
|
public Tester() {
|
||||||
|
super(mainClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String classpath(RunMode runMode) {
|
||||||
|
return appJar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] vmArgs(RunMode runMode) {
|
||||||
|
return new String[] {
|
||||||
|
"-Xlog:cds,cds+aot+load",
|
||||||
|
"-XX:+AOTClassLinking",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] appCommandLine(RunMode runMode) {
|
||||||
|
return new String[] {
|
||||||
|
mainClass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BulkLoaderTestApp {
|
||||||
|
static String allPerms = "null.*<no principals>.*java.security.Permissions.*,*java.security.AllPermission.*<all permissions>.*<all actions>";
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
checkClasses();
|
||||||
|
checkInitiatingLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the ClassLoader/Module/Package/ProtectionDomain/CodeSource of classes that are aot-linked
|
||||||
|
static void checkClasses() throws Exception {
|
||||||
|
check(String.class,
|
||||||
|
"null", // loader
|
||||||
|
"module java.base",
|
||||||
|
"package java.lang",
|
||||||
|
"null",
|
||||||
|
allPerms);
|
||||||
|
|
||||||
|
check(Class.forName("sun.util.logging.internal.LoggingProviderImpl"),
|
||||||
|
"null",
|
||||||
|
"module java.logging",
|
||||||
|
"package sun.util.logging.internal",
|
||||||
|
"null",
|
||||||
|
allPerms);
|
||||||
|
|
||||||
|
|
||||||
|
check(javax.tools.FileObject.class,
|
||||||
|
"^jdk.internal.loader.ClassLoaders[$]PlatformClassLoader@",
|
||||||
|
"module java.compiler",
|
||||||
|
"package javax.tools",
|
||||||
|
"jrt:/java.compiler <no signer certificates>",
|
||||||
|
"jdk.internal.loader.ClassLoaders[$]PlatformClassLoader.*<no principals>.*java.security.Permissions");
|
||||||
|
|
||||||
|
check(BulkLoaderTestApp.class,
|
||||||
|
"jdk.internal.loader.ClassLoaders[$]AppClassLoader@",
|
||||||
|
"^unnamed module @",
|
||||||
|
"package ",
|
||||||
|
"file:.*BulkLoaderTestApp.jar <no signer certificates>",
|
||||||
|
"jdk.internal.loader.ClassLoaders[$]AppClassLoader.*<no principals>.*java.security.Permissions");
|
||||||
|
|
||||||
|
check(Class.forName("com.sun.tools.javac.Main"),
|
||||||
|
"jdk.internal.loader.ClassLoaders[$]AppClassLoader@",
|
||||||
|
"module jdk.compiler",
|
||||||
|
"package com.sun.tools.javac",
|
||||||
|
"jrt:/jdk.compiler <no signer certificates>",
|
||||||
|
"jdk.internal.loader.ClassLoaders[$]AppClassLoader.*<no principals>.*java.security.Permissions");
|
||||||
|
|
||||||
|
doit(() -> {
|
||||||
|
Class<?> lambdaClass = MyUtil.getCallerClass(1);
|
||||||
|
check(lambdaClass,
|
||||||
|
"jdk.internal.loader.ClassLoaders[$]AppClassLoader@",
|
||||||
|
"unnamed module",
|
||||||
|
"package ",
|
||||||
|
"file:.*BulkLoaderTestApp.jar <no signer certificates>",
|
||||||
|
"jdk.internal.loader.ClassLoaders[$]AppClassLoader.*<no principals>.*java.security.Permissions");
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check(Class c, String loader, String module, String pkg, String codeSource, String protectionDomain) {
|
||||||
|
System.out.println("====================================================================");
|
||||||
|
System.out.println(c.getName() + ", loader = " + c.getClassLoader());
|
||||||
|
System.out.println(c.getName() + ", module = " + c.getModule());
|
||||||
|
System.out.println(c.getName() + ", package = " + c.getPackage());
|
||||||
|
System.out.println(c.getName() + ", CS = " + c.getProtectionDomain().getCodeSource());
|
||||||
|
System.out.println(c.getName() + ", PD = " + c.getProtectionDomain());
|
||||||
|
|
||||||
|
expectMatch("" + c.getClassLoader(), loader);
|
||||||
|
expectMatch("" + c.getModule(), module);
|
||||||
|
expectSame("" + c.getPackage(), pkg);
|
||||||
|
expectMatch("" + c.getProtectionDomain().getCodeSource(), codeSource);
|
||||||
|
expectMatch("" + c.getProtectionDomain(), protectionDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expectSame(String a, String b) {
|
||||||
|
if (!a.equals(b)) {
|
||||||
|
throw new RuntimeException("Expected \"" + b + "\" but got \"" + a + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void expectMatch(String string, String pattern) {
|
||||||
|
Matcher matcher = Pattern.compile(pattern, Pattern.DOTALL).matcher(string);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
throw new RuntimeException("Expected pattern \"" + pattern + "\" but got \"" + string + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void doit(Runnable t) {
|
||||||
|
t.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkInitiatingLoader() throws Exception {
|
||||||
|
try {
|
||||||
|
InitiatingLoaderTester.tryAccess();
|
||||||
|
} catch (IllegalAccessError t) {
|
||||||
|
if (t.getMessage().contains("cannot access class jdk.internal.misc.Unsafe (in module java.base)")) {
|
||||||
|
System.out.println("Expected exception:");
|
||||||
|
t.printStackTrace(System.out);
|
||||||
|
// Class.forName() should still work. We just can't resolve it in CP entries.
|
||||||
|
Class<?> c = Class.forName("jdk.internal.misc.Unsafe");
|
||||||
|
System.out.println("App loader can still resolve by name: " + c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected exception", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Should not have succeeded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyUtil {
|
||||||
|
// depth is 0-based -- i.e., depth==0 returns the class of the immediate caller of getCallerClass
|
||||||
|
static Class<?> getCallerClass(int depth) {
|
||||||
|
// Need to add the frame of the getCallerClass -- so the immediate caller (depth==0) of this method
|
||||||
|
// is at stack.get(1) == stack.get(depth+1);
|
||||||
|
StackWalker walker = StackWalker.getInstance(
|
||||||
|
Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE,
|
||||||
|
StackWalker.Option.SHOW_HIDDEN_FRAMES));
|
||||||
|
List<StackFrame> stack = walker.walk(s -> s.limit(depth+2).collect(Collectors.toList()));
|
||||||
|
return stack.get(depth+1).getDeclaringClass();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
class InitiatingLoaderTester {
|
||||||
|
public static Object tryAccess() {
|
||||||
|
return jdk.internal.misc.Unsafe.getUnsafe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
super class InitiatingLoaderTester
|
||||||
|
version 66:0
|
||||||
|
{
|
||||||
|
Method "<init>":"()V"
|
||||||
|
stack 1 locals 1
|
||||||
|
{
|
||||||
|
aload_0;
|
||||||
|
invokespecial Method java/lang/Object."<init>":"()V";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
public static Method tryAccess:"()Ljava/lang/Object;"
|
||||||
|
stack 2 locals 0
|
||||||
|
{
|
||||||
|
invokestatic Method jdk/internal/misc/Unsafe."getUnsafe":"()Ljdk/internal/misc/Unsafe;";
|
||||||
|
areturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end Class InitiatingLoaderTester
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,7 +27,7 @@
|
|||||||
* @bug 8214781 8293187
|
* @bug 8214781 8293187
|
||||||
* @summary Test for the -XX:ArchiveHeapTestClass flag
|
* @summary Test for the -XX:ArchiveHeapTestClass flag
|
||||||
* @requires vm.debug == true & vm.cds.write.archived.java.heap
|
* @requires vm.debug == true & vm.cds.write.archived.java.heap
|
||||||
* @modules java.base/sun.invoke.util java.logging
|
* @modules java.logging
|
||||||
* @library /test/jdk/lib/testlibrary /test/lib
|
* @library /test/jdk/lib/testlibrary /test/lib
|
||||||
* /test/hotspot/jtreg/runtime/cds/appcds
|
* /test/hotspot/jtreg/runtime/cds/appcds
|
||||||
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
||||||
@ -35,12 +35,13 @@
|
|||||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar
|
||||||
* CDSTestClassA CDSTestClassA$XX CDSTestClassA$YY
|
* CDSTestClassA CDSTestClassA$XX CDSTestClassA$YY
|
||||||
* CDSTestClassB CDSTestClassC CDSTestClassD
|
* CDSTestClassB CDSTestClassC CDSTestClassD
|
||||||
* CDSTestClassE CDSTestClassF CDSTestClassG
|
* CDSTestClassE CDSTestClassF CDSTestClassG CDSTestClassG$MyEnum CDSTestClassG$Wrapper
|
||||||
* pkg.ClassInPackage
|
* pkg.ClassInPackage
|
||||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello
|
||||||
* @run driver ArchiveHeapTestClass
|
* @run driver ArchiveHeapTestClass
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
import jdk.test.lib.Platform;
|
import jdk.test.lib.Platform;
|
||||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
import jdk.test.lib.process.OutputAnalyzer;
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
@ -151,19 +152,24 @@ public class ArchiveHeapTestClass {
|
|||||||
output = dumpBootAndHello(CDSTestClassD_name);
|
output = dumpBootAndHello(CDSTestClassD_name);
|
||||||
mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassD::archivedObjects");
|
mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassD::archivedObjects");
|
||||||
|
|
||||||
testCase("Use a disallowed class: in unnamed module but not in unname package");
|
if (!CDSTestUtils.isAOTClassLinkingEnabled()) {
|
||||||
output = dumpBootAndHello(CDSTestClassE_name);
|
testCase("Use a disallowed class: in unnamed module but not in unname package");
|
||||||
mustFail(output, "Class pkg.ClassInPackage not allowed in archive heap");
|
output = dumpBootAndHello(CDSTestClassE_name);
|
||||||
|
mustFail(output, "Class pkg.ClassInPackage not allowed in archive heap");
|
||||||
|
|
||||||
testCase("Use a disallowed class: not in java.base module");
|
testCase("Use a disallowed class: not in java.base module");
|
||||||
output = dumpBootAndHello(CDSTestClassF_name);
|
output = dumpBootAndHello(CDSTestClassF_name);
|
||||||
mustFail(output, "Class java.util.logging.Level not allowed in archive heap");
|
mustFail(output, "Class java.util.logging.Level not allowed in archive heap");
|
||||||
|
|
||||||
if (false) { // JDK-8293187
|
|
||||||
testCase("sun.invoke.util.Wrapper");
|
|
||||||
output = dumpBootAndHello(CDSTestClassG_name);
|
|
||||||
mustSucceed(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testCase("Complex enums");
|
||||||
|
output = dumpBootAndHello(CDSTestClassG_name, "-XX:+AOTClassLinking", "-Xlog:cds+class=debug");
|
||||||
|
mustSucceed(output);
|
||||||
|
|
||||||
|
TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:cds+heap,cds+init",
|
||||||
|
CDSTestClassG_name)
|
||||||
|
.assertNormalExit("init subgraph " + CDSTestClassG_name,
|
||||||
|
"Initialized from CDS");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,12 +177,27 @@ class CDSTestClassA {
|
|||||||
static final String output = "CDSTestClassA.<clinit> was executed";
|
static final String output = "CDSTestClassA.<clinit> was executed";
|
||||||
static Object[] archivedObjects;
|
static Object[] archivedObjects;
|
||||||
static {
|
static {
|
||||||
archivedObjects = new Object[5];
|
// The usual convention would be to call this here:
|
||||||
archivedObjects[0] = output;
|
// CDS.initializeFromArchive(CDSTestClassA.class);
|
||||||
archivedObjects[1] = new CDSTestClassA[0];
|
// However, the CDS class is not exported to the unnamed module by default,
|
||||||
archivedObjects[2] = new YY();
|
// and we don't want to use "--add-exports java.base/jdk.internal.misc=ALL-UNNAMED", as
|
||||||
archivedObjects[3] = new int[0];
|
// that would disable the archived full module graph, which will disable
|
||||||
archivedObjects[4] = new int[2][2];
|
// CDSConfig::is_using_aot_linked_classes().
|
||||||
|
//
|
||||||
|
// Instead, HeapShared::initialize_test_class_from_archive() will set up the
|
||||||
|
// "archivedObjects" field first, before calling CDSTestClassA.<clinit>. So
|
||||||
|
// if we see that archivedObjects is magically non-null here, that means
|
||||||
|
// it has been restored from the CDS archive.
|
||||||
|
if (archivedObjects == null) {
|
||||||
|
archivedObjects = new Object[5];
|
||||||
|
archivedObjects[0] = output;
|
||||||
|
archivedObjects[1] = new CDSTestClassA[0];
|
||||||
|
archivedObjects[2] = new YY();
|
||||||
|
archivedObjects[3] = new int[0];
|
||||||
|
archivedObjects[4] = new int[2][2];
|
||||||
|
} else {
|
||||||
|
System.out.println("Initialized from CDS");
|
||||||
|
}
|
||||||
System.out.println(output);
|
System.out.println(output);
|
||||||
System.out.println("CDSTestClassA module = " + CDSTestClassA.class.getModule());
|
System.out.println("CDSTestClassA module = " + CDSTestClassA.class.getModule());
|
||||||
System.out.println("CDSTestClassA package = " + CDSTestClassA.class.getPackage());
|
System.out.println("CDSTestClassA package = " + CDSTestClassA.class.getPackage());
|
||||||
@ -269,8 +290,143 @@ class CDSTestClassF {
|
|||||||
class CDSTestClassG {
|
class CDSTestClassG {
|
||||||
static Object[] archivedObjects;
|
static Object[] archivedObjects;
|
||||||
static {
|
static {
|
||||||
// Not in java.base
|
if (archivedObjects == null) {
|
||||||
archivedObjects = new Object[1];
|
archivedObjects = new Object[13];
|
||||||
archivedObjects[0] = sun.invoke.util.Wrapper.BOOLEAN;
|
archivedObjects[0] = Wrapper.BOOLEAN;
|
||||||
|
archivedObjects[1] = Wrapper.INT.zero();
|
||||||
|
archivedObjects[2] = Wrapper.DOUBLE.zero();
|
||||||
|
archivedObjects[3] = MyEnum.DUMMY1;
|
||||||
|
|
||||||
|
archivedObjects[4] = Boolean.class;
|
||||||
|
archivedObjects[5] = Byte.class;
|
||||||
|
archivedObjects[6] = Character.class;
|
||||||
|
archivedObjects[7] = Short.class;
|
||||||
|
archivedObjects[8] = Integer.class;
|
||||||
|
archivedObjects[9] = Long.class;
|
||||||
|
archivedObjects[10] = Float.class;
|
||||||
|
archivedObjects[11] = Double.class;
|
||||||
|
archivedObjects[12] = Void.class;
|
||||||
|
} else {
|
||||||
|
System.out.println("Initialized from CDS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
if (archivedObjects[0] != Wrapper.BOOLEAN) {
|
||||||
|
throw new RuntimeException("Huh 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (archivedObjects[1] != Wrapper.INT.zero()) {
|
||||||
|
throw new RuntimeException("Huh 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (archivedObjects[2] != Wrapper.DOUBLE.zero()) {
|
||||||
|
throw new RuntimeException("Huh 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (archivedObjects[3] != MyEnum.DUMMY1) {
|
||||||
|
throw new RuntimeException("Huh 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MyEnum.BOOLEAN != true) {
|
||||||
|
throw new RuntimeException("Huh 10.1");
|
||||||
|
}
|
||||||
|
if (MyEnum.BYTE != -128) {
|
||||||
|
throw new RuntimeException("Huh 10.2");
|
||||||
|
}
|
||||||
|
if (MyEnum.CHAR != 'c') {
|
||||||
|
throw new RuntimeException("Huh 10.3");
|
||||||
|
}
|
||||||
|
if (MyEnum.SHORT != -12345) {
|
||||||
|
throw new RuntimeException("Huh 10.4");
|
||||||
|
}
|
||||||
|
if (MyEnum.INT != -123456) {
|
||||||
|
throw new RuntimeException("Huh 10.5");
|
||||||
|
}
|
||||||
|
if (MyEnum.LONG != 0x1234567890L) {
|
||||||
|
throw new RuntimeException("Huh 10.6");
|
||||||
|
}
|
||||||
|
if (MyEnum.LONG2 != -0x1234567890L) {
|
||||||
|
throw new RuntimeException("Huh 10.7");
|
||||||
|
}
|
||||||
|
if (MyEnum.FLOAT != 567891.0f) {
|
||||||
|
throw new RuntimeException("Huh 10.8");
|
||||||
|
}
|
||||||
|
if (MyEnum.DOUBLE != 12345678905678.890) {
|
||||||
|
throw new RuntimeException("Huh 10.9");
|
||||||
|
}
|
||||||
|
|
||||||
|
checkClass(4, Boolean.class);
|
||||||
|
checkClass(5, Byte.class);
|
||||||
|
checkClass(6, Character.class);
|
||||||
|
checkClass(7, Short.class);
|
||||||
|
checkClass(8, Integer.class);
|
||||||
|
checkClass(9, Long.class);
|
||||||
|
checkClass(10, Float.class);
|
||||||
|
checkClass(11, Double.class);
|
||||||
|
checkClass(12, Void.class);
|
||||||
|
|
||||||
|
System.out.println("Success!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkClass(int index, Class c) {
|
||||||
|
if (archivedObjects[index] != c) {
|
||||||
|
throw new RuntimeException("archivedObjects[" + index + "] should be " + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplified version of sun.invoke.util.Wrapper
|
||||||
|
public enum Wrapper {
|
||||||
|
// wrapperType simple primitiveType simple char emptyArray
|
||||||
|
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0]),
|
||||||
|
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0]),
|
||||||
|
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0])
|
||||||
|
;
|
||||||
|
|
||||||
|
public static final int COUNT = 10;
|
||||||
|
private static final Object DOUBLE_ZERO = (Double)(double)0;
|
||||||
|
|
||||||
|
private final Class<?> wrapperType;
|
||||||
|
private final Class<?> primitiveType;
|
||||||
|
private final char basicTypeChar;
|
||||||
|
private final String basicTypeString;
|
||||||
|
private final Object emptyArray;
|
||||||
|
|
||||||
|
Wrapper(Class<?> wtype,
|
||||||
|
String wtypeName,
|
||||||
|
Class<?> ptype,
|
||||||
|
String ptypeName,
|
||||||
|
char tchar,
|
||||||
|
Object emptyArray) {
|
||||||
|
this.wrapperType = wtype;
|
||||||
|
this.primitiveType = ptype;
|
||||||
|
this.basicTypeChar = tchar;
|
||||||
|
this.basicTypeString = String.valueOf(this.basicTypeChar);
|
||||||
|
this.emptyArray = emptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object zero() {
|
||||||
|
return switch (this) {
|
||||||
|
case BOOLEAN -> Boolean.FALSE;
|
||||||
|
case INT -> (Integer)0;
|
||||||
|
case DOUBLE -> DOUBLE_ZERO;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MyEnum {
|
||||||
|
DUMMY1,
|
||||||
|
DUMMY2;
|
||||||
|
|
||||||
|
static final boolean BOOLEAN = true;
|
||||||
|
static final byte BYTE = -128;
|
||||||
|
static final short SHORT = -12345;
|
||||||
|
static final char CHAR = 'c';
|
||||||
|
static final int INT = -123456;
|
||||||
|
static final long LONG = 0x1234567890L;
|
||||||
|
static final long LONG2 = -0x1234567890L;
|
||||||
|
static final float FLOAT = 567891.0f;
|
||||||
|
static final double DOUBLE = 12345678905678.890;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -94,7 +94,7 @@ public class CustomClassListDump {
|
|||||||
.shouldContain("unreg CustomLoadee")
|
.shouldContain("unreg CustomLoadee")
|
||||||
.shouldContain("unreg CustomLoadee2")
|
.shouldContain("unreg CustomLoadee2")
|
||||||
.shouldContain("unreg CustomLoadee3Child")
|
.shouldContain("unreg CustomLoadee3Child")
|
||||||
.shouldContain("unreg OldClass ** unlinked");
|
.shouldContain("unreg OldClass old unlinked");
|
||||||
|
|
||||||
// Use the dumped static archive
|
// Use the dumped static archive
|
||||||
opts = (new CDSOptions())
|
opts = (new CDSOptions())
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -96,5 +96,23 @@ public class ClassFileLoadHookTest {
|
|||||||
"ClassFileLoadHook",
|
"ClassFileLoadHook",
|
||||||
"" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON);
|
"" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON);
|
||||||
TestCommon.checkExec(out);
|
TestCommon.checkExec(out);
|
||||||
|
|
||||||
|
// JEP 483: if dumped with -XX:+AOTClassLinking, cannot use archive when CFLH is enabled
|
||||||
|
TestCommon.testDump(appJar, sharedClasses, useWb, "-XX:+AOTClassLinking");
|
||||||
|
out = TestCommon.exec(appJar,
|
||||||
|
"-XX:+UnlockDiagnosticVMOptions",
|
||||||
|
"-XX:+WhiteBoxAPI", useWb,
|
||||||
|
"-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
|
||||||
|
"-Xlog:cds",
|
||||||
|
"ClassFileLoadHook",
|
||||||
|
"" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON);
|
||||||
|
if (out.contains("Using AOT-linked classes: false (static archive: no aot-linked classes")) {
|
||||||
|
// JTREG is executed with VM options that do not support -XX:+AOTClassLinking, so
|
||||||
|
// the static archive was not created with aot-linked classes.
|
||||||
|
out.shouldHaveExitValue(0);
|
||||||
|
} else {
|
||||||
|
out.shouldContain("CDS archive has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.");
|
||||||
|
out.shouldNotHaveExitValue(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@ public class OldClassAndRedefineClass {
|
|||||||
String agentCmdArg = "-javaagent:redefineagent.jar";
|
String agentCmdArg = "-javaagent:redefineagent.jar";
|
||||||
|
|
||||||
OutputAnalyzer out = TestCommon.testDump(appJar, sharedClasses, "-Xlog:cds,cds+class=debug");
|
OutputAnalyzer out = TestCommon.testDump(appJar, sharedClasses, "-Xlog:cds,cds+class=debug");
|
||||||
out.shouldMatch("klasses.*OldSuper.[*][*].unlinked")
|
out.shouldMatch("klasses.*OldSuper.* unlinked")
|
||||||
.shouldMatch("klasses.*ChildOldSuper.[*][*].unlinked");
|
.shouldMatch("klasses.*ChildOldSuper.* unlinked");
|
||||||
|
|
||||||
out = TestCommon.exec(
|
out = TestCommon.exec(
|
||||||
appJar,
|
appJar,
|
||||||
|
@ -0,0 +1,388 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary AOT resolution of lambda expressions
|
||||||
|
* @bug 8340836
|
||||||
|
* @requires vm.cds
|
||||||
|
* @requires vm.cds.supports.aot.class.linking
|
||||||
|
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/
|
||||||
|
* @build AOTLinkedLambdas
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
|
||||||
|
* AOTLinkedLambdasApp InitTracker
|
||||||
|
* IntfWithNoClinit IntfWithNoClinit2
|
||||||
|
* IA IB IC ID1 ID2 IE1 IE2 IF1 IF2 IG1 IG2 IH1 IH2 IH3
|
||||||
|
* FooA FooB
|
||||||
|
* BarA BarB BarC
|
||||||
|
* @run driver AOTLinkedLambdas
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import static java.util.stream.Collectors.*;
|
||||||
|
import jdk.test.lib.cds.CDSOptions;
|
||||||
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
|
public class AOTLinkedLambdas {
|
||||||
|
static final String classList = "AOTLinkedLambdas.classlist";
|
||||||
|
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||||
|
static final String mainClass = AOTLinkedLambdasApp.class.getName();
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldContain("Hello AOTLinkedLambdasApp");
|
||||||
|
});
|
||||||
|
|
||||||
|
CDSOptions opts = (new CDSOptions())
|
||||||
|
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
|
||||||
|
"-XX:+AOTClassLinking",
|
||||||
|
"-Xlog:cds+resolve=trace",
|
||||||
|
"-Xlog:cds+class=debug",
|
||||||
|
"-cp", appJar);
|
||||||
|
|
||||||
|
OutputAnalyzer dumpOut = CDSTestUtils.createArchiveAndCheck(opts);
|
||||||
|
dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type IA");
|
||||||
|
dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type IB");
|
||||||
|
dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IC");
|
||||||
|
dumpOut.shouldContain("Can aot-resolve Lambda proxy of interface type ID2");
|
||||||
|
dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IE2"); // unsupported = IE1
|
||||||
|
dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IF2");
|
||||||
|
dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IG2");
|
||||||
|
dumpOut.shouldContain("Cannot aot-resolve Lambda proxy of interface type IH3"); // unsupported = IH1
|
||||||
|
|
||||||
|
CDSOptions runOpts = (new CDSOptions())
|
||||||
|
.setUseVersion(false)
|
||||||
|
.addPrefix("-Xlog:cds",
|
||||||
|
"-esa", // see JDK-8340836
|
||||||
|
"-cp", appJar)
|
||||||
|
.addSuffix(mainClass);
|
||||||
|
|
||||||
|
CDSTestUtils.run(runOpts)
|
||||||
|
.assertNormalExit("Hello AOTLinkedLambdasApp",
|
||||||
|
"hello, world");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AOTLinkedLambdasApp {
|
||||||
|
static {
|
||||||
|
System.out.println("AOTLinkedLambdasApp.<clinit>");
|
||||||
|
}
|
||||||
|
public static void main(String args[]) {
|
||||||
|
System.out.println("Hello AOTLinkedLambdasApp");
|
||||||
|
|
||||||
|
// (1) Simple tests
|
||||||
|
var words = java.util.List.of("hello", "fuzzy", "world");
|
||||||
|
System.out.println(words.stream().filter(w->!w.contains("u")).collect(joining(", ")));
|
||||||
|
// => hello, world
|
||||||
|
|
||||||
|
// (2) Test for <clinit> order.
|
||||||
|
testClinitOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check that aot-linking of lambdas does not cause <clinit> to be skipped or
|
||||||
|
// otherwise executed in the wrong order.
|
||||||
|
//
|
||||||
|
// A lambda is declared to implement an interface X, but it also implicitly
|
||||||
|
// implements all super interfaces of X.
|
||||||
|
//
|
||||||
|
// For any interface IN that's implemented by a lambda, if IN has declared
|
||||||
|
// a non-abstract, non-static method (JVMS 5.5. Initialization), IN must be
|
||||||
|
// initialized before the lambda can be linked. If IN::<clinit> exists, the
|
||||||
|
// initialization of IN can have side effects.
|
||||||
|
//
|
||||||
|
// AOTConstantPoolResolver::is_indy_resolution_deterministic() excludes
|
||||||
|
// any lambda if initializing its interfaces can cause side effects. This test
|
||||||
|
// checks that such exclusions are working as expected.
|
||||||
|
//
|
||||||
|
// This test also proves that is_indy_resolution_deterministic() doen't need to check
|
||||||
|
// for all other types that are mentioned by the lambda call site, as those classes
|
||||||
|
// will not be initialized as part of linking the lambda.
|
||||||
|
static void testClinitOrder() {
|
||||||
|
/*
|
||||||
|
* An indy callsite is associated with the following MethodType and MethodHandles:
|
||||||
|
*
|
||||||
|
* https://github.com/openjdk/jdk/blob/580eb62dc097efeb51c76b095c1404106859b673/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java#L293-L309
|
||||||
|
*
|
||||||
|
* MethodType factoryType The expected signature of the {@code CallSite}. The
|
||||||
|
* parameter types represent the types of capture variables;
|
||||||
|
* the return type is the interface to implement. When
|
||||||
|
* used with {@code invokedynamic}, this is provided by
|
||||||
|
* the {@code NameAndType} of the {@code InvokeDynamic}
|
||||||
|
*
|
||||||
|
* MethodType interfaceMethodType Signature and return type of method to be
|
||||||
|
* implemented by the function object.
|
||||||
|
*
|
||||||
|
* MethodHandle implementation A direct method handle describing the implementation
|
||||||
|
* method which should be called (with suitable adaptation
|
||||||
|
* of argument types and return types, and with captured
|
||||||
|
* arguments prepended to the invocation arguments) at
|
||||||
|
* invocation time.
|
||||||
|
*
|
||||||
|
* MethodType dynamicMethodType The signature and return type that should
|
||||||
|
* be enforced dynamically at invocation time.
|
||||||
|
* In simple use cases this is the same as
|
||||||
|
* {@code interfaceMethodType}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Initial condition: no <clinit> used by our Foo* and Bar* types have been called.
|
||||||
|
InitTracker.assertOrder("InitTracker");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// Case (i) -- Check for types used by factoryType, interfaceMethodType and dynamicMethodType
|
||||||
|
// (Note: no tests for captured variables in factoryType yet; will be tested in case (ii))
|
||||||
|
// factoryType = "()LIntfWithNoClinit;
|
||||||
|
// interfaceMethodType = "(LFooB;)LFooA;"
|
||||||
|
// implementation = "REF_invokeStatic AOTLinkedLambdasApp.implAB:(LBarB;)LBarA;"
|
||||||
|
// dynamicMethodType = "(LBarB;)LBarA;"
|
||||||
|
IntfWithNoClinit<BarA, BarB> noclinit = AOTLinkedLambdasApp::implAB;
|
||||||
|
|
||||||
|
// None of the Foo? and Bar? types used by the lambda should have been initialized yet, even though
|
||||||
|
// the indy callsite has been resolved now.
|
||||||
|
InitTracker.assertOrder("InitTracker");
|
||||||
|
|
||||||
|
BarB barB = new BarB();
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB");
|
||||||
|
BarA barA = noclinit.doit(barB);
|
||||||
|
System.out.println(barB);
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// Case (ii) -- Check for types used by captured variables in factoryType
|
||||||
|
BarC barC = null;
|
||||||
|
IntfWithNoClinit2 noclinit2 = () -> { return barC.hashCode(); };
|
||||||
|
try {
|
||||||
|
noclinit2.doit();
|
||||||
|
throw new RuntimeException("NullPointerException should have been thrown");
|
||||||
|
} catch (NullPointerException npe) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
// BarC shouldn't be initialized as no instances of it has been created.
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA");
|
||||||
|
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// (IA) No default methods -- is not initialized during lambda linking. Lambda can be archived.
|
||||||
|
IA ia = () -> {};
|
||||||
|
ia.doit();
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA");
|
||||||
|
System.out.println(IA._dummy);
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// (IB) Has default method but has not <clinit> -- OK to initialize IB during lambda linking. Lambda can be archived.
|
||||||
|
IB ib = () -> {};
|
||||||
|
ib.doit();
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// (IC) Has both default method and <clinit> -- cannot AOT link the lambda
|
||||||
|
IC ic = () -> {};
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC");
|
||||||
|
ic.doit();
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// ID1 - has default method, but no <clinit>
|
||||||
|
// ID2 - has <clinit>, but no default method
|
||||||
|
ID2 id2 = () -> {};
|
||||||
|
id2.doit();
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC");
|
||||||
|
System.out.println(ID2._dummy);
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// IE1 - has both default method and <clinit>
|
||||||
|
// IE2 - has <clinit>, but no default method
|
||||||
|
IE2 ie2 = () -> {};
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1");
|
||||||
|
System.out.println(IE2._dummy);
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1, IE2");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// IF1 - has <clinit>, but no default method
|
||||||
|
// IF2 - has both default method and <clinit>
|
||||||
|
IF2 if2 = () -> {};
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1, IE2, IF2");
|
||||||
|
System.out.println(IF1._dummy);
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1, IE2, IF2, IF1");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// IG1 - has both default method and <clinit>
|
||||||
|
// IG2 - has both default method and <clinit>
|
||||||
|
IG2 ig2 = () -> {};
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1, IE2, IF2, IF1, IG1, IG2");
|
||||||
|
|
||||||
|
//==============================
|
||||||
|
// Similar to IE1/IE2, but IH3 is one more level away from IH1
|
||||||
|
// IH1 - has both default method and <clinit>
|
||||||
|
// IH2 - has <clinit>, but no default method
|
||||||
|
// IH3 - has <clinit>, but no default method
|
||||||
|
IH3 ih3 = () -> {};
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1, IE2, IF2, IF1, IG1, IG2, IH1");
|
||||||
|
System.out.println(IH3._dummy);
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1, IE2, IF2, IF1, IG1, IG2, IH1, IH3");
|
||||||
|
System.out.println(IH2._dummy);
|
||||||
|
InitTracker.assertOrder("InitTracker, FooB, BarB, FooA, BarA, IA, IC, ID2, IE1, IE2, IF2, IF1, IG1, IG2, IH1, IH3, IH2");
|
||||||
|
}
|
||||||
|
|
||||||
|
static BarA implAB(BarB param) {
|
||||||
|
return new BarA(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// An interface with no <clinit> method. A lambda implementing this
|
||||||
|
// interface can be AOT-linked.
|
||||||
|
@FunctionalInterface
|
||||||
|
interface IntfWithNoClinit<X extends FooA, Y extends FooB> {
|
||||||
|
X doit(Y param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another interface with no <clinit> method. A lambda implementing this
|
||||||
|
// interface can be AOT-linked.
|
||||||
|
@FunctionalInterface
|
||||||
|
interface IntfWithNoClinit2 {
|
||||||
|
int doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// (IA) No default methods -- is not initialized during lambda linking. Lambda can be archived.
|
||||||
|
@FunctionalInterface interface IA {
|
||||||
|
static int _dummy = InitTracker.trackEvent("IA");
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// (IB) Has default method but has not <clinit> -- OK to initialize IB during lambda linking. Lambda can be archived.
|
||||||
|
@FunctionalInterface interface IB {
|
||||||
|
default int dummy() { return 0; }
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// (IC) Has both default method and <clinit> -- cannot AOT link the lambda
|
||||||
|
@FunctionalInterface interface IC {
|
||||||
|
static int _dummy = InitTracker.trackEvent("IC");
|
||||||
|
default int dummy() { return _dummy; }
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// (ID1/ID2)
|
||||||
|
@FunctionalInterface interface ID1 { // has default method, but no <clinit>
|
||||||
|
default int dummy() { return 0; }
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface interface ID2 extends ID1 { // has <clinit>, but no default method
|
||||||
|
static int _dummy = InitTracker.trackEvent("ID2");
|
||||||
|
}
|
||||||
|
|
||||||
|
// (IE1/IE2)
|
||||||
|
@FunctionalInterface interface IE1 { // has default method and <clinit>
|
||||||
|
static int _dummy = InitTracker.trackEvent("IE1");
|
||||||
|
default int dummy() { return _dummy; }
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface interface IE2 extends IE1 { // has <clinit>, but no default method
|
||||||
|
static int _dummy = InitTracker.trackEvent("IE2");
|
||||||
|
}
|
||||||
|
|
||||||
|
// (IF1/IF2)
|
||||||
|
@FunctionalInterface interface IF1 { // has <clinit>, but no default method
|
||||||
|
static int _dummy = InitTracker.trackEvent("IF1");
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface interface IF2 extends IF1 { // has default method and <clinit>
|
||||||
|
static int _dummy = InitTracker.trackEvent("IF2");
|
||||||
|
default int dummy() { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// (IG1/IG2)
|
||||||
|
@FunctionalInterface interface IG1 { // has default method and <clinit>
|
||||||
|
static int _dummy = InitTracker.trackEvent("IG1");
|
||||||
|
default int dummy() { return _dummy; }
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface interface IG2 extends IG1 { // has default method and <clinit>
|
||||||
|
static int _dummy = InitTracker.trackEvent("IG2");
|
||||||
|
default int dummy() { return _dummy; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// (IH1/IH2/IH3)
|
||||||
|
@FunctionalInterface interface IH1 { // has default method and <clinit>
|
||||||
|
static int _dummy = InitTracker.trackEvent("IH1");
|
||||||
|
default int dummy() { return _dummy; }
|
||||||
|
void doit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface interface IH2 extends IH1 { // has <clinit> but no default method
|
||||||
|
static int _dummy = InitTracker.trackEvent("IH2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface interface IH3 extends IH2 { // has <clinit> but no default method
|
||||||
|
static int _dummy = InitTracker.trackEvent("IH3");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class InitTracker {
|
||||||
|
static String actualOrder = "InitTracker";
|
||||||
|
static int trackEvent(String event) {
|
||||||
|
actualOrder += ", " + event;
|
||||||
|
return actualOrder.lastIndexOf(',');
|
||||||
|
}
|
||||||
|
static void assertOrder(String wantOrder) {
|
||||||
|
System.out.println("wantOrder = " + wantOrder);
|
||||||
|
System.out.println("actualOrder = " + actualOrder);
|
||||||
|
if (!actualOrder.equals(wantOrder)) {
|
||||||
|
throw new RuntimeException("Want <clinit> order: {" + wantOrder + "}, but got {" + actualOrder + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FooA {
|
||||||
|
static final int _dummy = InitTracker.trackEvent("FooA");
|
||||||
|
default int dummy() { return _dummy; }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FooB {
|
||||||
|
static final int _dummy = InitTracker.trackEvent("FooB");
|
||||||
|
default int dummy() { return _dummy; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class BarA implements FooA {
|
||||||
|
static {InitTracker.trackEvent("BarA");}
|
||||||
|
BarA(BarB dummy) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BarB implements FooB {
|
||||||
|
static {InitTracker.trackEvent("BarB");}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BarC {
|
||||||
|
static {InitTracker.trackEvent("BarC");}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary AOT resolution of VarHandle invocation
|
||||||
|
* @bug 8343245
|
||||||
|
* @requires vm.cds
|
||||||
|
* @requires vm.cds.supports.aot.class.linking
|
||||||
|
* @library /test/lib
|
||||||
|
* @build AOTLinkedVarHandles
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
|
||||||
|
* AOTLinkedVarHandlesApp AOTLinkedVarHandlesApp$Data
|
||||||
|
* @run driver AOTLinkedVarHandles
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.VarHandle;
|
||||||
|
import jdk.test.lib.cds.CDSOptions;
|
||||||
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
|
public class AOTLinkedVarHandles {
|
||||||
|
static final String classList = "AOTLinkedVarHandles.classlist";
|
||||||
|
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||||
|
static final String mainClass = AOTLinkedVarHandlesApp.class.getName();
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass)
|
||||||
|
.assertNormalExit(output -> {
|
||||||
|
output.shouldContain("Hello AOTLinkedVarHandlesApp");
|
||||||
|
});
|
||||||
|
|
||||||
|
CDSOptions opts = (new CDSOptions())
|
||||||
|
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
|
||||||
|
"-XX:+AOTClassLinking",
|
||||||
|
"-Xlog:cds+resolve=trace",
|
||||||
|
"-Xlog:cds+class=debug",
|
||||||
|
"-cp", appJar);
|
||||||
|
|
||||||
|
String s = "archived method CP entry.* AOTLinkedVarHandlesApp ";
|
||||||
|
OutputAnalyzer dumpOut = CDSTestUtils.createArchiveAndCheck(opts);
|
||||||
|
dumpOut.shouldMatch(s + "java/lang/invoke/VarHandle.compareAndExchangeAcquire:\\(\\[DIDI\\)D =>");
|
||||||
|
dumpOut.shouldMatch(s + "java/lang/invoke/VarHandle.get:\\(\\[DI\\)D => ");
|
||||||
|
|
||||||
|
CDSOptions runOpts = (new CDSOptions())
|
||||||
|
.setUseVersion(false)
|
||||||
|
.addPrefix("-Xlog:cds",
|
||||||
|
"-esa",
|
||||||
|
"-cp", appJar)
|
||||||
|
.addSuffix(mainClass);
|
||||||
|
|
||||||
|
CDSTestUtils.run(runOpts)
|
||||||
|
.assertNormalExit("Hello AOTLinkedVarHandlesApp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AOTLinkedVarHandlesApp {
|
||||||
|
static final VarHandle initialized;
|
||||||
|
static final VarHandle lazy;
|
||||||
|
static long longField = 5678;
|
||||||
|
static long seed;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
lazy = MethodHandles.lookup().findStaticVarHandle(Data.class, "longField", long.class);
|
||||||
|
initialized = MethodHandles.lookup().findStaticVarHandle(AOTLinkedVarHandlesApp.class, "longField", long.class);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Data {
|
||||||
|
static long longField = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
seed = 1234;
|
||||||
|
System.out.println("Hello AOTLinkedVarHandlesApp");
|
||||||
|
long a = (long) lazy.get();
|
||||||
|
long b = (long) initialized.get();
|
||||||
|
System.out.println(a);
|
||||||
|
System.out.println(b);
|
||||||
|
if (a != 1234) {
|
||||||
|
throw new RuntimeException("Data class should not be initialized: " + a);
|
||||||
|
}
|
||||||
|
if (b != 5678) {
|
||||||
|
throw new RuntimeException("VarHandle.get() failed: " + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
VarHandle vh = MethodHandles.arrayElementVarHandle(double[].class);
|
||||||
|
double[] array = new double[] {1.0};
|
||||||
|
int index = 0;
|
||||||
|
int v = 4;
|
||||||
|
|
||||||
|
// JDK-8343245 -- this generates "java.lang.invoke.LambdaForm$VH/0x80????" hidden class
|
||||||
|
double r = (double) vh.compareAndExchangeAcquire(array, index, 1.0, v);
|
||||||
|
if (r != 1.0) {
|
||||||
|
throw new RuntimeException("Unexpected result: " + r);
|
||||||
|
}
|
||||||
|
r = (double) vh.get(array, index);
|
||||||
|
if (r != 4.0) {
|
||||||
|
throw new RuntimeException("Unexpected result: " + r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,26 +24,40 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @summary Dump time resolutiom of constant pool entries.
|
* @summary Dump time resolution of constant pool entries.
|
||||||
* @requires vm.cds
|
* @requires vm.cds
|
||||||
|
* @requires vm.cds.supports.aot.class.linking
|
||||||
* @requires vm.compMode != "Xcomp"
|
* @requires vm.compMode != "Xcomp"
|
||||||
* @library /test/lib
|
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/
|
||||||
|
* @build OldProvider OldClass OldConsumer StringConcatTestOld
|
||||||
* @build ResolvedConstants
|
* @build ResolvedConstants
|
||||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar ResolvedConstantsApp ResolvedConstantsFoo ResolvedConstantsBar
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
|
||||||
|
* ResolvedConstantsApp ResolvedConstantsFoo ResolvedConstantsBar
|
||||||
|
* MyInterface InterfaceWithClinit NormalClass
|
||||||
|
* OldProvider OldClass OldConsumer SubOfOldClass
|
||||||
|
* StringConcatTest StringConcatTestOld
|
||||||
* @run driver ResolvedConstants
|
* @run driver ResolvedConstants
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
import jdk.test.lib.cds.CDSOptions;
|
import jdk.test.lib.cds.CDSOptions;
|
||||||
import jdk.test.lib.cds.CDSTestUtils;
|
import jdk.test.lib.cds.CDSTestUtils;
|
||||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
public class ResolvedConstants {
|
public class ResolvedConstants {
|
||||||
static final String classList = "ResolvedConstants.classlist";
|
static final String classList = "ResolvedConstants.classlist";
|
||||||
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||||
static final String mainClass = ResolvedConstantsApp.class.getName();
|
static final String mainClass = ResolvedConstantsApp.class.getName();
|
||||||
|
|
||||||
|
static boolean aotClassLinking;
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
// dump class list
|
test(false);
|
||||||
|
test(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test(boolean testMode) throws Exception {
|
||||||
|
aotClassLinking = testMode;
|
||||||
CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass)
|
CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass)
|
||||||
.assertNormalExit(output -> {
|
.assertNormalExit(output -> {
|
||||||
output.shouldContain("Hello ResolvedConstantsApp");
|
output.shouldContain("Hello ResolvedConstantsApp");
|
||||||
@ -52,78 +66,109 @@ public class ResolvedConstants {
|
|||||||
CDSOptions opts = (new CDSOptions())
|
CDSOptions opts = (new CDSOptions())
|
||||||
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
|
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
|
||||||
"-cp", appJar,
|
"-cp", appJar,
|
||||||
"-Xlog:cds+resolve=trace");
|
"-Xlog:cds+resolve=trace",
|
||||||
CDSTestUtils.createArchiveAndCheck(opts)
|
"-Xlog:cds+class=debug");
|
||||||
|
if (aotClassLinking) {
|
||||||
|
opts.addPrefix("-XX:+AOTClassLinking");
|
||||||
|
} else {
|
||||||
|
opts.addPrefix("-XX:-AOTClassLinking");
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputAnalyzer out = CDSTestUtils.createArchiveAndCheck(opts);
|
||||||
// Class References ---
|
// Class References ---
|
||||||
|
|
||||||
// Always resolve reference when a class references itself
|
// Always resolve reference when a class references itself
|
||||||
.shouldMatch("cds,resolve.*archived klass.* ResolvedConstantsApp app => ResolvedConstantsApp app")
|
out.shouldMatch(ALWAYS("klass.* ResolvedConstantsApp app => ResolvedConstantsApp app"))
|
||||||
|
|
||||||
// Always resolve reference when a class references a super class
|
// Always resolve reference when a class references a super class
|
||||||
.shouldMatch("cds,resolve.*archived klass.* ResolvedConstantsApp app => java/lang/Object boot")
|
.shouldMatch(ALWAYS("klass.* ResolvedConstantsApp app => java/lang/Object boot"))
|
||||||
.shouldMatch("cds,resolve.*archived klass.* ResolvedConstantsBar app => ResolvedConstantsFoo app")
|
.shouldMatch(ALWAYS("klass.* ResolvedConstantsBar app => ResolvedConstantsFoo app"))
|
||||||
|
|
||||||
// Always resolve reference when a class references a super interface
|
// Always resolve reference when a class references a super interface
|
||||||
.shouldMatch("cds,resolve.*archived klass.* ResolvedConstantsApp app => java/lang/Runnable boot")
|
.shouldMatch(ALWAYS("klass.* ResolvedConstantsApp app => java/lang/Runnable boot"))
|
||||||
|
|
||||||
// java/lang/System is in the root loader but ResolvedConstantsApp is loaded by the app loader.
|
// Without -XX:+AOTClassLinking:
|
||||||
// Even though System is in the vmClasses list, when ResolvedConstantsApp looks up
|
// java/lang/System is in the boot loader but ResolvedConstantsApp is loaded by the app loader.
|
||||||
// "java/lang/System" in its ConstantPool, the app loader may not have resolved the System
|
// Even though System is in the vmClasses list, when ResolvedConstantsApp looks up
|
||||||
// class yet (i.e., there's no initiaited class entry for System in the app loader's dictionary)
|
// "java/lang/System" in its ConstantPool, the app loader may not have resolved the System
|
||||||
.shouldMatch("cds,resolve.*reverted klass.* ResolvedConstantsApp .*java/lang/System")
|
// class yet (i.e., there's no initiaited class entry for System in the app loader's dictionary)
|
||||||
|
.shouldMatch(AOTLINK_ONLY("klass.* ResolvedConstantsApp .*java/lang/System"))
|
||||||
|
|
||||||
// Field References ---
|
// Field References ---
|
||||||
|
|
||||||
// Always resolve references to fields in the current class or super class(es)
|
// Always resolve references to fields in the current class or super class(es)
|
||||||
.shouldMatch("cds,resolve.*archived field.* ResolvedConstantsBar => ResolvedConstantsBar.b:I")
|
.shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.b:I"))
|
||||||
.shouldMatch("cds,resolve.*archived field.* ResolvedConstantsBar => ResolvedConstantsBar.a:I")
|
.shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.a:I"))
|
||||||
.shouldMatch("cds,resolve.*archived field.* ResolvedConstantsBar => ResolvedConstantsFoo.a:I")
|
.shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsFoo.a:I"))
|
||||||
|
.shouldMatch(ALWAYS("field.* ResolvedConstantsFoo => ResolvedConstantsFoo.a:I"))
|
||||||
|
|
||||||
// Do not resolve field references to child classes
|
// Resolve field references to child classes ONLY when using -XX:+AOTClassLinking
|
||||||
.shouldMatch("cds,resolve.*archived field.* ResolvedConstantsFoo => ResolvedConstantsFoo.a:I")
|
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.a:I"))
|
||||||
.shouldMatch("cds,resolve.*reverted field.* ResolvedConstantsFoo ResolvedConstantsBar.a:I")
|
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.b:I"))
|
||||||
.shouldMatch("cds,resolve.*reverted field.* ResolvedConstantsFoo ResolvedConstantsBar.b:I")
|
|
||||||
|
|
||||||
// Do not resolve field references to unrelated classes
|
// Resolve field references to unrelated classes ONLY when using -XX:+AOTClassLinking
|
||||||
.shouldMatch("cds,resolve.*reverted field.* ResolvedConstantsApp ResolvedConstantsBar.a:I")
|
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.a:I"))
|
||||||
.shouldMatch("cds,resolve.*reverted field.* ResolvedConstantsApp ResolvedConstantsBar.b:I")
|
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.b:I"))
|
||||||
|
|
||||||
// Method References ---
|
// Method References ---
|
||||||
|
|
||||||
// Should resolve references to own constructor
|
// Should resolve references to own constructor
|
||||||
.shouldMatch("cds,resolve.*archived method .* ResolvedConstantsApp ResolvedConstantsApp.<init>:")
|
.shouldMatch(ALWAYS("method.* ResolvedConstantsApp ResolvedConstantsApp.<init>:"))
|
||||||
// Should resolve references to super constructor
|
// Should resolve references to super constructor
|
||||||
.shouldMatch("cds,resolve.*archived method .* ResolvedConstantsApp java/lang/Object.<init>:")
|
.shouldMatch(ALWAYS("method.* ResolvedConstantsApp java/lang/Object.<init>:"))
|
||||||
|
|
||||||
// Should resolve interface methods in VM classes
|
// Should resolve interface methods in VM classes
|
||||||
.shouldMatch("cds,resolve.*archived interface method .* ResolvedConstantsApp java/lang/Runnable.run:")
|
.shouldMatch(ALWAYS("interface method .* ResolvedConstantsApp java/lang/Runnable.run:"))
|
||||||
|
|
||||||
// Should resolve references to own non-static method (private or public)
|
// Should resolve references to own non-static method (private or public)
|
||||||
.shouldMatch("archived method.*: ResolvedConstantsBar ResolvedConstantsBar.doBar:")
|
.shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsBar.doBar:"))
|
||||||
.shouldMatch("archived method.*: ResolvedConstantsApp ResolvedConstantsApp.privateInstanceCall:")
|
.shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.privateInstanceCall:"))
|
||||||
.shouldMatch("archived method.*: ResolvedConstantsApp ResolvedConstantsApp.publicInstanceCall:")
|
.shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.publicInstanceCall:"))
|
||||||
|
|
||||||
// Should not resolve references to static method
|
// Should not resolve references to static method
|
||||||
.shouldNotMatch(" archived method CP entry.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:")
|
.shouldNotMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:"))
|
||||||
|
|
||||||
// Should resolve references to method in super type
|
// Should resolve references to method in super type
|
||||||
.shouldMatch(" archived method CP entry.*: ResolvedConstantsBar ResolvedConstantsFoo.doBar:")
|
.shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsFoo.doBar:"))
|
||||||
|
|
||||||
// App class cannot resolve references to methods in boot classes:
|
// Without -XX:+AOTClassLinking App class cannot resolve references to methods in boot classes:
|
||||||
// When the app class loader tries to resolve a class X that's normally loaded by
|
// When the app class loader tries to resolve a class X that's normally loaded by
|
||||||
// the boot loader, it's possible for the app class loader to get a different copy of
|
// the boot loader, it's possible for the app class loader to get a different copy of
|
||||||
// X (by using MethodHandles.Lookup.defineClass(), etc). Therefore, let's be on
|
// X (by using MethodHandles.Lookup.defineClass(), etc). Therefore, let's be on
|
||||||
// the side of safety and revert all such references.
|
// the side of safety and revert all such references.
|
||||||
//
|
.shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp java/io/PrintStream.println:"))
|
||||||
// This will be addressed in JDK-8315737.
|
.shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsBar java/lang/Class.getName:"))
|
||||||
.shouldMatch("reverted method.*: ResolvedConstantsApp java/io/PrintStream.println:")
|
|
||||||
.shouldMatch("reverted method.*: ResolvedConstantsBar java/lang/Class.getName:")
|
|
||||||
|
|
||||||
// Should not resolve methods in unrelated classes.
|
// Resole resolve methods in unrelated classes ONLY when using -XX:+AOTClassLinking
|
||||||
.shouldMatch("reverted method.*: ResolvedConstantsApp ResolvedConstantsBar.doit:")
|
.shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp ResolvedConstantsBar.doit:"))
|
||||||
|
|
||||||
// End ---
|
// End ---
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
// Indy References ---
|
||||||
|
if (aotClassLinking) {
|
||||||
|
out.shouldContain("Cannot aot-resolve Lambda proxy because OldConsumer is excluded")
|
||||||
|
.shouldContain("Cannot aot-resolve Lambda proxy because OldProvider is excluded")
|
||||||
|
.shouldContain("Cannot aot-resolve Lambda proxy because OldClass is excluded")
|
||||||
|
.shouldContain("Cannot aot-resolve Lambda proxy of interface type InterfaceWithClinit")
|
||||||
|
.shouldMatch("klasses.* app *NormalClass[$][$]Lambda/.* hidden aot-linked inited")
|
||||||
|
.shouldNotMatch("klasses.* app *SubOfOldClass[$][$]Lambda/")
|
||||||
|
.shouldMatch("archived indy *CP entry.*StringConcatTest .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants")
|
||||||
|
.shouldNotMatch("archived indy *CP entry.*StringConcatTestOld .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String ALWAYS(String s) {
|
||||||
|
return "cds,resolve.*archived " + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String AOTLINK_ONLY(String s) {
|
||||||
|
if (aotClassLinking) {
|
||||||
|
return ALWAYS(s);
|
||||||
|
} else {
|
||||||
|
return "cds,resolve.*reverted " + s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,12 +187,98 @@ class ResolvedConstantsApp implements Runnable {
|
|||||||
bar.a ++;
|
bar.a ++;
|
||||||
bar.b ++;
|
bar.b ++;
|
||||||
bar.doit();
|
bar.doit();
|
||||||
|
|
||||||
|
testLambda();
|
||||||
|
StringConcatTest.test();
|
||||||
|
StringConcatTestOld.main(null);
|
||||||
}
|
}
|
||||||
private static void staticCall() {}
|
private static void staticCall() {}
|
||||||
private void privateInstanceCall() {}
|
private void privateInstanceCall() {}
|
||||||
public void publicInstanceCall() {}
|
public void publicInstanceCall() {}
|
||||||
|
|
||||||
public void run() {}
|
public void run() {}
|
||||||
|
|
||||||
|
static void testLambda() {
|
||||||
|
// The functional type used in the Lambda is an excluded class
|
||||||
|
OldProvider op = () -> {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A captured value is an instance of an excluded Class
|
||||||
|
OldClass c = new OldClass();
|
||||||
|
Runnable r = () -> {
|
||||||
|
System.out.println("Test 1 " + c);
|
||||||
|
};
|
||||||
|
r.run();
|
||||||
|
|
||||||
|
// The functional interface accepts an argument that's an excluded class
|
||||||
|
MyInterface i = (o) -> {
|
||||||
|
System.out.println("Test 2 " + o);
|
||||||
|
};
|
||||||
|
i.dispatch(c);
|
||||||
|
|
||||||
|
// Method reference to old class
|
||||||
|
OldConsumer oldConsumer = new OldConsumer();
|
||||||
|
Consumer<String> wrapper = oldConsumer::consumeString;
|
||||||
|
wrapper.accept("Hello");
|
||||||
|
|
||||||
|
// Lambda of interfaces that have <clinit> are not archived.
|
||||||
|
InterfaceWithClinit i2 = () -> {
|
||||||
|
System.out.println("Test 3");
|
||||||
|
};
|
||||||
|
i2.dispatch();
|
||||||
|
|
||||||
|
// These two classes have almost identical source code, but
|
||||||
|
// only NormalClass should have its lambdas pre-resolved.
|
||||||
|
// SubOfOldClass is "old" -- it should be excluded from the AOT cache,
|
||||||
|
// so none of its lambda proxies should be cached
|
||||||
|
NormalClass.testLambda(); // Lambda proxy should be cached
|
||||||
|
SubOfOldClass.testLambda(); // Lambda proxy shouldn't be cached
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringConcatTest {
|
||||||
|
static void test() {
|
||||||
|
System.out.println("StringConcatTest <concat> " + new StringConcatTest()); // concat should be aot-resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see StringConcatTestOld.jasm
|
||||||
|
|
||||||
|
class StringConcatTestOld {
|
||||||
|
public static void main(String args[]) {
|
||||||
|
// concat should be aot-resolved => the MethodType refers to an old class
|
||||||
|
System.out.println("StringConcatTestOld <concat> " + new OldConsumer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NormalClass {
|
||||||
|
static void testLambda() {
|
||||||
|
Runnable r = () -> {
|
||||||
|
System.out.println("NormalClass testLambda");
|
||||||
|
};
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubOfOldClass extends OldClass {
|
||||||
|
static void testLambda() {
|
||||||
|
Runnable r = () -> {
|
||||||
|
System.out.println("SubOfOldClass testLambda");
|
||||||
|
};
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MyInterface {
|
||||||
|
void dispatch(OldClass c);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InterfaceWithClinit {
|
||||||
|
static final long X = System.currentTimeMillis();
|
||||||
|
void dispatch();
|
||||||
|
default long dummy() { return X; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResolvedConstantsFoo {
|
class ResolvedConstantsFoo {
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
decompiled from
|
||||||
|
|
||||||
|
class OldConsumer {}
|
||||||
|
|
||||||
|
class StringConcatTestOld {
|
||||||
|
public static void main(String args[]) {
|
||||||
|
System.out.println("StringConcatTestOld <concat> " + new OldConsumer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
(1) Comment out this line
|
||||||
|
|
||||||
|
invokestatic Method java/lang/String.valueOf:"(Ljava/lang/Object;)Ljava/lang/String;";
|
||||||
|
|
||||||
|
(2) Change the MethodType parameter of makeConcatWithConstants from
|
||||||
|
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;"
|
||||||
|
->
|
||||||
|
|
||||||
|
"(LOldConsumer;)Ljava/lang/String;"
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
super class StringConcatTestOld
|
||||||
|
version 67:0
|
||||||
|
{
|
||||||
|
Method "<init>":"()V"
|
||||||
|
stack 1 locals 1
|
||||||
|
{
|
||||||
|
aload_0;
|
||||||
|
invokespecial Method java/lang/Object."<init>":"()V";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
public static Method main:"([Ljava/lang/String;)V"
|
||||||
|
stack 3 locals 1
|
||||||
|
{
|
||||||
|
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
|
||||||
|
new class OldConsumer;
|
||||||
|
dup;
|
||||||
|
invokespecial Method OldConsumer."<init>":"()V";
|
||||||
|
//invokestatic Method java/lang/String.valueOf:"(Ljava/lang/Object;)Ljava/lang/String;";
|
||||||
|
invokedynamic InvokeDynamic REF_invokeStatic:Method java/lang/invoke/StringConcatFactory.makeConcatWithConstants:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;":makeConcatWithConstants:"(LOldConsumer;)Ljava/lang/String;" {
|
||||||
|
String "StringConcatTestOld <concat> "
|
||||||
|
};
|
||||||
|
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final InnerClass Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles;
|
||||||
|
|
||||||
|
} // end Class StringConcatTestOld
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user