diff a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2024, Alibaba Group Holding Limited. 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
  * under the terms of the GNU General Public License version 2 only, as
@@ -28,24 +28,20 @@
 
 import java.lang.classfile.Attribute;
 import java.lang.classfile.Attributes;
 import java.lang.classfile.Label;
 import java.lang.classfile.attribute.StackMapTableAttribute;
-import java.lang.classfile.constantpool.ClassEntry;
-import java.lang.classfile.constantpool.ConstantDynamicEntry;
-import java.lang.classfile.constantpool.ConstantPoolBuilder;
-import java.lang.classfile.constantpool.InvokeDynamicEntry;
-import java.lang.classfile.constantpool.MemberRefEntry;
-import java.lang.classfile.constantpool.Utf8Entry;
+import java.lang.classfile.constantpool.*;
 import java.lang.constant.ClassDesc;
 import java.lang.constant.MethodTypeDesc;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
+import jdk.internal.classfile.impl.WritableField.UnsetField;
 import jdk.internal.constant.ClassOrInterfaceDescImpl;
 import jdk.internal.util.Preconditions;
 
 import static java.lang.classfile.ClassFile.*;
 import static java.lang.classfile.constantpool.PoolEntry.*;
@@ -155,10 +151,11 @@
                 dcb.methodInfo.methodTypeSymbol(),
                 (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
                 dcb.bytecodesBufWriter.bytecodeView(),
                 dcb.constantPool,
                 dcb.context,
+                buf.getStrictInstanceFields(),
                 dcb.handlers);
     }
 
     private static final String OBJECT_INITIALIZER_NAME = "<init>";
     private static final int FLAG_THIS_UNINIT = 0x01;
@@ -196,10 +193,11 @@
     private final boolean isStatic;
     private final LabelContext labelContext;
     private final List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers;
     private final List<RawExceptionCatch> rawHandlers;
     private final ClassHierarchyImpl classHierarchy;
+    private final UnsetField[] strictFieldsToPut; // exact-sized, do not modify this copy!
     private final boolean patchDeadCode;
     private final boolean filterDeadLabels;
     private Frame[] frames = EMPTY_FRAME_ARRAY;
     private int framesCount = 0;
     private final Frame currentFrame;
@@ -227,10 +225,11 @@
                      MethodTypeDesc methodDesc,
                      boolean isStatic,
                      RawBytecodeHelper.CodeRange bytecode,
                      SplitConstantPool cp,
                      ClassFileImpl context,
+                     UnsetField[] strictFields,
                      List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
         this.thisType = Type.referenceType(thisClass);
         this.methodName = methodName;
         this.methodDesc = methodDesc;
         this.isStatic = isStatic;
@@ -241,10 +240,15 @@
         this.rawHandlers = new ArrayList<>(handlers.size());
         this.classHierarchy = new ClassHierarchyImpl(context.classHierarchyResolver());
         this.patchDeadCode = context.patchDeadCode();
         this.filterDeadLabels = context.dropDeadLabels();
         this.currentFrame = new Frame(classHierarchy);
+        if (OBJECT_INITIALIZER_NAME.equals(methodName)) {
+            this.strictFieldsToPut = strictFields;
+        } else {
+            this.strictFieldsToPut = UnsetField.EMPTY_ARRAY;
+        }
         generate();
     }
 
     /**
      * Calculated maximum number of the locals required
@@ -389,20 +393,29 @@
      */
     public Attribute<? extends StackMapTableAttribute> stackMapTableAttribute() {
         return framesCount == 0 ? null : new UnboundAttribute.AdHocAttribute<>(Attributes.stackMapTable()) {
             @Override
             public void writeBody(BufWriterImpl b) {
+                int countPos = b.size();
                 b.writeU2(framesCount);
+                int extraFrameCount = 0;
                 Frame prevFrame =  new Frame(classHierarchy);
-                prevFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType);
+                prevFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType, strictFieldsToPut);
                 prevFrame.trimAndCompress();
                 for (int i = 0; i < framesCount; i++) {
                     var fr = frames[i];
                     fr.trimAndCompress();
-                    fr.writeTo(b, prevFrame, cp);
+                    extraFrameCount += fr.writeTo(b, prevFrame, cp);
                     prevFrame = fr;
                 }
+                if (extraFrameCount > 0) {
+                    int size = framesCount + extraFrameCount;
+                    if (size != (char) size) {
+                        throw generatorError("Too many frames: " + size);
+                    }
+                    b.patchU2(countPos, size);
+                }
             }
 
             @Override
             public Utf8Entry attributeName() {
                 return cp.utf8Entry(Attributes.NAME_STACK_MAP_TABLE);
@@ -415,13 +428,12 @@
     }
 
     private void processMethod() {
         var frames = this.frames;
         var currentFrame = this.currentFrame;
-        currentFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType);
+        currentFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType, strictFieldsToPut);
         currentFrame.stackSize = 0;
