diff a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CSupport.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CSupport.java
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CSupport.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CSupport.java
@@ -171,18 +171,32 @@
         }
 
         /**
          * Constructs a new {@code VaList} using a builder (see {@link Builder}).
          *
+         * Note that when there are no arguments added to the created va list,
+         * this method will return the same as {@linkplain #empty()}.
+         *
          * @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the contents
          *                of the underlying C {@code va_list}.
          * @return a new {@code VaList} instance backed by a fresh C {@code va_list}.
          */
         static VaList make(Consumer<VaList.Builder> actions) {
             return SharedUtils.newVaList(actions);
         }
 
+        /**
+         * Returns an empty C {@code va_list} constant.
+         *
+         * The returned {@code VaList} can not be closed.
+         *
+         * @return a {@code VaList} modelling an empty C {@code va_list}.
+         */
+        static VaList empty() {
+            return SharedUtils.emptyVaList();
+        }
+
         /**
          * A builder interface used to construct a C {@code va_list}.
          */
         interface Builder {
 
diff a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java
@@ -35,10 +35,11 @@
 import jdk.incubator.foreign.SequenceLayout;
 import jdk.incubator.foreign.ValueLayout;
 import jdk.internal.foreign.MemoryAddressImpl;
 import jdk.internal.foreign.Utils;
 import jdk.internal.foreign.abi.aarch64.AArch64Linker;
+import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -271,10 +272,48 @@
             case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform");
             default -> throw new IllegalStateException("Unknown linker name: " + name);
         };
     }
 
+    public static VaList emptyVaList() {
+        String name = CSupport.getSystemLinker().name();
+        return switch(name) {
+            case Win64.NAME -> Windowsx64Linker.emptyVaList();
+            case SysV.NAME -> SysVx64Linker.emptyVaList();
+            case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform");
+            default -> throw new IllegalStateException("Unknown linker name: " + name);
+        };
+    }
+
+    public static MethodType convertVaListCarriers(MethodType mt, Class<?> carrier) {
+        Class<?>[] params = new Class<?>[mt.parameterCount()];
+        for (int i = 0; i < params.length; i++) {
+            Class<?> pType = mt.parameterType(i);
+            params[i] = ((pType == VaList.class) ? carrier : pType);
+        }
+        return methodType(mt.returnType(), params);
+    }
+
+    public static MethodHandle unboxVaLists(MethodType type, MethodHandle handle, MethodHandle unboxer) {
+        for (int i = 0; i < type.parameterCount(); i++) {
+            if (type.parameterType(i) == VaList.class) {
+               handle = MethodHandles.filterArguments(handle, i, unboxer);
+            }
+        }
+        return handle;
+    }
+
+    public static MethodHandle boxVaLists(MethodHandle handle, MethodHandle boxer) {
+        MethodType type = handle.type();
+        for (int i = 0; i < type.parameterCount(); i++) {
+            if (type.parameterType(i) == VaList.class) {
+               handle = MethodHandles.filterArguments(handle, i, boxer);
+            }
+        }
+        return handle;
+    }
+
     public static class SimpleVaArg {
         public final Class<?> carrier;
         public final MemoryLayout layout;
         public final Object value;
 
@@ -288,6 +327,69 @@
             return carrier == MemoryAddress.class
                 ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize())))
                 : layout.varHandle(carrier);
         }
     }
+
+    public static class EmptyVaList implements CSupport.VaList {
+
+        private final MemoryAddress address;
+
+        public EmptyVaList(MemoryAddress address) {
+            this.address = address;
+        }
+
+        private static UnsupportedOperationException uoe() {
+            return new UnsupportedOperationException("Empty VaList");
+        }
+
+        @Override
+        public int vargAsInt(MemoryLayout layout) {
+            throw uoe();
+        }
+
+        @Override
+        public long vargAsLong(MemoryLayout layout) {
+            throw uoe();
+        }
+
+        @Override
+        public double vargAsDouble(MemoryLayout layout) {
+            throw uoe();
+        }
+
+        @Override
+        public MemoryAddress vargAsAddress(MemoryLayout layout) {
+            throw uoe();
+        }
+
+        @Override
+        public MemorySegment vargAsSegment(MemoryLayout layout) {
+            throw uoe();
+        }
+
+        @Override
+        public void skip(MemoryLayout... layouts) {
+            throw uoe();
+        }
+
+        @Override
+        public boolean isAlive() {
+            return true;
+        }
+
+        @Override
+        public void close() {
+            throw uoe();
+        }
+
+        @Override
+        public VaList copy() {
+            return this;
+        }
+
+        @Override
+        public MemoryAddress address() {
+            return address;
+        }
+    }
 }
diff a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java
@@ -29,14 +29,17 @@
 import jdk.incubator.foreign.GroupLayout;
 import jdk.incubator.foreign.MemoryAddress;
 import jdk.incubator.foreign.MemoryHandles;
 import jdk.incubator.foreign.MemoryLayout;
 import jdk.incubator.foreign.MemorySegment;
+import jdk.internal.foreign.NativeMemorySegmentImpl;
 import jdk.internal.foreign.Utils;
 import jdk.internal.foreign.abi.SharedUtils;
+import jdk.internal.misc.Unsafe;
 
 import java.lang.invoke.VarHandle;
+import java.lang.ref.Cleaner;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.List;
 
 import static jdk.incubator.foreign.CSupport.SysV;
@@ -46,10 +49,12 @@
 import static jdk.internal.foreign.abi.SharedUtils.checkCompatibleType;
 import static jdk.internal.foreign.abi.SharedUtils.vhPrimitiveOrAddress;
 
 // See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists"
 public class SysVVaList implements VaList {
+    private static final Unsafe U = Unsafe.getUnsafe();
+
     static final Class<?> CARRIER = MemoryAddress.class;
 
 //    struct typedef __va_list_tag __va_list_tag {
 //        unsigned int               gp_offset;            /*     0     4 */
 //        unsigned int               fp_offset;            /*     4     4 */
@@ -109,20 +114,40 @@
     private static final VarHandle VH_overflow_arg_area
         = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("overflow_arg_area")));
     private static final VarHandle VH_reg_save_area
         = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("reg_save_area")));
 
+    private static final Cleaner cleaner = Cleaner.create();
+    private static final CSupport.VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
+
     private final MemorySegment segment;
     private final List<MemorySegment> slices = new ArrayList<>();
     private final MemorySegment regSaveArea;
 
     SysVVaList(MemorySegment segment) {
         this.segment = segment;
         regSaveArea = regSaveArea();
         slices.add(regSaveArea);
     }
 
+    private static MemoryAddress emptyListAddress() {
+        long ptr = U.allocateMemory(LAYOUT.byteSize());
+        MemorySegment ms = NativeMemorySegmentImpl.makeNativeSegmentUnchecked(
+                MemoryAddress.ofLong(ptr), LAYOUT.byteSize(), null, () -> U.freeMemory(ptr), null);
+        cleaner.register(SysVVaList.class, ms::close);
+        MemoryAddress base = ms.baseAddress();
+        VH_gp_offset.set(base, MAX_GP_OFFSET);
+        VH_fp_offset.set(base, MAX_FP_OFFSET);
+        VH_overflow_arg_area.set(base, MemoryAddress.NULL);
+        VH_reg_save_area.set(base, MemoryAddress.NULL);
+        return ms.withAccessModes(0).baseAddress();
+    }
+
+    public static CSupport.VaList empty() {
+        return EMPTY;
+    }
+
     private int currentGPOffset() {
         return (int) VH_gp_offset.get(segment.baseAddress());
     }
 
     private void currentGPOffset(int i) {
@@ -267,14 +292,10 @@
 
     public static VaList ofAddress(MemoryAddress ma) {
         return new SysVVaList(MemorySegment.ofNativeRestricted(ma, LAYOUT.byteSize(), Thread.currentThread(), null, null));
     }
 
-    MemorySegment getSegment() {
-        return segment;
-    }
-
     @Override
     public boolean isAlive() {
         return segment.isAlive();
     }
 
@@ -381,11 +402,19 @@
                 }
             }
             return this;
         }
 
