diff a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
--- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
+++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
@@ -130,13 +130,13 @@
         if (member.isConstructor())
             return makeAllocator(member.getDeclaringClass(), member);
         return make(member.getDeclaringClass(), member);
     }
     static DirectMethodHandle makeAllocator(Class<?> instanceClass, MemberName ctor) {
-        assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
+        assert(ctor.isConstructor()) : ctor;
         ctor = ctor.asConstructor();
-        assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
+        assert(ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
         MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
         LambdaForm lform = preparedLambdaForm(ctor);
         MemberName init = ctor.asSpecial();
         assert(init.getMethodType().returnType() == void.class);
         return new Constructor(mtype, lform, ctor, true, init, instanceClass);
@@ -602,10 +602,25 @@
     /*non-public*/
     static Object checkCast(Object mh, Object obj) {
         return ((DirectMethodHandle) mh).checkCast(obj);
     }
 
+    @ForceInline
+    /*non-public*/ static Class<?> fieldType(Object accessorObj) {
+        return ((Accessor) accessorObj).fieldType;
+    }
+
+    @ForceInline
+    /*non-public*/ static Class<?> staticFieldType(Object accessorObj) {
+        return ((StaticAccessor) accessorObj).fieldType;
+    }
+
+    @ForceInline
+    /*non-public*/ static Object zeroInstanceIfNull(Class<?> fieldType, Object obj) {
+        return obj != null ? obj : UNSAFE.uninitializedDefaultValue(fieldType);
+    }
+
     Object checkCast(Object obj) {
         return member.getMethodType().returnType().cast(obj);
     }
 
     // Caching machinery for field accessors:
@@ -616,77 +631,81 @@
             AF_PUTSTATIC       = 3,
             AF_GETSTATIC_INIT  = 4,
             AF_PUTSTATIC_INIT  = 5,
             AF_LIMIT           = 6;
     // Enumerate the different field kinds using Wrapper,
-    // with an extra case added for checked references.
+    // with an extra case added for checked references and value field access
     static final int
-            FT_LAST_WRAPPER    = Wrapper.COUNT-1,
-            FT_UNCHECKED_REF   = Wrapper.OBJECT.ordinal(),
-            FT_CHECKED_REF     = FT_LAST_WRAPPER+1,
-            FT_LIMIT           = FT_LAST_WRAPPER+2;
-    private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
+            FT_LAST_WRAPPER     = Wrapper.COUNT-1,
+            FT_UNCHECKED_REF    = Wrapper.OBJECT.ordinal(),
+            FT_CHECKED_REF      = FT_LAST_WRAPPER+1,
+            FT_CHECKED_VALUE    = FT_LAST_WRAPPER+2,  // flat vs non-flat x null value vs null-restricted value
+            FT_LIMIT            = FT_LAST_WRAPPER+6;
+    private static int afIndex(byte formOp, boolean isVolatile, boolean isFlat, boolean isNullRestricted, int ftypeKind) {
         return ((formOp * FT_LIMIT * 2)
                 + (isVolatile ? FT_LIMIT : 0)
+                + (isFlat ? 1 : 0)
+                + (isNullRestricted ? 1 : 0)
                 + ftypeKind);
     }
     @Stable
     private static final LambdaForm[] ACCESSOR_FORMS
-            = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
+            = new LambdaForm[afIndex(AF_LIMIT, false, false, false, 0)];
     static int ftypeKind(Class<?> ftype) {
         if (ftype.isPrimitive()) {
             return Wrapper.forPrimitiveType(ftype).ordinal();
         } else if (ftype.isInterface() || ftype.isAssignableFrom(Object.class)) {
             // retyping can be done without a cast
             return FT_UNCHECKED_REF;
         } else {
-            return FT_CHECKED_REF;
+            return ftype.isValue() ? FT_CHECKED_VALUE : FT_CHECKED_REF;
         }
     }
 
     /**
      * Create a LF which can access the given field.
      * Cache and share this structure among all fields with
      * the same basicType and refKind.
      */
     private static LambdaForm preparedFieldLambdaForm(MemberName m) {
         Class<?> ftype = m.getFieldType();
-        boolean isVolatile = m.isVolatile();
         byte formOp = switch (m.getReferenceKind()) {
             case REF_getField  -> AF_GETFIELD;
             case REF_putField  -> AF_PUTFIELD;
             case REF_getStatic -> AF_GETSTATIC;
             case REF_putStatic -> AF_PUTSTATIC;
             default -> throw new InternalError(m.toString());
         };
         if (shouldBeInitialized(m)) {
             // precompute the barrier-free version:
-            preparedFieldLambdaForm(formOp, isVolatile, ftype);
+            preparedFieldLambdaForm(formOp, m.isVolatile(), m.isFlat(), m.isNullRestricted(), ftype);
             assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
                    (AF_PUTSTATIC_INIT - AF_PUTSTATIC));
             formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
         }
-        LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
+        LambdaForm lform = preparedFieldLambdaForm(formOp, m.isVolatile(), m.isFlat(), m.isNullRestricted(), ftype);
         maybeCompile(lform, m);
         assert(lform.methodType().dropParameterTypes(0, 1)
                 .equals(m.getInvocationType().basicType()))
                 : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
         return lform;
     }
-    private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
+
+    private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile,
+                                                      boolean isFlat, boolean isNullRestricted, Class<?> ftype) {
         int ftypeKind = ftypeKind(ftype);
-        int afIndex = afIndex(formOp, isVolatile, ftypeKind);
+        int afIndex = afIndex(formOp, isVolatile, isFlat, isNullRestricted, ftypeKind);
         LambdaForm lform = ACCESSOR_FORMS[afIndex];
         if (lform != null)  return lform;
-        lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind);
+        lform = makePreparedFieldLambdaForm(formOp, isVolatile,isFlat, isNullRestricted, ftypeKind);
         ACCESSOR_FORMS[afIndex] = lform;  // don't bother with a CAS
         return lform;
     }
 
     private static final Wrapper[] ALL_WRAPPERS = Wrapper.values();
 
