diff a/src/java.base/share/classes/java/io/ObjectStreamClass.java b/src/java.base/share/classes/java/io/ObjectStreamClass.java
--- a/src/java.base/share/classes/java/io/ObjectStreamClass.java
+++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java
@@ -28,10 +28,11 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InaccessibleObjectException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.RecordComponent;
 import java.lang.reflect.UndeclaredThrowableException;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
@@ -69,11 +70,11 @@
  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
  * loaded in this Java VM can be found/created using the lookup method.
  *
  * <p>The algorithm to compute the SerialVersionUID is described in
  * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
- *    <cite>Java Object Serialization Specification,</cite> Section 4.6, "Stream Unique Identifiers"</a>.
+ *    <cite>Java Object Serialization Specification</cite>, Section 4.6, "Stream Unique Identifiers"</a>.
  *
  * @spec serialization/index.html Java Object Serialization Specification
  * @author      Mike Warres
  * @author      Roger Riggs
  * @see ObjectStreamField
@@ -133,10 +134,12 @@
     private boolean isProxy;
     /** true if represents enum type */
     private boolean isEnum;
     /** true if represents record type */
     private boolean isRecord;
+    /** true if represents a value class */
+    private boolean isValue;
     /** true if represented class implements Serializable */
     private boolean serializable;
     /** true if represented class implements Externalizable */
     private boolean externalizable;
     /** true if desc has data written by class-defined writeObject method */
@@ -372,10 +375,11 @@
         this.cl = cl;
         name = cl.getName();
         isProxy = Proxy.isProxyClass(cl);
         isEnum = Enum.class.isAssignableFrom(cl);
         isRecord = cl.isRecord();
+        isValue = cl.isValue();
         serializable = Serializable.class.isAssignableFrom(cl);
         externalizable = Externalizable.class.isAssignableFrom(cl);
 
         Class<?> superCl = cl.getSuperclass();
         superDesc = (superCl != null) ? lookup(superCl, false) : null;
