Skip to content

Commit 9499175

Browse files
committed
8261090: Store old classfiles in static CDS archive
Reviewed-by: iklam, minqi
1 parent 159f5e1 commit 9499175

29 files changed

+895
-42
lines changed

src/hotspot/share/classfile/classFileParser.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5920,18 +5920,6 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
59205920
_minor_version = stream->get_u2_fast();
59215921
_major_version = stream->get_u2_fast();
59225922

5923-
if (DumpSharedSpaces && _major_version < JAVA_6_VERSION) {
5924-
ResourceMark rm;
5925-
warning("Pre JDK 6 class not supported by CDS: %u.%u %s",
5926-
_major_version, _minor_version, _class_name->as_C_string());
5927-
Exceptions::fthrow(
5928-
THREAD_AND_LOCATION,
5929-
vmSymbols::java_lang_UnsupportedClassVersionError(),
5930-
"Unsupported major.minor version for dump time %u.%u",
5931-
_major_version,
5932-
_minor_version);
5933-
}
5934-
59355923
// Check version numbers - we check this even with verifier off
59365924
verify_class_version(_major_version, _minor_version, _class_name, CHECK);
59375925

src/hotspot/share/classfile/systemDictionaryShared.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,18 +1380,28 @@ bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) {
13801380
// class loader doesn't expect it.
13811381
if (has_class_failed_verification(k)) {
13821382
warn_excluded(k, "Failed verification");
1383+
return true;
13831384
} else {
1384-
warn_excluded(k, "Not linked");
1385+
if (!MetaspaceShared::is_old_class(k)) {
1386+
warn_excluded(k, "Not linked");
1387+
return true;
1388+
}
13851389
}
1386-
return true;
13871390
}
1388-
if (k->major_version() < 50 /*JAVA_6_VERSION*/) {
1391+
if (DynamicDumpSharedSpaces && k->major_version() < 50 /*JAVA_6_VERSION*/) {
1392+
// In order to support old classes during dynamic dump, class rewriting needs to
1393+
// be reverted. This would result in more complex code and testing but not much gain.
13891394
ResourceMark rm;
13901395
log_warning(cds)("Pre JDK 6 class not supported by CDS: %u.%u %s",
13911396
k->major_version(), k->minor_version(), k->name()->as_C_string());
13921397
return true;
13931398
}
13941399

1400+
if (MetaspaceShared::is_old_class(k) && k->is_linked()) {
1401+
warn_excluded(k, "Old class has been linked");
1402+
return true;
1403+
}
1404+
13951405
InstanceKlass* super = k->java_super();
13961406
if (super != NULL && should_be_excluded(super)) {
13971407
ResourceMark rm;

src/hotspot/share/classfile/verifier.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,9 @@ bool Verifier::is_eligible_for_verification(InstanceKlass* klass, bool should_ve
285285
// already been rewritten to contain constant pool cache indices,
286286
// which the verifier can't understand.
287287
// Shared classes shouldn't have stackmaps either.
288-
!klass->is_shared() &&
288+
// However, bytecodes for shared old classes can be verified because
289+
// they have not been rewritten.
290+
!(klass->is_shared() && klass->is_rewritten()) &&
289291

290292
// As of the fix for 4486457 we disable verification for all of the
291293
// dynamically-generated bytecodes associated with the 1.4

src/hotspot/share/interpreter/rewriter.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "interpreter/interpreter.hpp"
2929
#include "interpreter/rewriter.hpp"
3030
#include "memory/metadataFactory.hpp"
31+
#include "memory/metaspaceShared.hpp"
3132
#include "memory/resourceArea.hpp"
3233
#include "oops/constantPool.hpp"
3334
#include "oops/generateOopMap.hpp"
@@ -567,8 +568,9 @@ void Rewriter::rewrite_bytecodes(TRAPS) {
567568
}
568569

569570
void Rewriter::rewrite(InstanceKlass* klass, TRAPS) {
570-
if (!DumpSharedSpaces) {
571-
assert(!klass->is_shared(), "archive methods must not be rewritten at run time");
571+
if (klass->is_shared()) {
572+
assert(!klass->is_rewritten(), "rewritten shared classes cannot be rewritten again");
573+
assert(MetaspaceShared::is_old_class(klass), "only shared old classes aren't rewritten");
572574
}
573575
ResourceMark rm(THREAD);
574576
constantPoolHandle cpool(THREAD, klass->constants());

src/hotspot/share/memory/metaspaceShared.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,9 @@ static void rewrite_nofast_bytecode(const methodHandle& method) {
389389
void MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread* thread, InstanceKlass* ik) {
390390
for (int i = 0; i < ik->methods()->length(); i++) {
391391
methodHandle m(thread, ik->methods()->at(i));
392-
rewrite_nofast_bytecode(m);
392+
if (!is_old_class(ik)) {
393+
rewrite_nofast_bytecode(m);
394+
}
393395
Fingerprinter fp(m);
394396
// The side effect of this call sets method's fingerprint field.
395397
fp.fingerprint();
@@ -578,9 +580,31 @@ class CollectCLDClosure : public CLDClosure {
578580
ClassLoaderData* cld_at(int index) { return _loaded_cld.at(index); }
579581
};
580582

583+
// Check if a class or its super class/interface is old.
584+
bool MetaspaceShared::is_old_class(InstanceKlass* ik) {
585+
if (ik == NULL) {
586+
return false;
587+
}
588+
if (ik->major_version() < 50 /*JAVA_6_VERSION*/) {
589+
return true;
590+
}
591+
if (is_old_class(ik->java_super())) {
592+
return true;
593+
}
594+
Array<InstanceKlass*>* interfaces = ik->local_interfaces();
595+
int len = interfaces->length();
596+
for (int i = 0; i < len; i++) {
597+
if (is_old_class(interfaces->at(i))) {
598+
return true;
599+
}
600+
}
601+
return false;
602+
}
603+
581604
bool MetaspaceShared::linking_required(InstanceKlass* ik) {
605+
// For static CDS dump, do not link old classes.
582606
// For dynamic CDS dump, only link classes loaded by the builtin class loaders.
583-
return DumpSharedSpaces ? true : !ik->is_shared_unregistered_class();
607+
return DumpSharedSpaces ? !MetaspaceShared::is_old_class(ik) : !ik->is_shared_unregistered_class();
584608
}
585609

586610
bool MetaspaceShared::link_class_for_cds(InstanceKlass* ik, TRAPS) {
@@ -760,7 +784,7 @@ bool MetaspaceShared::try_link_class(Thread* current, InstanceKlass* ik) {
760784
ExceptionMark em(current);
761785
Thread* THREAD = current; // For exception macros.
762786
Arguments::assert_is_dumping_archive();
763-
if (ik->is_loaded() && !ik->is_linked() &&
787+
if (ik->is_loaded() && !ik->is_linked() && !MetaspaceShared::is_old_class(ik) &&
764788
!SystemDictionaryShared::has_class_failed_verification(ik)) {
765789
bool saved = BytecodeVerificationLocal;
766790
if (ik->is_shared_unregistered_class() && ik->class_loader() == NULL) {
@@ -806,7 +830,9 @@ void VM_PopulateDumpSharedSpace::dump_java_heap_objects(GrowableArray<Klass*>* k
806830
Klass* k = klasses->at(i);
807831
if (k->is_instance_klass()) {
808832
InstanceKlass* ik = InstanceKlass::cast(k);
809-
ik->constants()->add_dumped_interned_strings();
833+
if (ik->is_linked()) {
834+
ik->constants()->add_dumped_interned_strings();
835+
}
810836
}
811837
}
812838
if (_extra_interned_strings != NULL) {

src/hotspot/share/memory/metaspaceShared.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class MetaspaceShared : AllStatic {
137137
static void link_and_cleanup_shared_classes(TRAPS) NOT_CDS_RETURN;
138138
static bool link_class_for_cds(InstanceKlass* ik, TRAPS) NOT_CDS_RETURN_(false);
139139
static bool linking_required(InstanceKlass* ik) NOT_CDS_RETURN_(false);
140+
static bool is_old_class(InstanceKlass* ik);
140141

141142
#if INCLUDE_CDS
142143
// Alignment for the 3 core CDS regions (MC/RW/RO) only.

src/hotspot/share/oops/constMethod.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,11 @@ void ConstMethod::copy_annotations_from(ClassLoaderData* loader_data, ConstMetho
405405
void ConstMethod::metaspace_pointers_do(MetaspaceClosure* it) {
406406
log_trace(cds)("Iter(ConstMethod): %p", this);
407407

408-
it->push(&_constants);
408+
if (!method()->method_holder()->is_rewritten()) {
409+
it->push(&_constants, MetaspaceClosure::_writable);
410+
} else {
411+
it->push(&_constants);
412+
}
409413
it->push(&_stackmap_data);
410414
if (has_method_annotations()) {
411415
it->push(method_annotations_addr());
@@ -419,7 +423,6 @@ void ConstMethod::metaspace_pointers_do(MetaspaceClosure* it) {
419423
if (has_default_annotations()) {
420424
it->push(default_annotations_addr());
421425
}
422-
ConstMethod* this_ptr = this;
423426
}
424427

425428
// Printing

src/hotspot/share/oops/constantPool.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ void ConstantPool::add_dumped_interned_strings() {
353353

354354
// CDS support. Create a new resolved_references array.
355355
void ConstantPool::restore_unshareable_info(TRAPS) {
356+
if (!_pool_holder->is_linked() && !_pool_holder->is_rewritten()) {
357+
return;
358+
}
356359
assert(is_constantPool(), "ensure C++ vtable is restored");
357360
assert(on_stack(), "should always be set for shared constant pools");
358361
assert(is_shared(), "should always be set for shared constant pools");
@@ -390,6 +393,9 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
390393
}
391394

392395
void ConstantPool::remove_unshareable_info() {
396+
if (!_pool_holder->is_linked() && _pool_holder->is_shared_old_klass()) {
397+
return;
398+
}
393399
// Resolved references are not in the shared archive.
394400
// Save the length for restoration. It is not necessarily the same length
395401
// as reference_map.length() if invokedynamic is saved. It is needed when

src/hotspot/share/oops/instanceKlass.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2454,7 +2454,11 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
24542454

24552455
it->push(&_annotations);
24562456
it->push((Klass**)&_array_klasses);
2457-
it->push(&_constants);
2457+
if (!is_rewritten()) {
2458+
it->push(&_constants, MetaspaceClosure::_writable);
2459+
} else {
2460+
it->push(&_constants);
2461+
}
24582462
it->push(&_inner_classes);
24592463
#if INCLUDE_JVMTI
24602464
it->push(&_previous_versions);
@@ -2491,6 +2495,12 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
24912495
}
24922496

24932497
void InstanceKlass::remove_unshareable_info() {
2498+
2499+
if (MetaspaceShared::is_old_class(this)) {
2500+
// Set the old class bit.
2501+
set_is_shared_old_klass();
2502+
}
2503+
24942504
Klass::remove_unshareable_info();
24952505

24962506
if (SystemDictionaryShared::has_class_failed_verification(this)) {

src/hotspot/share/oops/klass.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ class Klass : public Metadata {
177177
u2 _shared_class_flags;
178178
enum {
179179
_archived_lambda_proxy_is_available = 2,
180-
_has_value_based_class_annotation = 4
180+
_has_value_based_class_annotation = 4,
181+
_is_shared_old_klass = 8
181182
};
182183
#endif
183184

@@ -333,6 +334,14 @@ class Klass : public Metadata {
333334
NOT_CDS(return false;)
334335
}
335336

337+
void set_is_shared_old_klass() {
338+
CDS_ONLY(_shared_class_flags |= _is_shared_old_klass;)
339+
}
340+
bool is_shared_old_klass() const {
341+
CDS_ONLY(return (_shared_class_flags & _is_shared_old_klass) != 0;)
342+
NOT_CDS(return false;)
343+
}
344+
336345

337346
// Obtain the module or package for this class
338347
virtual ModuleEntry* module() const = 0;

src/hotspot/share/oops/klassVtable.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ inline InstanceKlass* klassVtable::ik() const {
5151
}
5252

5353
bool klassVtable::is_preinitialized_vtable() {
54-
return _klass->is_shared() && !MetaspaceShared::remapped_readwrite();
54+
return _klass->is_shared() && !MetaspaceShared::remapped_readwrite() && !_klass->is_shared_old_klass();
5555
}
5656

5757

@@ -1093,7 +1093,8 @@ void itableMethodEntry::initialize(Method* m) {
10931093

10941094
#ifdef ASSERT
10951095
if (MetaspaceShared::is_in_shared_metaspace((void*)&_method) &&
1096-
!MetaspaceShared::remapped_readwrite()) {
1096+
!MetaspaceShared::remapped_readwrite() &&
1097+
!MetaspaceShared::is_old_class(m->method_holder())) {
10971098
// At runtime initialize_itable is rerun as part of link_class_impl()
10981099
// for a shared class loaded by the non-boot loader.
10991100
// The dumptime itable method entry should be the same as the runtime entry.

src/hotspot/share/oops/method.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,13 @@ Symbol* Method::klass_name() const {
344344
void Method::metaspace_pointers_do(MetaspaceClosure* it) {
345345
log_trace(cds)("Iter(Method): %p", this);
346346

347-
it->push(&_constMethod);
347+
if (!method_holder()->is_rewritten()) {
348+
it->push(&_constMethod, MetaspaceClosure::_writable);
349+
} else {
350+
it->push(&_constMethod);
351+
}
348352
it->push(&_method_data);
349353
it->push(&_method_counters);
350-
351-
Method* this_ptr = this;
352354
}
353355

354356
// Attempt to return method to original state. Clear any pointers
@@ -362,7 +364,7 @@ void Method::remove_unshareable_info() {
362364
}
363365

364366
void Method::set_vtable_index(int index) {
365-
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
367+
if (is_shared() && !MetaspaceShared::remapped_readwrite() && !method_holder()->is_shared_old_klass()) {
366368
// At runtime initialize_vtable is rerun as part of link_class_impl()
367369
// for a shared class loaded by the non-boot loader to obtain the loader
368370
// constraints based on the runtime classloaders' context.
@@ -373,7 +375,7 @@ void Method::set_vtable_index(int index) {
373375
}
374376

375377
void Method::set_itable_index(int index) {
376-
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
378+
if (is_shared() && !MetaspaceShared::remapped_readwrite() && !method_holder()->is_shared_old_klass()) {
377379
// At runtime initialize_itable is rerun as part of link_class_impl()
378380
// for a shared class loaded by the non-boot loader to obtain the loader
379381
// constraints based on the runtime classloaders' context. The dumptime

test/hotspot/jtreg/runtime/cds/appcds/LambdaWithOldClass.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static void main(String[] args) throws Exception {
6868
.addSuffix(mainClass);
6969
OutputAnalyzer output = CDSTestUtils.runWithArchive(runOpts);
7070
output.shouldContain("[class,load] LambdaWithOldClassApp source: shared objects file")
71-
.shouldMatch(".class.load. LambdaWithOldClassApp[$][$]Lambda[$].*/0x.*source:.*LambdaWithOldClassApp")
71+
.shouldMatch(".class.load. LambdaWithOldClassApp[$][$]Lambda[$].*/0x.*source:.*shared objects file")
7272
.shouldHaveExitValue(0);
7373
}
7474
}

test/hotspot/jtreg/runtime/cds/appcds/OldClassTest.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
/*
2626
* @test
27-
* @summary classes with major version < JDK_6 (50) should not be included in CDS
27+
* @summary CDS support of old classes with major version < JDK_6 (50) for static archive.
2828
* @requires vm.cds
2929
* @library /test/lib
3030
* @modules java.base/jdk.internal.org.objectweb.asm
@@ -35,6 +35,7 @@
3535

3636
import java.io.File;
3737
import java.io.FileOutputStream;
38+
import jdk.test.lib.cds.CDSTestUtils;
3839
import jdk.test.lib.process.OutputAnalyzer;
3940
import java.nio.file.Files;
4041

@@ -55,26 +56,47 @@ public static void main(String[] args) throws Exception {
5556
}
5657

5758
String appClasses[] = TestCommon.list("Hello");
59+
boolean dynamicMode = CDSTestUtils.DYNAMIC_DUMP;
5860

5961
// CASE 1: pre-JDK 6 compiled classes should be excluded from the dump
60-
OutputAnalyzer output = TestCommon.dump(jar, appClasses);
61-
TestCommon.checkExecReturn(output, 0, true, "Pre JDK 6 class not supported by CDS");
62+
OutputAnalyzer output = TestCommon.dump(jar, appClasses, "-Xlog:class+load,cds=debug");
63+
TestCommon.checkExecReturn(output, 0,
64+
dynamicMode ? true : false,
65+
"Pre JDK 6 class not supported by CDS");
6266

6367
TestCommon.run(
6468
"-cp", jar,
69+
"-Xlog:class+load",
6570
"Hello")
66-
.assertNormalExit("Hello Unicode world (Old)");
71+
.assertNormalExit(out -> {
72+
out.shouldContain("Hello Unicode world (Old)");
73+
if (!dynamicMode) {
74+
out.shouldContain("Hello source: shared objects file");
75+
} else {
76+
out.shouldMatch(".class.load. Hello source:.*OldClassTest_old.jar");
77+
}
78+
});
6779

6880
// CASE 2: if we exlcude old version of this class, we should not pick up
6981
// the newer version of this class in a subsequent classpath element.
7082
String classpath = jar + File.pathSeparator + jarSrcFile.getPath();
7183
output = TestCommon.dump(classpath, appClasses);
72-
TestCommon.checkExecReturn(output, 0, true, "Pre JDK 6 class not supported by CDS");
84+
TestCommon.checkExecReturn(output, 0,
85+
dynamicMode ? true : false,
86+
"Pre JDK 6 class not supported by CDS");
7387

7488
TestCommon.run(
7589
"-cp", classpath,
90+
"-Xlog:class+load",
7691
"Hello")
77-
.assertNormalExit("Hello Unicode world (Old)");
92+
.assertNormalExit(out -> {
93+
out.shouldContain("Hello Unicode world (Old)");
94+
if (!dynamicMode) {
95+
out.shouldContain("Hello source: shared objects file");
96+
} else {
97+
out.shouldMatch(".class.load. Hello source:.*OldClassTest_old.jar");
98+
}
99+
});
78100
}
79101

80102
static void createTestJarFile(File jarSrcFile, File jarFile) throws Exception {

0 commit comments

Comments
 (0)