-    private static Kind getFieldKind(boolean isGetter, boolean isVolatile, Wrapper wrapper) {
+    private static Kind getFieldKind(boolean isGetter, boolean isVolatile, boolean isFlat, Wrapper wrapper) {
         if (isGetter) {
             if (isVolatile) {
                 switch (wrapper) {
                     case BOOLEAN: return GET_BOOLEAN_VOLATILE;
                     case BYTE:    return GET_BYTE_VOLATILE;
@@ -694,11 +713,11 @@
                     case CHAR:    return GET_CHAR_VOLATILE;
                     case INT:     return GET_INT_VOLATILE;
                     case LONG:    return GET_LONG_VOLATILE;
                     case FLOAT:   return GET_FLOAT_VOLATILE;
                     case DOUBLE:  return GET_DOUBLE_VOLATILE;
-                    case OBJECT:  return GET_REFERENCE_VOLATILE;
+                    case OBJECT:  return isFlat ? GET_VALUE_VOLATILE : GET_REFERENCE_VOLATILE;
                 }
             } else {
                 switch (wrapper) {
                     case BOOLEAN: return GET_BOOLEAN;
                     case BYTE:    return GET_BYTE;
@@ -706,11 +725,11 @@
                     case CHAR:    return GET_CHAR;
                     case INT:     return GET_INT;
                     case LONG:    return GET_LONG;
                     case FLOAT:   return GET_FLOAT;
                     case DOUBLE:  return GET_DOUBLE;
-                    case OBJECT:  return GET_REFERENCE;
+                    case OBJECT:  return isFlat ? GET_VALUE : GET_REFERENCE;
                 }
             }
         } else {
             if (isVolatile) {
                 switch (wrapper) {
@@ -720,11 +739,11 @@
                     case CHAR:    return PUT_CHAR_VOLATILE;
                     case INT:     return PUT_INT_VOLATILE;
                     case LONG:    return PUT_LONG_VOLATILE;
                     case FLOAT:   return PUT_FLOAT_VOLATILE;
                     case DOUBLE:  return PUT_DOUBLE_VOLATILE;
-                    case OBJECT:  return PUT_REFERENCE_VOLATILE;
+                    case OBJECT:  return isFlat ? PUT_VALUE_VOLATILE : PUT_REFERENCE_VOLATILE;
                 }
             } else {
                 switch (wrapper) {
                     case BOOLEAN: return PUT_BOOLEAN;
                     case BYTE:    return PUT_BYTE;
@@ -732,34 +751,45 @@
                     case CHAR:    return PUT_CHAR;
                     case INT:     return PUT_INT;
                     case LONG:    return PUT_LONG;
                     case FLOAT:   return PUT_FLOAT;
                     case DOUBLE:  return PUT_DOUBLE;
-                    case OBJECT:  return PUT_REFERENCE;
+                    case OBJECT:  return isFlat ? PUT_VALUE : PUT_REFERENCE;
                 }
             }
         }
         throw new AssertionError("Invalid arguments");
     }
 
-    static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
+    /** invoked by GenerateJLIClassesHelper */
+    static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftype) {
+        return makePreparedFieldLambdaForm(formOp, isVolatile, false, false, ftype);
+    }
+
+    private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile,
+                                                          boolean isFlat, boolean isNullRestricted, int ftypeKind) {
         boolean isGetter  = (formOp & 1) == (AF_GETFIELD & 1);
         boolean isStatic  = (formOp >= AF_GETSTATIC);
         boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
-        boolean needsCast = (ftypeKind == FT_CHECKED_REF);
+        boolean needsCast = (ftypeKind == FT_CHECKED_REF || ftypeKind == FT_CHECKED_VALUE);
         Wrapper fw = (needsCast ? Wrapper.OBJECT : ALL_WRAPPERS[ftypeKind]);
         Class<?> ft = fw.primitiveType();
-        assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
+        assert(needsCast ? true : ftypeKind(ft) == ftypeKind);
 
         // getObject, putIntVolatile, etc.
-        Kind kind = getFieldKind(isGetter, isVolatile, fw);
+        Kind kind = getFieldKind(isGetter, isVolatile, isFlat, fw);
 
         MethodType linkerType;
-        if (isGetter)
-            linkerType = MethodType.methodType(ft, Object.class, long.class);
-        else
-            linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
+        if (isGetter) {
+            linkerType = isFlat
+                            ? MethodType.methodType(ft, Object.class, long.class, Class.class)
+                            : MethodType.methodType(ft, Object.class, long.class);
+        } else {
+            linkerType = isFlat
+                            ? MethodType.methodType(void.class, Object.class, long.class, Class.class, ft)
+                            : MethodType.methodType(void.class, Object.class, long.class, ft);
+        }
         MemberName linker = new MemberName(Unsafe.class, kind.methodName, linkerType, REF_invokeVirtual);
         try {
             linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, LM_TRUSTED,
                                               NoSuchMethodException.class);
         } catch (ReflectiveOperationException ex) {
@@ -786,36 +816,58 @@
         final int F_HOLDER  = (isStatic ? nameCursor++ : -1);  // static base if any
         final int F_OFFSET  = nameCursor++;  // Either static offset or field offset.
         final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
         final int U_HOLDER  = nameCursor++;  // UNSAFE holder
         final int INIT_BAR  = (needsInit ? nameCursor++ : -1);
+        final int VALUE_TYPE = (isFlat ? nameCursor++ : -1);
+        final int NULL_CHECK  = (isNullRestricted && !isGetter ? nameCursor++ : -1);
         final int PRE_CAST  = (needsCast && !isGetter ? nameCursor++ : -1);
         final int LINKER_CALL = nameCursor++;
+        final int FIELD_TYPE = (isNullRestricted && isGetter ? nameCursor++ : -1);
+        final int ZERO_INSTANCE = (isNullRestricted && isGetter ? nameCursor++ : -1);
         final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
-        final int RESULT    = nameCursor-1;  // either the call or the cast
+        final int RESULT    = nameCursor-1;  // either the call, zero instance, or the cast
         Name[] names = invokeArguments(nameCursor - ARG_LIMIT, mtype);
         if (needsInit)
             names[INIT_BAR] = new Name(getFunction(NF_ensureInitialized), names[DMH_THIS]);
-        if (needsCast && !isGetter)
-            names[PRE_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[SET_VALUE]);
+        if (!isGetter) {
+            if (isNullRestricted)
+                names[NULL_CHECK] = new Name(getFunction(NF_nullCheck), names[SET_VALUE]);
+            if (needsCast)
+                names[PRE_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[SET_VALUE]);
+        }
         Object[] outArgs = new Object[1 + linkerType.parameterCount()];
-        assert(outArgs.length == (isGetter ? 3 : 4));
+        assert (outArgs.length == (isGetter ? 3 : 4) + (isFlat ? 1 : 0));
         outArgs[0] = names[U_HOLDER] = new Name(getFunction(NF_UNSAFE));
         if (isStatic) {
             outArgs[1] = names[F_HOLDER]  = new Name(getFunction(NF_staticBase), names[DMH_THIS]);
             outArgs[2] = names[F_OFFSET]  = new Name(getFunction(NF_staticOffset), names[DMH_THIS]);
         } else {
             outArgs[1] = names[OBJ_CHECK] = new Name(getFunction(NF_checkBase), names[OBJ_BASE]);
             outArgs[2] = names[F_OFFSET]  = new Name(getFunction(NF_fieldOffset), names[DMH_THIS]);
         }
+        int x = 3;
+        if (isFlat) {
+            outArgs[x++] = names[VALUE_TYPE] = isStatic ? new Name(getFunction(NF_staticFieldType), names[DMH_THIS])
+                                                        : new Name(getFunction(NF_fieldType), names[DMH_THIS]);
+        }
         if (!isGetter) {
-            outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
+            outArgs[x] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
         }
         for (Object a : outArgs)  assert(a != null);
         names[LINKER_CALL] = new Name(linker, outArgs);
-        if (needsCast && isGetter)
-            names[POST_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[LINKER_CALL]);
+        if (isGetter) {
+            int argIndex = LINKER_CALL;
+            if (isNullRestricted) {
+                names[FIELD_TYPE] = isStatic ? new Name(getFunction(NF_staticFieldType), names[DMH_THIS])
+                                             : new Name(getFunction(NF_fieldType), names[DMH_THIS]);
+                names[ZERO_INSTANCE] = new Name(getFunction(NF_zeroInstance), names[FIELD_TYPE], names[LINKER_CALL]);
+                argIndex = ZERO_INSTANCE;
+            }
+            if (needsCast)
+                names[POST_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[argIndex]);
+        }
         for (Name n : names)  assert(n != null);
 
         LambdaForm form;
         if (needsCast || needsInit) {
             // can't use the pre-generated form when casting and/or initializing
@@ -857,11 +909,15 @@
             NF_checkCast = 7,
             NF_allocateInstance = 8,
             NF_constructorMethod = 9,
             NF_UNSAFE = 10,
             NF_checkReceiver = 11,
-            NF_LIMIT = 12;
+            NF_fieldType = 12,
+            NF_staticFieldType = 13,
+            NF_zeroInstance = 14,
+            NF_nullCheck = 15,
+            NF_LIMIT = 16;
 
     private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
 
     private static NamedFunction getFunction(byte func) {
         NamedFunction nf = NFS[func];
@@ -872,10 +928,12 @@
         nf = NFS[func] = createFunction(func);
         assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf));
         return nf;
     }
 