@@ -405,10 +409,13 @@
                     }
 
                     if (isRecord) {
                         canonicalCtr = canonicalRecordCtr(cl);
                         deserializationCtrs = new DeserializationConstructorsCache();
+                    } else if (isValue) {
+                        // Value objects are created using Unsafe.
+                        cons = null;
                     } else if (externalizable) {
                         cons = getExternalizableConstructor(cl);
                     } else {
                         cons = getSerializableConstructor(cl);
                         writeObjectMethod = getPrivateMethod(cl, "writeObject",
@@ -442,11 +449,11 @@
         }
 
         if (deserializeEx == null) {
             if (isEnum) {
                 deserializeEx = new ExceptionInfo(name, "enum type");
-            } else if (cons == null && !isRecord) {
+            } else if (cons == null && !(isRecord | isValue)) {
                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
             }
         }
         if (isRecord && canonicalCtr == null) {
             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
@@ -636,10 +643,11 @@
         numObjFields = model.numObjFields;
 
         if (osc != null) {
             localDesc = osc;
             isRecord = localDesc.isRecord;
+            isValue = localDesc.isValue;
             // canonical record constructor is shared
             canonicalCtr = localDesc.canonicalCtr;
             // cache of deserialization constructors is shared
             deserializationCtrs = localDesc.deserializationCtrs;
             writeObjectMethod = localDesc.writeObjectMethod;
@@ -917,10 +925,18 @@
     boolean isSerializable() {
         requireInitialized();
         return serializable;
     }
 
+    /**
+     * {@return {code true} if the class is a value class, {@code false} otherwise}
+     */
+    boolean isValue() {
+        requireInitialized();
+        return isValue;
+    }
+
     /**
      * Returns true if class descriptor represents externalizable class that
      * has written its data in 1.2 (block data) format, false otherwise.
      */
     boolean hasBlockExternalData() {
@@ -939,17 +955,18 @@
     }
 
     /**
      * Returns true if represented class is serializable/externalizable and can
      * be instantiated by the serialization runtime--i.e., if it is
-     * externalizable and defines a public no-arg constructor, or if it is
+     * externalizable and defines a public no-arg constructor, if it is
      * non-externalizable and its first non-serializable superclass defines an
-     * accessible no-arg constructor.  Otherwise, returns false.
+     * accessible no-arg constructor, or if the class is a value class.
+     * Otherwise, returns false.
      */
     boolean isInstantiable() {
         requireInitialized();
-        return (cons != null);
+        return (cons != null | isValue);
     }
 
     /**
      * Returns true if represented class is serializable (but not
      * externalizable) and defines a conformant writeObject method.  Otherwise,
@@ -1056,15 +1073,27 @@
             } catch (InstantiationError err) {
                 var ex = new InstantiationException();
                 ex.initCause(err);
                 throw ex;
             }
-        } else {
+        } else if (isValue) {
+            // Start with a buffered default value.
+            return FieldReflector.newValueInstance(cl);
+        }  else {
             throw new UnsupportedOperationException();
         }
     }
 
+    /**
+     * Finish the initialization of a value object.
+     * @param obj an object (larval if a value object)
+     * @return the finished object
+     */
+    Object finishValue(Object obj) {
+        return (isValue) ? FieldReflector.finishValueInstance(obj) : obj;
+    }
+
     /**
      * Invokes the writeObject method of the represented serializable class.
      * Throws UnsupportedOperationException if this class descriptor is not
      * associated with a class, or if the class is externalizable,
      * non-serializable or does not define writeObject.
@@ -1435,11 +1464,11 @@
         try {
             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
             cons.setAccessible(true);
             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
                 cons : null;
-        } catch (NoSuchMethodException ex) {
+        } catch (NoSuchMethodException | InaccessibleObjectException ex) {
             return null;
         }
     }
 
     /**
@@ -1928,10 +1957,33 @@
         /** field type codes */
         private final char[] typeCodes;
         /** field types */
         private final Class<?>[] types;
 
+        /**
+         * Return a new instance of the class using Unsafe.uninitializedDefaultValue
+         * and buffer it.
+         * @param clazz The value class
+         * @return a buffered default value
+         */
+        static Object newValueInstance(Class<?> clazz) throws InstantiationException{
+            assert clazz.isValue() : "Should be a value class";
+            // may not be implicitly constructible; so allocate with Unsafe
+            Object obj = UNSAFE.uninitializedDefaultValue(clazz);
+            return UNSAFE.makePrivateBuffer(obj);
+        }
+
+        /**
+         * Finish a value object, clear the larval state and returning the value object.
+         * @param obj a buffered value object in a larval state
+         * @return the finished value object
+         */
+        static Object finishValueInstance(Object obj) {
+            assert (obj.getClass().isValue()) : "Should be a value class";
+            return UNSAFE.finishPrivateBuffer(obj);
+        }
+
         /**
          * Constructs FieldReflector capable of setting/getting values from the
          * subset of fields whose ObjectStreamFields contain non-null
          * reflective Field objects.  ObjectStreamFields with null Fields are
          * treated as filler, for which get operations return default values
@@ -2048,12 +2100,16 @@
             /* assuming checkDefaultSerialize() has been called on the class
              * descriptor this FieldReflector was obtained from, no field keys
              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
              */
             for (int i = numPrimFields; i < fields.length; i++) {
+                Field f = fields[i].getField();
                 vals[offsets[i]] = switch (typeCodes[i]) {
-                    case 'L', '[' -> UNSAFE.getReference(obj, readKeys[i]);
+                    case 'L', '[' ->
+                            UNSAFE.isFlatField(f)
+                                    ? UNSAFE.getValue(obj, readKeys[i], f.getType())
+                                    : UNSAFE.getReference(obj, readKeys[i]);
                     default       -> throw new InternalError();
                 };
             }
         }
 
@@ -2086,25 +2142,30 @@
                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
                     continue;           // discard value
                 }
                 switch (typeCodes[i]) {
                     case 'L', '[' -> {
+                        Field f = fields[i].getField();
                         Object val = vals[offsets[i]];
                         if (val != null &&
                             !types[i - numPrimFields].isInstance(val))
                         {
-                            Field f = fields[i].getField();
                             throw new ClassCastException(
                                 "cannot assign instance of " +
                                 val.getClass().getName() + " to field " +
                                 f.getDeclaringClass().getName() + "." +
                                 f.getName() + " of type " +
                                 f.getType().getName() + " in instance of " +
                                 obj.getClass().getName());
                         }
-                        if (!dryRun)
-                            UNSAFE.putReference(obj, key, val);
+                        if (!dryRun) {
+                            if (UNSAFE.isFlatField(f)) {
+                                UNSAFE.putValue(obj, key, f.getType(), val);
+                            } else {
+                                UNSAFE.putReference(obj, key, val);
+                            }
+                        }
                     }
                     default -> throw new InternalError();
                 }
             }
         }