-        currentFrame.flags = 0;
         currentFrame.offset = -1;
         int stackmapIndex = 0;
         var bcs = bytecode.start();
         boolean ncf = false;
         while (bcs.next()) {
@@ -760,11 +772,12 @@
             checkJumpTarget(currentFrame, target);
         }
     }
 
     private void processFieldInstructions(RawBytecodeHelper bcs) {
-        var desc = Util.fieldTypeSymbol(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).type());
+        var nameAndType = cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType();
+        var desc = Util.fieldTypeSymbol(nameAndType.type());
         var currentFrame = this.currentFrame;
         switch (bcs.opcode()) {
             case GETSTATIC ->
                 currentFrame.pushStack(desc);
             case PUTSTATIC -> {
@@ -773,10 +786,13 @@
             case GETFIELD -> {
                 currentFrame.decStack(1);
                 currentFrame.pushStack(desc);
             }
             case PUTFIELD -> {
+                if (strictFieldsToPut.length > 0) {
+                    currentFrame.putStrictField(nameAndType);
+                }
                 currentFrame.decStack(Util.isDoubleSlot(desc) ? 3 : 2);
             }
             default -> throw new AssertionError("Should not reach here");
         }
     }
@@ -796,11 +812,18 @@
                 Type type = currentFrame.popStack();
                 if (type == Type.UNITIALIZED_THIS_TYPE) {
                     if (inTryBlock) {
                         processExceptionHandlerTargets(bci, true);
                     }
+                    var owner = cp.entryByIndex(index, MemberRefEntry.class).owner();
+                    if (!owner.name().equalsString(((ClassOrInterfaceDescImpl) thisType.sym).internalName())
+                            && currentFrame.unsetFieldsSize != 0) {
+                        throw generatorError("Unset fields mismatch");
+                    }
                     currentFrame.initializeObject(type, thisType);
+                    currentFrame.unsetFieldsSize = 0;
+                    currentFrame.unsetFields = UnsetField.EMPTY_ARRAY;
                     thisUninit = true;
                 } else if (type.tag == ITEM_UNINITIALIZED) {
                     Type new_class_type = cpIndexToType(bcs.getU2(type.bci + 1), cp);
                     if (inTryBlock) {
                         processExceptionHandlerTargets(bci, thisUninit);
@@ -939,19 +962,20 @@
     }
 
     private final class Frame {
 
         int offset;
-        int localsSize, stackSize;
+        int localsSize, stackSize, unsetFieldsSize;
         int flags;
         int frameMaxStack = 0, frameMaxLocals = 0;
         boolean dirty = false;
         boolean localsChanged = false;
 
         private final ClassHierarchyImpl classHierarchy;
 
         private Type[] locals, stack;
+        private UnsetField[] unsetFields = UnsetField.EMPTY_ARRAY; // sorted, modifiable oversized array
 
         Frame(ClassHierarchyImpl classHierarchy) {
             this(-1, 0, 0, 0, null, null, classHierarchy);
         }
 
@@ -1024,11 +1048,12 @@
                 if (stack[i].equals(old_object)) {
                     stack[i] = new_object;
                 }
             }
             if (old_object == Type.UNITIALIZED_THIS_TYPE) {
-                flags = 0;
+                flags &= ~FLAG_THIS_UNINIT;
+                assert flags == 0 : flags;
             }
         }
 
         Frame checkLocal(int index) {
             if (index >= frameMaxLocals) frameMaxLocals = index + 1;
@@ -1041,10 +1066,28 @@
                 Arrays.fill(locals, current, locals.length, Type.TOP_TYPE);
             }
             return this;
         }
 
