diff a/src/java.base/share/classes/java/lang/reflect/Proxy.java b/src/java.base/share/classes/java/lang/reflect/Proxy.java
--- a/src/java.base/share/classes/java/lang/reflect/Proxy.java
+++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java
@@ -31,10 +31,11 @@
 import java.lang.invoke.WrongMethodTypeException;
 import java.lang.module.ModuleDescriptor;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -50,10 +51,11 @@
 
 import jdk.internal.access.JavaLangAccess;
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.module.Modules;
 import jdk.internal.misc.VM;
+import jdk.internal.misc.CDS;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.loader.ClassLoaderValue;
 import jdk.internal.vm.annotation.Stable;
 import sun.reflect.misc.ReflectUtil;
@@ -438,10 +440,17 @@
                 (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
             );
         }
     }
 
+    /**
+     * Called from VM native code during dump time.
+     */
+    private static void initCacheForCDS(ClassLoader platformLoader, ClassLoader appLoader) {
+        ProxyBuilder.initCacheForCDS(platformLoader, appLoader);
+    }
+
     /*
      * Check permissions required to create a Proxy class.
      *
      * To define a proxy class, it performs the access checks as in
      * Class.forName (VM will invoke ClassLoader.checkPackageAccess):
@@ -481,21 +490,34 @@
      * in which the proxy class will be defined.
      */
     private static final class ProxyBuilder {
         private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 
+        private static String makeProxyClassNamePrefix() {
+            // Allow unique proxy names to be used across CDS dump time and app run time.
+            if (CDS.isDumpingArchive()) {
+                if (CDS.isUsingArchive()) {
+                    return "$Proxy0100"; // CDS dynamic dump
+                } else {
+                    return "$Proxy0010"; // CDS static dump
+                }
+            } else {
+                return "$Proxy";
+            }
+        }
+
         // prefix for all proxy class names
-        private static final String proxyClassNamePrefix = "$Proxy";
+        private static final String proxyClassNamePrefix = makeProxyClassNamePrefix();
 
         // next number to use for generation of unique proxy class names
         private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
         // a reverse cache of defined proxy classes
         private static final ClassLoaderValue<Boolean> reverseProxyCache =
             new ClassLoaderValue<>();
 
-        private record ProxyClassContext(Module module, String packageName, int accessFlags) {
+        private record ProxyClassContext(Module module, String packageName, int accessFlags, boolean isDynamicModule) {
             private ProxyClassContext {
                 if (module.isNamed()) {
                     if (packageName.isEmpty()) {
                         // Per JLS 7.4.2, unnamed package can only exist in unnamed modules.
                         // This means a package-private superinterface exist in the unnamed
@@ -526,23 +548,37 @@
 
         private static Class<?> defineProxyClass(ProxyClassContext context, List<Class<?>> interfaces) {
             /*
              * Choose a name for the proxy class to generate.
              */
+            String packagePrefix = context.packageName().isEmpty()
+                                    ? proxyClassNamePrefix
+                                    : context.packageName() + "." + proxyClassNamePrefix;
+            ClassLoader loader = getLoader(context.module());
+            int accessFlags = context.accessFlags() | Modifier.FINAL;
+
+            if (archivedData != null) {
+                Class<?> pc = archivedData.getArchivedProxyClass(loader, packagePrefix, interfaces);
+                if (pc != null) {
+                    reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
+                    return pc;
+                }
+            }
+
             long num = nextUniqueNumber.getAndIncrement();
-            String proxyName = context.packageName().isEmpty()
-                                    ? proxyClassNamePrefix + num
-                                    : context.packageName() + "." + proxyClassNamePrefix + num;
+            String proxyName = packagePrefix + num;
 
-            ClassLoader loader = getLoader(context.module());
             trace(proxyName, context.module(), loader, interfaces);
 
+            if (CDS.isLoggingDynamicProxies() && context.isDynamicModule()) {
+                CDS.logDynamicProxy(loader, proxyName, interfaces.toArray(new Class<?>[0]), accessFlags);
+            }
+
             /*
              * Generate the specified proxy class.
              */
-            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces,
-                                                                      context.accessFlags() | Modifier.FINAL);
+            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
             try {
                 Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                               null, "__dynamic_proxy__");
                 reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                 return pc;
@@ -556,10 +592,27 @@
                  */
                 throw new IllegalArgumentException(e.toString());
             }
         }
 
+        /**
+         * Called from VM native code to define a proxy class to be stored in archivedData.
+         */
+        private static Class<?> defineProxyClassForCDS(ClassLoader loader, String proxyName, Class<?>[] interfaces,
+                                                       int accessFlags) {
+            ArrayList<Class<?>> list = new ArrayList<>();
+            for (Object o : interfaces) {
+                list.add((Class<?>)o);
+            }
+
+            ProxyBuilder builder = new ProxyBuilder(loader, list);
+            Constructor<?> cons = builder.build();
+            Class<?> proxyClass = cons.getDeclaringClass();
+            archivedData.putArchivedProxyClass(loader, proxyName, list, proxyClass);
+            return proxyClass;
+        }
+
         /**
          * Test if given class is a class defined by
          * {@link #defineProxyClass(ProxyClassContext, List)}
          */
         static boolean isProxyClass(Class<?> c) {
@@ -829,11 +882,11 @@
                 // opens the package of the non-public proxy class for java.base to access
                 if (targetModule.isNamed()) {
                     Modules.addOpens(targetModule, targetPackageName, Proxy.class.getModule());
                 }
                 // return the module of the package-private interface
-                return new ProxyClassContext(targetModule, targetPackageName, 0);
+                return new ProxyClassContext(targetModule, targetPackageName, 0, false);
             }
 
             // All proxy interfaces are public.  So maps to a dynamic proxy module
             // and add reads edge and qualified exports, if necessary
             Module targetModule = getDynamicModule(loader);
@@ -846,11 +899,11 @@
                 ensureAccess(targetModule, c);
             }
 
             var pkgName = nonExported ? PROXY_PACKAGE_PREFIX + '.' + targetModule.getName()
                                       : targetModule.getName();
-            return new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC);
+            return new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC, true);
         }
 
         /*
          * Ensure the given module can access the given class.
          */