+    private static final MethodType CLS_OBJ_TYPE = MethodType.methodType(Class.class, Object.class);
+
     private static final MethodType OBJ_OBJ_TYPE = MethodType.methodType(Object.class, Object.class);
 
     private static final MethodType LONG_OBJ_TYPE = MethodType.methodType(long.class, Object.class);
 
     private static NamedFunction createFunction(byte func) {
@@ -911,10 +969,18 @@
                     member = new MemberName(DirectMethodHandle.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
                     return new NamedFunction(
                             MemberName.getFactory().resolveOrFail(REF_invokeVirtual, member,
                                                                   DirectMethodHandle.class, LM_TRUSTED,
                                                                   NoSuchMethodException.class));
+                case NF_fieldType:
+                    return getNamedFunction("fieldType", CLS_OBJ_TYPE);
+                case NF_staticFieldType:
+                    return getNamedFunction("staticFieldType", CLS_OBJ_TYPE);
+                case NF_zeroInstance:
+                    return getNamedFunction("zeroInstanceIfNull", MethodType.methodType(Object.class, Class.class, Object.class));
+                case NF_nullCheck:
+                    return getNamedFunction("nullCheck", OBJ_OBJ_TYPE);
                 default:
                     throw newInternalError("Unknown function: " + func);
             }
         } catch (ReflectiveOperationException ex) {
             throw newInternalError(ex);