+        void putStrictField(NameAndTypeEntry nat) {
+            int shift = 0;
+            var array = unsetFields;
+            for (int i = 0; i < unsetFieldsSize; i++) {
+                var f = array[i];
+                if (f.name().equals(nat.name()) && f.type().equals(nat.type())) {
+                    shift++;
+                } else if (shift != 0) {
+                    array[i - shift] = array[i];
+                    array[i] = null;
+                }
+            }
+            if (shift > 1) {
+                throw generatorError(nat + "; discovered " + shift);
+            }
+            unsetFieldsSize -= shift;
+        }
+
         private void checkStack(int index) {
             if (index >= frameMaxStack) frameMaxStack = index + 1;
             if (stack == null) {
                 stack = new Type[index + FRAME_DEFAULT_CAPACITY];
                 Arrays.fill(stack, Type.TOP_TYPE);
@@ -1059,22 +1102,28 @@
             checkLocal(index);
             localsChanged |= !type.equals(locals[index]);
             locals[index] = type;
         }
 
-        void setLocalsFromArg(String name, MethodTypeDesc methodDesc, boolean isStatic, Type thisKlass) {
+        void setLocalsFromArg(String name, MethodTypeDesc methodDesc, boolean isStatic, Type thisKlass, UnsetField[] strictFieldsToPut) {
             int localsSize = 0;
             // Pre-emptively create a locals array that encompass all parameter slots
             checkLocal(Util.parameterSlots(methodDesc) + (isStatic ? -1 : 0));
             Type type;
             Type[] locals = this.locals;
             if (!isStatic) {
                 if (OBJECT_INITIALIZER_NAME.equals(name) && !CD_Object.equals(thisKlass.sym)) {
+                    int strictFieldCount = strictFieldsToPut.length;
+                    this.unsetFields = UnsetField.copyArray(strictFieldsToPut, strictFieldCount);
+                    this.unsetFieldsSize = strictFieldCount;
                     type = Type.UNITIALIZED_THIS_TYPE;
-                    flags |= FLAG_THIS_UNINIT;
+                    this.flags = FLAG_THIS_UNINIT;
                 } else {
+                    this.unsetFields = UnsetField.EMPTY_ARRAY;
+                    this.unsetFieldsSize = 0;
                     type = thisKlass;
+                    this.flags = 0;
                 }
                 locals[localsSize++] = type;
             }
             for (int i = 0; i < methodDesc.parameterCount(); i++) {
                 var desc = methodDesc.parameterType(i);
@@ -1107,24 +1156,29 @@
             if (src.localsSize > 0) System.arraycopy(src.locals, 0, locals, 0, src.localsSize);
             if (stack != null && src.stackSize < stack.length) Arrays.fill(stack, src.stackSize, stack.length, Type.TOP_TYPE);
             stackSize = src.stackSize;
             checkStack(src.stackSize - 1);
             if (src.stackSize > 0) System.arraycopy(src.stack, 0, stack, 0, src.stackSize);
+            unsetFieldsSize = src.unsetFieldsSize;
+            unsetFields = UnsetField.copyArray(src.unsetFields, src.unsetFieldsSize);
             flags = src.flags;
             localsChanged = true;
         }
 
         void checkAssignableTo(Frame target) {
             int localsSize = this.localsSize;
             int stackSize = this.stackSize;
+            int myUnsetFieldsSize = this.unsetFieldsSize;
             if (target.flags == -1) {
                 target.locals = locals == null ? null : locals.clone();
                 target.localsSize = localsSize;
                 if (stackSize > 0) {
                     target.stack = stack.clone();
                     target.stackSize = stackSize;
                 }
+                target.unsetFields = UnsetField.copyArray(this.unsetFields, myUnsetFieldsSize);
+                target.unsetFieldsSize = myUnsetFieldsSize;
                 target.flags = flags;
                 target.dirty = true;
             } else {
                 if (target.localsSize > localsSize) {
                     target.localsSize = localsSize;
@@ -1139,10 +1193,13 @@
                 for (int i = 0; i < target.stackSize; i++) {
                     if (merge(stack[i], target.stack, i, target) == Type.TOP_TYPE) {
                         throw generatorError("Stack content mismatch");
                     }
                 }
+                if (myUnsetFieldsSize != 0) {
+                    mergeUnsetFields(target);
+                }
             }
         }
 
         private Type getLocalRawInternal(int index) {
             checkLocal(index);
@@ -1195,10 +1252,55 @@
                 target.dirty = true;
             }
             return newTo;
         }
 
+        // Merge this frame's unset fields into the target frame
+        private void mergeUnsetFields(Frame target) {
+            int myUnsetSize = unsetFieldsSize;
+            int targetUnsetSize = target.unsetFieldsSize;
+            var myUnsets = unsetFields;
+            var targetUnsets = target.unsetFields;
+            if (!UnsetField.mismatches(myUnsets, myUnsetSize, targetUnsets, targetUnsetSize)) {
+                return; // no merge
+            }
+            // merge sort
+            var merged = new UnsetField[StackMapGenerator.this.strictFieldsToPut.length];
+            int mergedSize = 0;
+            int i = 0;
+            int j = 0;
+            while (i < myUnsetSize && j < targetUnsetSize) {
+                var myCandidate = myUnsets[i];
+                var targetCandidate = targetUnsets[j];
+                var cmp = myCandidate.compareTo(targetCandidate);
+                if (cmp == 0) {
+                    merged[mergedSize++] = myCandidate;
+                    i++;
+                    j++;
+                } else if (cmp < 0) {
+                    merged[mergedSize++] = myCandidate;
+                    i++;
+                } else {
+                    merged[mergedSize++] = targetCandidate;
+                    j++;
+                }
+            }
+            if (i < myUnsetSize) {
+                int len = myUnsetSize - i;
+                System.arraycopy(myUnsets, i, merged, mergedSize, len);
+                mergedSize += len;
+            } else if (j < targetUnsetSize) {
+                int len = targetUnsetSize - j;
+                System.arraycopy(targetUnsets, j, merged, mergedSize, len);
+                mergedSize += len;
+            }
+
+            target.unsetFieldsSize = mergedSize;
+            target.unsetFields = merged;
+            target.dirty = true;
+        }
+
         private static int trimAndCompress(Type[] types, int count) {
             while (count > 0 && types[count - 1] == Type.TOP_TYPE) count--;
             int compressed = 0;
             for (int i = 0; i < count; i++) {
                 if (!types[i].isCategory2_2nd()) {
@@ -1219,11 +1321,22 @@
         private static boolean equals(Type[] l1, Type[] l2, int commonSize) {
             if (l1 == null || l2 == null) return commonSize == 0;
             return Arrays.equals(l1, 0, commonSize, l2, 0, commonSize);
         }
 
-        void writeTo(BufWriterImpl out, Frame prevFrame, ConstantPoolBuilder cp) {
+        int writeTo(BufWriterImpl out, Frame prevFrame, ConstantPoolBuilder cp) {
+            int extraFrames = 0;
+            if (UnsetField.mismatches(unsetFields, unsetFieldsSize, prevFrame.unsetFields, prevFrame.unsetFieldsSize)) {
+                // Emit unset_fields frame
+                out.writeU1U2(StackMapDecoder.ASSERT_UNSET_FIELDS, unsetFieldsSize);
+                var array = unsetFields;
+                for (int i = 0; i < unsetFieldsSize; i++) {
+                    var f = array[i];
+                    out.writeIndex(cp.nameAndTypeEntry(f.name(), f.type()));
+                }
+                extraFrames++;
+            }
             int localsSize = this.localsSize;
             int stackSize = this.stackSize;
             int offsetDelta = offset - prevFrame.offset - 1;
             if (stackSize == 0) {
                 int commonLocalsSize = localsSize > prevFrame.localsSize ? prevFrame.localsSize : localsSize;
@@ -1233,26 +1346,27 @@
                         out.writeU1(offsetDelta);
                     } else {   //chop, same extended or append frame
                         out.writeU1U2(251 + diffLocalsSize, offsetDelta);
                         for (int i=commonLocalsSize; i<localsSize; i++) locals[i].writeTo(out, cp);
                     }
-                    return;
+                    return extraFrames;
                 }
             } else if (stackSize == 1 && localsSize == prevFrame.localsSize && equals(locals, prevFrame.locals, localsSize)) {
                 if (offsetDelta < 64) {  //same locals 1 stack item frame
                     out.writeU1(64 + offsetDelta);
                 } else {  //same locals 1 stack item extended frame
                     out.writeU1U2(247, offsetDelta);
                 }
                 stack[0].writeTo(out, cp);
-                return;
+                return extraFrames;
             }
             //full frame
             out.writeU1U2U2(255, offsetDelta, localsSize);
             for (int i=0; i<localsSize; i++) locals[i].writeTo(out, cp);
             out.writeU2(stackSize);
             for (int i=0; i<stackSize; i++) stack[i].writeTo(out, cp);
+            return extraFrames;
         }
     }
 
     private static record Type(int tag, ClassDesc sym, int bci) {
 