@@ -903,26 +956,174 @@
          * Each class loader will have one dynamic module.
          */
         private static Module getDynamicModule(ClassLoader loader) {
             return dynProxyModules.computeIfAbsent(loader, (ld, clv) -> {
                 // create a dynamic module and setup module access
-                String mn = "jdk.proxy" + counter.incrementAndGet();
+                int num = counter.incrementAndGet();
+                String mn = "jdk.proxy" + num;
                 String pn = PROXY_PACKAGE_PREFIX + "." + mn;
                 ModuleDescriptor descriptor =
                         ModuleDescriptor.newModule(mn, Set.of(SYNTHETIC))
                                         .packages(Set.of(pn, mn))
                                         .exports(mn)
                                         .build();
                 Module m = Modules.defineModule(ld, descriptor, null);
-                Modules.addReads(m, Proxy.class.getModule());
-                Modules.addExports(m, mn);
-                // java.base to create proxy instance and access its Lookup instance
-                Modules.addOpens(m, pn, Proxy.class.getModule());
-                Modules.addOpens(m, mn, Proxy.class.getModule());
+                openDynamicModule(m);
+
+                if (CDS.isDumpingHeap() && archivedData != null) {
+                    archivedData.recordModule(loader, m, num);
+                }
                 return m;
             });
         }
+
+        private static void openDynamicModule(Module m) {
+            String mn = m.getName();
+            String pn = PROXY_PACKAGE_PREFIX + "." + mn;
+            Modules.addReads(m, Proxy.class.getModule());
+            Modules.addExports(m, mn);
+            // java.base to create proxy instance and access its Lookup instance
+            Modules.addOpens(m, pn, Proxy.class.getModule());
+            Modules.addOpens(m, mn, Proxy.class.getModule());
+        }
+
+        static class ArchivedData {
+            static class InterfacesKey {
+                Class<?>[] intfsArray;
+                InterfacesKey(List<Class<?>> intfs) {
+                    intfsArray = new Class<?>[intfs.size()];
+                    for (int i = 0; i < intfs.size(); i++) {
+                        intfsArray[i] = intfs.get(i);
+                    }
+                }
+                @Override
+                public int hashCode() {
+                    return Arrays.hashCode(intfsArray);
+                }
+                @Override
+                public boolean equals(Object other) {
+                    if (other instanceof InterfacesKey) {
+                        InterfacesKey o = (InterfacesKey)other;
+                        int len = intfsArray.length;
+                        if (len != o.intfsArray.length) {
+                            return false;
+                        }
+                        Class<?>[] oa = o.intfsArray;
+                        for (int i = 0; i < len; i++) {
+                            if (intfsArray[i] != oa[i]) {
+                                return false;
+                            }
+                        }
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+
+            ClassLoader platformLoader;
+            ClassLoader appLoader;
+            HashMap<InterfacesKey,Class<?>> bootCache = new HashMap<>();
+            HashMap<InterfacesKey,Class<?>> platformCache = new HashMap<>();
+            HashMap<InterfacesKey,Class<?>> appCache = new HashMap<>();
+
+            Module bootModule;
+            Module platformModule;
+            Module appModule;
+            int maxNum;
+
+            ArchivedData(ClassLoader plat, ClassLoader app) {
+                platformLoader = plat;
+                appLoader = app;
+            }
+
+            HashMap<InterfacesKey,Class<?>> cacheForLoader(ClassLoader loader) {
+                if (loader == null) {
+                    return bootCache;
+                } else if (loader == platformLoader) {
+                    return platformCache;
+                } else if (loader == appLoader) {
+                    return appCache;
+                } else {
+                    return null;
+                }
+            }
+
+            void recordModule(ClassLoader loader, Module m, int num) {
+                if (loader == null) {
+                    bootModule = m;
+                } else if (loader == platformLoader) {
+                    platformModule = m;
+                } else if (loader == appLoader) {
+                    appModule = m;
+                } else {
+                    throw new UnsupportedOperationException("Class loader " + loader + " is not supported");
+                }
+                if (maxNum < num) {
+                    maxNum = num;
+                }
+            }
+
+            void restore() {
+                // The info for addReads/addExports/addOpens are maintained solely inside the VM.
+                // CDS currently doesn't properly archive such info for the dynamically generated modules,
+                // so we have to recreate them at runtime.
+                //
+                // TODO --  consider improving CDS to archive the above info, so we can avoid calling openDynamicModule()
+                if (bootModule != null) {
+                    Module last = dynProxyModules.putIfAbsent(null, bootModule);
+                    assert last == null;
+                    openDynamicModule(bootModule);
+                }
+                if (platformModule != null) {
+                    Module last = dynProxyModules.putIfAbsent(platformLoader, platformModule);
+                    assert last == null;
+                    openDynamicModule(platformModule);
+                }
+                if (appModule != null) {
+                    Module last = dynProxyModules.putIfAbsent(appLoader, appModule);
+                    assert last == null;
+                    openDynamicModule(appModule);
+                }
+
+                while (maxNum > counter.get()) {
+                    counter.incrementAndGet();
+                }
+            }
+
+
+            Class<?> getArchivedProxyClass(ClassLoader loader, String proxyPrefix, List<Class<?>> interfaces) {
+                HashMap<InterfacesKey,Class<?>> cache = cacheForLoader(loader);
+                if (cache != null && cache.size() > 0) {
+                    InterfacesKey key = new InterfacesKey(interfaces);
+                    return cache.get(key);
+                } else {
+                    return null;
+                }
+            }
+
+            void putArchivedProxyClass(ClassLoader loader, String proxyName, List<Class<?>> interfaces, Class<?> cls) {
+                HashMap<InterfacesKey,Class<?>> cache = cacheForLoader(loader);
+                if (cache != null) {
+                    InterfacesKey key = new InterfacesKey(interfaces);
+                    cache.put(key, cls);
+                }
+            }
+        }
+
+        static ArchivedData archivedData;
+
+        static {
+            CDS.initializeFromArchive(ProxyBuilder.class);
+            if (archivedData != null) {
+                archivedData.restore();
+            }
+        }
+
+        private static void initCacheForCDS(ClassLoader platformLoader, ClassLoader appLoader) {
+            archivedData = new ArchivedData(platformLoader, appLoader);
+        }
     }
 
     /**
      * Returns a proxy instance for the specified interfaces
      * that dispatches method invocations to the specified invocation
