mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
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
106 changed files with 6601 additions and 760 deletions
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue