diff a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
@@ -23,20 +23,23 @@
  * questions.
  */
 
 package java.lang.invoke;
 
+import jdk.internal.misc.PreviewFeatures;
 import jdk.internal.misc.CDS;
 import jdk.internal.org.objectweb.asm.*;
 import jdk.internal.util.ClassFileDumper;
 import sun.invoke.util.BytecodeDescriptor;
 import sun.invoke.util.VerifyAccess;
 import sun.security.action.GetBooleanAction;
 
 import java.io.Serializable;
 import java.lang.constant.ConstantDescs;
+import java.lang.reflect.AccessFlag;
 import java.lang.reflect.Modifier;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
 import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
@@ -312,11 +315,12 @@
                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
             }
             interfaceNames = itfs.toArray(new String[itfs.size()]);
         }
 
-        cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
+        int version = CLASSFILE_VERSION | (PreviewFeatures.isEnabled() ? Opcodes.V_PREVIEW : 0);
+        cw.visit(version, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
                  lambdaClassName, null,
                  JAVA_LANG_OBJECT, interfaceNames);
 
         // Generate final fields to be filled in by constructor
         for (int i = 0; i < argDescs.length; i++) {
@@ -350,10 +354,22 @@
         if (isSerializable)
             generateSerializationFriendlyMethods();
         else if (accidentallySerializable)
             generateSerializationHostileMethods();
 
+        // generate LoadableDescriptors attribute if it references any value class
+        if (PreviewFeatures.isEnabled()) {
+            LoadableDescriptorsAttributeBuilder builder = new LoadableDescriptorsAttributeBuilder(targetClass);
+            builder.add(factoryType)
+                    .add(interfaceMethodType)
+                    .add(implMethodType)
+                    .add(dynamicMethodType)
+                    .add(altMethods);
+            if (!builder.isEmpty())
+                cw.visitAttribute(builder.build());
+        }
+
         cw.visitEnd();
 
         // Define the generated class in this VM.
 
         final byte[] classBytes = cw.toByteArray();
@@ -564,10 +580,72 @@
                 default -> throw new InternalError("Unexpected invocation kind: " + implKind);
             };
         }
     }
 
+    /*
+     * LoadableDescriptors attribute builder
+     */
+    static class LoadableDescriptorsAttributeBuilder {
+        private final Set<String> loadableDescriptors = new HashSet<>();
+        LoadableDescriptorsAttributeBuilder(Class<?> targetClass) {
+            if (requiresLoadableDescriptors(targetClass)) {
+                loadableDescriptors.add(Type.getDescriptor(targetClass));
+            }
+        }
+
+        /*
+         * Add the value types referenced in the given MethodType.
+         */
+        LoadableDescriptorsAttributeBuilder add(MethodType mt) {
+            // parameter types
+            for (Class<?> paramType : mt.ptypes()) {
+                if (requiresLoadableDescriptors(paramType)) {
+                    loadableDescriptors.add(Type.getDescriptor(paramType));
+                }
+            }
+            // return type
+            if (requiresLoadableDescriptors(mt.returnType())) {
+                loadableDescriptors.add(Type.getDescriptor(mt.returnType()));
+            }
+            return this;
+        }
+
+        LoadableDescriptorsAttributeBuilder add(MethodType... mtypes) {
+            for (MethodType mt : mtypes) {
+                add(mt);
+            }
+            return this;
+        }
+
+        boolean requiresLoadableDescriptors(Class<?> cls) {
+            return cls.isValue() && cls.accessFlags().contains(AccessFlag.FINAL);
+        }
+
+        boolean isEmpty() {
+            return loadableDescriptors.isEmpty();
+        }
+
+        Attribute build() {
+            return new Attribute("LoadableDescriptors") {
+                @Override
+                protected ByteVector write(ClassWriter cw,
+                                           byte[] code,
+                                           int len,
+                                           int maxStack,
+                                           int maxLocals) {
+                    ByteVector attr = new ByteVector();
+                    attr.putShort(loadableDescriptors.size());
+                    for (String s : loadableDescriptors) {
+                        attr.putShort(cw.newUTF8(s));
+                    }
+                    return attr;
+                }
+            };
+        }
+    }
+
     static int getParameterSize(Class<?> c) {
         if (c == Void.TYPE) {
             return 0;
         } else if (c == Long.TYPE || c == Double.TYPE) {
             return 2;
