8331497: Implement JEP 483: Ahead-of-Time Class Loading & Linking

Reviewed-by: jrose, kvn, heidinga, asmehra, vlivanov
This commit is contained in:
Ioi Lam 2024-11-15 22:28:54 +00:00
parent 276251c44a
commit 41a2d49f0a
106 changed files with 6601 additions and 760 deletions

View file

@ -225,6 +225,11 @@ public final class Class<T> implements java.io.Serializable,
private static native void registerNatives();
static {
runtimeSetup();
}
// Called from JVM when loading an AOT cache
private static void runtimeSetup() {
registerNatives();
}
@ -3425,6 +3430,15 @@ public final class Class<T> implements java.io.Serializable,
}
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
* Class object does not represent an enum class.

View file

@ -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.
*
* 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.NF_loop;
import static java.lang.invoke.MethodHandleImpl.makeIntrinsic;
import static java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE;
/** Transforms on LFs.
* 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
* Transforms.
*/
private static final class Transform extends SoftReference<LambdaForm> {
private static final class Transform {
final Object cache;
final long packedBytes;
final byte[] fullBytes;
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.fullBytes = fullBytes;
}
@ -135,6 +141,15 @@ class LambdaFormEditor {
}
return buf.toString();
}
@SuppressWarnings({"rawtypes", "unchecked"})
public LambdaForm get() {
if (cache instanceof LambdaForm lf) {
return lf;
} else {
return ((SoftReference<LambdaForm>)cache).get();
}
}
}
/**

View file

@ -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.
*
* 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.ref.CleanerFactory;
import sun.invoke.util.Wrapper;
import sun.security.action.GetPropertyAction;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.util.Properties;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
@ -690,4 +692,23 @@ class MethodHandleNatives {
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
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"));
}
}

View file

@ -31,6 +31,8 @@ import java.lang.constant.MethodTypeDesc;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Supplier;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -40,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.util.ReferencedKeySet;
import jdk.internal.util.ReferenceKey;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.BytecodeDescriptor;
import sun.invoke.util.VerifyType;
@ -391,6 +394,17 @@ class MethodType
ptypes = NO_PTYPES; trusted = true;
}
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);
if (mt != null)
return mt;
@ -409,7 +423,9 @@ class MethodType
mt.form = MethodTypeForm.findForm(mt);
return internTable.intern(mt);
}
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.
@ -1380,4 +1396,30 @@ s.writeObject(this.parameterArray());
wrapAlt = null;
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();
}
}

View file

@ -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.
*
* 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 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
@ -51,7 +52,7 @@ final class MethodTypeForm {
final MethodType basicType; // the canonical erasure, with primitives simplified
// Cached adapter information:
final SoftReference<MethodHandle>[] methodHandles;
private final Object[] methodHandles;
// Indexes into methodHandles:
static final int
@ -61,7 +62,7 @@ final class MethodTypeForm {
MH_LIMIT = 3;
// Cached lambda form information, for basic types only:
final SoftReference<LambdaForm>[] lambdaForms;
private final Object[] lambdaForms;
// Indexes into lambdaForms:
static final int
@ -109,39 +110,55 @@ final class MethodTypeForm {
return basicType;
}
@SuppressWarnings({"rawtypes", "unchecked"})
public MethodHandle cachedMethodHandle(int which) {
SoftReference<MethodHandle> entry = methodHandles[which];
return (entry != null) ? entry.get() : null;
Object entry = methodHandles[which];
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) {
// Simulate a CAS, to avoid racy duplication of results.
SoftReference<MethodHandle> entry = methodHandles[which];
if (entry != null) {
MethodHandle prev = entry.get();
if (prev != null) {
return prev;
}
MethodHandle prev = cachedMethodHandle(which);
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;
}
@SuppressWarnings({"rawtypes", "unchecked"})
public LambdaForm cachedLambdaForm(int which) {
SoftReference<LambdaForm> entry = lambdaForms[which];
return (entry != null) ? entry.get() : null;
Object entry = lambdaForms[which];
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) {
// Simulate a CAS, to avoid racy duplication of results.
SoftReference<LambdaForm> entry = lambdaForms[which];
if (entry != null) {
LambdaForm prev = entry.get();
if (prev != null) {
return prev;
}
LambdaForm prev = cachedLambdaForm(which);
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;
}
@ -191,8 +208,8 @@ final class MethodTypeForm {
this.primitiveCount = primitiveCount;
this.parameterSlotCount = (short)pslotCount;
this.lambdaForms = new SoftReference[LF_LIMIT];
this.methodHandles = new SoftReference[MH_LIMIT];
this.lambdaForms = new Object[LF_LIMIT];
this.methodHandles = new Object[MH_LIMIT];
} else {
this.basicType = MethodType.methodType(basicReturnType, basicPtypes, true);
// fill in rest of data from the basic type:

View file

@ -1080,6 +1080,8 @@ public final class StringConcatFactory {
* without copying.
*/
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 METHOD_NAME = "concat";

View file

@ -70,6 +70,7 @@ import java.util.function.ToLongFunction;
import java.util.stream.Stream;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
import jdk.internal.vm.annotation.Stable;
/**
* 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
/** 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.