-        public SysVVaList build() {
+        private boolean isEmpty() {
+            return currentGPOffset == 0 && currentFPOffset == FP_OFFSET && stackArgs.isEmpty();
+        }
+
+        public VaList build() {
+            if (isEmpty()) {
+                return EMPTY;
+            }
+
             MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT.byteSize());
             SysVVaList res = new SysVVaList(vaListSegment);
             MemoryAddress stackArgsPtr = MemoryAddress.NULL;
             if (!stackArgs.isEmpty()) {
                 long stackArgsSize = stackArgs.stream().reduce(0L, (acc, e) -> acc + e.layout.byteSize(), Long::sum);
diff a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java
@@ -28,10 +28,11 @@
 import jdk.incubator.foreign.ForeignLinker;
 import jdk.incubator.foreign.FunctionDescriptor;
 import jdk.incubator.foreign.MemoryAddress;
 import jdk.incubator.foreign.MemoryLayout;
 import jdk.incubator.foreign.MemorySegment;
+import jdk.internal.foreign.abi.SharedUtils;
 import jdk.internal.foreign.abi.UpcallStubs;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -58,13 +59,13 @@
     private static final MethodHandle MH_boxVaList;
 
     static {
         try {
             MethodHandles.Lookup lookup = MethodHandles.lookup();
-            MH_unboxVaList = lookup.findStatic(SysVx64Linker.class, "unboxVaList",
-                MethodType.methodType(MemoryAddress.class, CSupport.VaList.class));
-            MH_boxVaList = lookup.findStatic(SysVx64Linker.class, "boxVaList",
+            MH_unboxVaList = lookup.findVirtual(CSupport.VaList.class, "address",
+                MethodType.methodType(MemoryAddress.class));
+            MH_boxVaList = lookup.findStatic(SysVx64Linker.class, "newVaListOfAddress",
                 MethodType.methodType(VaList.class, MemoryAddress.class));
         } catch (ReflectiveOperationException e) {
             throw new ExceptionInInitializerError(e);
         }
     }
@@ -80,49 +81,21 @@
         SysVVaList.Builder builder = SysVVaList.builder();
         actions.accept(builder);
         return builder.build();
     }
 
-    private static MethodType convertVaListCarriers(MethodType mt) {
-        Class<?>[] params = new Class<?>[mt.parameterCount()];
-        for (int i = 0; i < params.length; i++) {
-            Class<?> pType = mt.parameterType(i);
-            params[i] = ((pType == CSupport.VaList.class) ? SysVVaList.CARRIER : pType);
-        }
-        return MethodType.methodType(mt.returnType(), params);
-    }
-
-    private static MethodHandle unxboxVaLists(MethodType type, MethodHandle handle) {
-        for (int i = 0; i < type.parameterCount(); i++) {
-            if (type.parameterType(i) == VaList.class) {
-               handle = MethodHandles.filterArguments(handle, i, MH_unboxVaList);
-            }
-        }
-        return handle;
-    }
-
     @Override
     public MethodHandle downcallHandle(MemoryAddress symbol, MethodType type, FunctionDescriptor function) {
-        MethodType llMt = convertVaListCarriers(type);
+        MethodType llMt = SharedUtils.convertVaListCarriers(type, SysVVaList.CARRIER);
         MethodHandle handle = CallArranger.arrangeDowncall(symbol, llMt, function);
-        handle = unxboxVaLists(type, handle);
-        return handle;
-    }
-
-    private static MethodHandle boxVaLists(MethodHandle handle) {
-        MethodType type = handle.type();
-        for (int i = 0; i < type.parameterCount(); i++) {
-            if (type.parameterType(i) == VaList.class) {
-               handle = MethodHandles.filterArguments(handle, i, MH_boxVaList);
-            }
-        }
+        handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList);
         return handle;
     }
 
     @Override
     public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function) {
-        target = boxVaLists(target);
+        target = SharedUtils.boxVaLists(target, MH_boxVaList);
         return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function));
     }
 
     @Override
     public String name() {
@@ -141,17 +114,13 @@
             case POINTER -> ArgumentClassImpl.POINTER;
             default -> null;
         });
     }
 
-    private static MemoryAddress unboxVaList(CSupport.VaList list) {
-        return ((SysVVaList) list).getSegment().baseAddress();
-    }
-
-    private static CSupport.VaList boxVaList(MemoryAddress ma) {
+    public static VaList newVaListOfAddress(MemoryAddress ma) {
         return SysVVaList.ofAddress(ma);
     }
 
-    public static VaList newVaListOfAddress(MemoryAddress ma) {
-        return SysVVaList.ofAddress(ma);
+    public static VaList emptyVaList() {
+        return SysVVaList.empty();
     }
 }
diff a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java
@@ -24,10 +24,11 @@
  *
  */
 package jdk.internal.foreign.abi.x64.windows;
 
 import jdk.incubator.foreign.CSupport;
+import jdk.incubator.foreign.CSupport.VaList;
 import jdk.incubator.foreign.MemoryAddress;
 import jdk.incubator.foreign.MemoryHandles;
 import jdk.incubator.foreign.MemoryLayout;
 import jdk.incubator.foreign.MemorySegment;
 import jdk.internal.foreign.abi.SharedUtils;
@@ -55,15 +56,17 @@
 //    #define __crt_va_arg(ap, t)                                               \
 //        ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \
 //            ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64))             \
 //            :  *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
 //
-class WinVaList implements CSupport.VaList {
+class WinVaList implements VaList {
     public static final Class<?> CARRIER = MemoryAddress.class;
     private static final long VA_SLOT_SIZE_BYTES = 8;
     private static final VarHandle VH_address = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class));
 
+    private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL);
+
     private final MemorySegment segment;
     private MemoryAddress ptr;
     private final List<MemorySegment> copies;
 
     WinVaList(MemorySegment segment) {
@@ -74,10 +77,14 @@
         this.segment = segment;
         this.ptr = segment.baseAddress();
         this.copies = copies;
     }
 
+    public static final VaList empty() {
+        return EMPTY;
+    }
+
     @Override
     public int vargAsInt(MemoryLayout layout) {
         return (int) read(int.class, layout);
     }
 
@@ -142,22 +149,18 @@
 
     static Builder builder() {
         return new Builder();
     }
 
-    MemorySegment getSegment() {
-        return segment;
-    }
-
     @Override
     public void close() {
         segment.close();
         copies.forEach(MemorySegment::close);
     }
 
     @Override
-    public CSupport.VaList copy() {
+    public VaList copy() {
         return WinVaList.ofAddress(ptr);
     }
 
     @Override
     public MemoryAddress address() {
@@ -167,11 +170,11 @@
     @Override
     public boolean isAlive() {
         return segment.isAlive();
     }
 
-    static class Builder implements CSupport.VaList.Builder {
+    static class Builder implements VaList.Builder {
 
         private final List<SimpleVaArg> args = new ArrayList<>();
 
         private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
             SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE);
@@ -202,11 +205,14 @@
         @Override
         public Builder vargFromSegment(MemoryLayout layout, MemorySegment value) {
             return arg(MemorySegment.class, layout, value);
         }
 
-        public WinVaList build() {
+        public VaList build() {
+            if (args.isEmpty()) {
+                return EMPTY;
+            }
             MemorySegment ms = MemorySegment.allocateNative(VA_SLOT_SIZE_BYTES * args.size());
             List<MemorySegment> copies = new ArrayList<>();
 
             MemoryAddress addr = ms.baseAddress();
             for (SimpleVaArg arg : args) {
diff a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java
@@ -28,10 +28,11 @@
 import jdk.incubator.foreign.ForeignLinker;
 import jdk.incubator.foreign.FunctionDescriptor;
 import jdk.incubator.foreign.MemoryAddress;
 import jdk.incubator.foreign.MemoryLayout;
 import jdk.incubator.foreign.MemorySegment;
+import jdk.internal.foreign.abi.SharedUtils;
 import jdk.internal.foreign.abi.UpcallStubs;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -59,13 +60,13 @@
     private static final MethodHandle MH_boxVaList;
 
     static {
         try {
             MethodHandles.Lookup lookup = MethodHandles.lookup();
-            MH_unboxVaList = lookup.findStatic(Windowsx64Linker.class, "unboxVaList",
-                MethodType.methodType(MemoryAddress.class, CSupport.VaList.class));
-            MH_boxVaList = lookup.findStatic(Windowsx64Linker.class, "boxVaList",
+            MH_unboxVaList = lookup.findVirtual(CSupport.VaList.class, "address",
+                MethodType.methodType(MemoryAddress.class));
+            MH_boxVaList = lookup.findStatic(Windowsx64Linker.class, "newVaListOfAddress",
                 MethodType.methodType(VaList.class, MemoryAddress.class));
         } catch (ReflectiveOperationException e) {
             throw new ExceptionInInitializerError(e);
         }
     }
@@ -81,49 +82,21 @@
         WinVaList.Builder builder = WinVaList.builder();
         actions.accept(builder);
         return builder.build();
     }
 
-    private static MethodType convertVaListCarriers(MethodType mt) {
-        Class<?>[] params = new Class<?>[mt.parameterCount()];
-        for (int i = 0; i < params.length; i++) {
-            Class<?> pType = mt.parameterType(i);
-            params[i] = ((pType == CSupport.VaList.class) ? WinVaList.CARRIER : pType);
-        }
-        return MethodType.methodType(mt.returnType(), params);
-    }
-
-    private static MethodHandle unxboxVaLists(MethodType type, MethodHandle handle) {
-        for (int i = 0; i < type.parameterCount(); i++) {
-            if (type.parameterType(i) == VaList.class) {
-               handle = MethodHandles.filterArguments(handle, i, MH_unboxVaList);
-            }
-        }
-        return handle;
-    }
-
     @Override
     public MethodHandle downcallHandle(MemoryAddress symbol, MethodType type, FunctionDescriptor function) {
-        MethodType llMt = convertVaListCarriers(type);
+        MethodType llMt = SharedUtils.convertVaListCarriers(type, WinVaList.CARRIER);
         MethodHandle handle = CallArranger.arrangeDowncall(symbol, llMt, function);
-        handle = unxboxVaLists(type, handle);
-        return handle;
-    }
-
-    private static MethodHandle boxVaLists(MethodHandle handle) {
-        MethodType type = handle.type();
-        for (int i = 0; i < type.parameterCount(); i++) {
-            if (type.parameterType(i) == VaList.class) {
-               handle = MethodHandles.filterArguments(handle, i, MH_boxVaList);
-            }
-        }
+        handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList);
         return handle;
     }
 
     @Override
     public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function) {
-        target = boxVaLists(target);
+        target = SharedUtils.boxVaLists(target, MH_boxVaList);
         return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function));
     }
 
     @Override
     public String name() {
@@ -132,18 +105,13 @@
 
     static Win64.ArgumentClass argumentClassFor(MemoryLayout layout) {
         return (Win64.ArgumentClass)layout.attribute(Win64.CLASS_ATTRIBUTE_NAME).get();
     }
 
-    private static MemoryAddress unboxVaList(CSupport.VaList list) {
-        return ((WinVaList) list).getSegment().baseAddress();
-    }
-
-    private static CSupport.VaList boxVaList(MemoryAddress ma) {
-        return WinVaList.ofAddress(ma);
-    }
-
     public static VaList newVaListOfAddress(MemoryAddress ma) {
         return WinVaList.ofAddress(ma);
     }
 
+    public static VaList emptyVaList() {
+        return WinVaList.empty();
+    }
 }
diff a/test/jdk/java/foreign/StdLibTest.java b/test/jdk/java/foreign/StdLibTest.java
--- a/test/jdk/java/foreign/StdLibTest.java
+++ b/test/jdk/java/foreign/StdLibTest.java
@@ -356,12 +356,19 @@
             }
         }
 
         int vprintf(String format, List<PrintfArg> args) throws Throwable {
             try (MemorySegment formatStr = toCString(format)) {
-                return (int)vprintf.invokeExact(formatStr.baseAddress(),
-                        VaList.make(b -> args.forEach(a -> a.accept(b))));
+                VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b)));
+                int result = (int)vprintf.invokeExact(formatStr.baseAddress(), vaList);
+                try {
+                    vaList.close();
+                }
+                catch (UnsupportedOperationException e) {
+                    assertEquals(e.getMessage(), "Empty VaList");
+                }
+                return result;
             }
         }
 
         private MethodHandle specializedPrintf(List<PrintfArg> args) {
             //method type
diff a/test/jdk/java/foreign/valist/VaListTest.java b/test/jdk/java/foreign/valist/VaListTest.java
--- a/test/jdk/java/foreign/valist/VaListTest.java
+++ b/test/jdk/java/foreign/valist/VaListTest.java
@@ -212,10 +212,24 @@
         try (MemorySegment stub = abi.upcallStub(callback, desc)) {
             target.invokeExact(stub.baseAddress());
         }
     }
 
+    @Test(expectedExceptions = UnsupportedOperationException.class,
+          expectedExceptionsMessageRegExp = ".*Empty VaList.*")
+    public void testEmptyNotCloseable() {
+        VaList list = VaList.empty();
+        list.close();
+    }
+
+    @Test(expectedExceptions = UnsupportedOperationException.class,
+          expectedExceptionsMessageRegExp = ".*Empty VaList.*")
+    public void testEmptyVaListFromBuilderNotCloseable() {
+        VaList list = VaList.make(b -> {});
+        list.close();
+    }
+
     @DataProvider
     public static Object[][] upcalls() {
         return new Object[][]{
             { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
                 try (MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT)) {
diff a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java
--- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java
+++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java
@@ -77,11 +77,11 @@
                                 1, 2D, 3L);
     }
 
     @Benchmark
     public void vaList() throws Throwable {
-        try (CSupport.VaList vaList = CSupport.newVaList(b ->
+        try (CSupport.VaList vaList = CSupport.VaList.make(b ->
             b.vargFromInt(C_INT, 1)
              .vargFromDouble(C_DOUBLE, 2D)
              .vargFromLong(C_LONGLONG, 3L)
         )) {
             MH_vaList.invokeExact(3,
