diff a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationFrame.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationFrame.java
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationFrame.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationFrame.java
@@ -22,11 +22,16 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 package jdk.internal.classfile.impl.verifier;
 
+import java.lang.classfile.constantpool.NameAndTypeEntry;
+import java.lang.classfile.constantpool.Utf8Entry;
 import java.util.Arrays;
+import java.util.Set;
+
+import jdk.internal.classfile.impl.TemporaryConstantPool;
 
 /// From `stackMapFrame.cpp`.
 class VerificationFrame {
 
     public static final int FLAG_THIS_UNINIT = 0x01;
@@ -35,22 +40,25 @@
     private int _locals_size, _stack_size;
     private int _stack_mark;
     private final int _max_locals, _max_stack;
     private int _flags;
     private final VerificationType[] _locals, _stack;
+    private Set<NameAndTypeEntry> _assert_unset_fields;
     private final VerifierImpl _verifier;
 
-    public VerificationFrame(int offset, int flags, int locals_size, int stack_size, int max_locals, int max_stack, VerificationType[] locals, VerificationType[] stack, VerifierImpl v) {
+    public VerificationFrame(int offset, int flags, int locals_size, int stack_size, int max_locals, int max_stack,
+                             VerificationType[] locals, VerificationType[] stack, Set<NameAndTypeEntry> assert_unset_fields, VerifierImpl v) {
         this._offset = offset;
         this._locals_size = locals_size;
         this._stack_size = stack_size;
         this._stack_mark = -1;
         this._max_locals = max_locals;
         this._max_stack = max_stack;
         this._flags = flags;
         this._locals = locals;
         this._stack = stack;
+        this._assert_unset_fields = assert_unset_fields;
         this._verifier = v;
     }
 
     @Override
     public String toString() {
@@ -107,10 +115,54 @@
 
     boolean flag_this_uninit() {
         return (_flags & FLAG_THIS_UNINIT) == FLAG_THIS_UNINIT;
     }
 
+    Set<NameAndTypeEntry> assert_unset_fields() {
+        return _assert_unset_fields;
+    }
+
+    void set_assert_unset_fields(Set<NameAndTypeEntry> table) {
+        _assert_unset_fields = table;
+    }
+
+    // Called when verifying putfields to mark strict instance fields as satisfied
+    boolean satisfy_unset_field(Utf8Entry name, Utf8Entry signature) {
+        var nat = TemporaryConstantPool.INSTANCE.nameAndTypeEntry(name, signature);
+        return _assert_unset_fields.remove(nat);
+    }
+
+    // Verify that all strict fields have been initialized
+    // Strict fields must be initialized before the super constructor is called
+    boolean verify_unset_fields_satisfied() {
+        return _assert_unset_fields.isEmpty();
+    }
+
+    // Merge incoming unset strict fields from StackMapTable with
+    // initial strict instance fields
+    Set<NameAndTypeEntry> merge_unset_fields(Set<NameAndTypeEntry> new_fields) {
+        // ClassFile API: We track all strict fields in another structure, noop here
+        return new_fields;
+    }
+
+    boolean verify_unset_fields_compatibility(Set<NameAndTypeEntry> target_table) {
+        for (var e : _assert_unset_fields) {
+            if (!target_table.contains(e))
+                return false;
+        }
+        return true;
+    }
+
+    void unsatisfied_strict_fields_error(VerificationWrapper klass, int bci) {
+        _verifier.verifyError("All strict final fields must be initialized before super(): %d field(s), %s"
+                .formatted(_assert_unset_fields.size(), _assert_unset_fields));
+    }
+
+    void print_strict_fields(Set<NameAndTypeEntry> table) {
+        // ignore, we don't do stdout/err
+    }
+
     void reset() {
         for (int i = 0; i < _max_locals; i++) {
             _locals[i] = VerificationType.bogus_type;
         }
         for (int i = 0; i < _max_stack; i++) {
@@ -179,11 +231,11 @@
         }
         pop_stack_ex(type1);
         pop_stack_ex(type2);
     }
 
-    VerificationFrame(int max_locals, int max_stack, VerifierImpl verifier) {
+    VerificationFrame(int max_locals, int max_stack, Set<NameAndTypeEntry> initial_strict_fields, VerifierImpl verifier) {
         _offset = 0;
         _locals_size = 0;
         _stack_size = 0;
         _stack_mark = 0;
         _max_locals = max_locals;
@@ -196,16 +248,17 @@
             _locals[i] = VerificationType.bogus_type;
         }
         for (int i = 0; i < max_stack; i++) {
             _stack[i] = VerificationType.bogus_type;
         }
+        _assert_unset_fields = initial_strict_fields;
     }
 
     VerificationFrame frame_in_exception_handler(int flags) {
         return new VerificationFrame(_offset, flags, _locals_size, 0,
                 _max_locals, _max_stack, _locals, new VerificationType[1],
-                _verifier);
+                _assert_unset_fields, _verifier);
     }
 
     void initialize_object(VerificationType old_object, VerificationType new_object) {
         int i;
         for (i = 0; i < _max_locals; i++) {
@@ -297,10 +350,20 @@
         mismatch_loc = is_assignable_to(_stack, target.stack(), _stack_size);
         if (mismatch_loc != _stack_size) {
             _verifier.verifyError("Bad type", this, target);
         }
 
+        // Check that assert unset fields are compatible
+        boolean compatible = verify_unset_fields_compatibility(target.assert_unset_fields());
+        if (!compatible) {
+            print_strict_fields(assert_unset_fields());
+            print_strict_fields(target.assert_unset_fields());
+            _verifier.verifyError("Strict fields mismatch from %s to %s".formatted(
+                    assert_unset_fields(), target.assert_unset_fields()), this, target);
+            return false;
+        }
+
         if ((_flags | target.flags()) == target.flags()) {
             return true;
         } else {
             _verifier.verifyError("Bad flags", this, target);
         }
