diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 9130018b3e3..1282b373f06 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -3363,25 +3363,23 @@ u2 ClassFileParser::parse_classfile_permitted_subclasses_attribute(const ClassFi
     cfs->guarantee_more(2, CHECK_0);  // length
     length = cfs->get_u2_fast();
   }
-  if (length < 1) {
-    classfile_parse_error("PermittedSubclasses attribute is empty in class file %s", THREAD);
-    return 0;
-  }
   const int size = length;
   Array<u2>* const permitted_subclasses = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0);
   _permitted_subclasses = permitted_subclasses;
 
-  int index = 0;
-  cfs->guarantee_more(2 * length, CHECK_0);
-  for (int n = 0; n < length; n++) {
-    const u2 class_info_index = cfs->get_u2_fast();
-    check_property(
-      valid_klass_reference_at(class_info_index),
-      "Permitted subclass class_info_index %u has bad constant type in class file %s",
-      class_info_index, CHECK_0);
-    permitted_subclasses->at_put(index++, class_info_index);
+  if (length > 0) {
+    int index = 0;
+    cfs->guarantee_more(2 * length, CHECK_0);
+    for (int n = 0; n < length; n++) {
+      const u2 class_info_index = cfs->get_u2_fast();
+      check_property(
+        valid_klass_reference_at(class_info_index),
+        "Permitted subclass class_info_index %u has bad constant type in class file %s",
+        class_info_index, CHECK_0);
+      permitted_subclasses->at_put(index++, class_info_index);
+    }
+    assert(index == size, "wrong size");
   }
-  assert(index == size, "wrong size");
 
   // Restore buffer's current position.
   cfs->set_current(current_mark);
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index e4918ec6533..0c54c2db4e0 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -741,8 +741,7 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
 
 bool InstanceKlass::is_sealed() const {
   return _permitted_subclasses != NULL &&
-         _permitted_subclasses != Universe::the_empty_short_array() &&
-         _permitted_subclasses->length() > 0;
+         _permitted_subclasses != Universe::the_empty_short_array();
 }
 
 bool InstanceKlass::should_be_initialized() const {
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 9bb29a8993c..8f9464add4f 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -2124,10 +2124,10 @@ JVM_ENTRY(jobjectArray, JVM_GetPermittedSubclasses(JNIEnv* env, jclass current))
   Klass* c = java_lang_Class::as_Klass(mirror);
   assert(c->is_instance_klass(), "must be");
   InstanceKlass* ik = InstanceKlass::cast(c);
-  {
+  if (ik->is_sealed()) {
     JvmtiVMObjectAllocEventCollector oam;
     Array<u2>* subclasses = ik->permitted_subclasses();
-    int length = subclasses == NULL ? 0 : subclasses->length();
+    int length = subclasses->length();
     objArrayOop r = oopFactory::new_objArray(SystemDictionary::Class_klass(),
                                              length, CHECK_NULL);
     objArrayHandle result(THREAD, r);
@@ -2156,6 +2156,8 @@ JVM_ENTRY(jobjectArray, JVM_GetPermittedSubclasses(JNIEnv* env, jclass current))
       return (jobjectArray)JNIHandles::make_local(THREAD, result2());
     }
     return (jobjectArray)JNIHandles::make_local(THREAD, result());
+  } else {
+    return NULL;
   }
 }
 JVM_END
diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
index 2657667bea2..51577044455 100644
--- a/src/java.base/share/classes/java/lang/Class.java
+++ b/src/java.base/share/classes/java/lang/Class.java
@@ -4390,10 +4390,13 @@ public final class Class<T> implements java.io.Serializable,
      *
      * Returns an array containing {@code Class} objects representing the
      * direct subinterfaces or subclasses permitted to extend or
-     * implement this class or interface if it is sealed. The order of such elements
-     * is unspecified. If this {@code Class} object represents a primitive type,
+     * implement this class or interface if it is sealed.  The order of such elements
+     * is unspecified. The array is empty if this sealed class or interface has no
+     * permitted subclass. If this {@code Class} object represents a primitive type,
      * {@code void}, an array type, or a class or interface that is not sealed,
-     * an empty array is returned.
+     * that is {@link #isSealed()} returns {@code false}, then this method returns {@code null}.
+     * Conversely, if {@link #isSealed()} returns {@code true}, then this method
+     * returns a non-null value.
      *
      * For each class or interface {@code C} which is recorded as a permitted
      * direct subinterface or subclass of this class or interface,
@@ -4406,7 +4409,8 @@ public final class Class<T> implements java.io.Serializable,
      * cannot be obtained, it is silently ignored, and not included in the result
      * array.
      *
-     * @return an array of {@code Class} objects of the permitted subclasses of this class or interface
+     * @return an array of {@code Class} objects of the permitted subclasses of this class or interface,
+     *         or {@code null} if this class or interface is not sealed.
      *
      * @throws SecurityException
      *         If a security manager, <i>s</i>, is present and the caller's
@@ -4423,8 +4427,8 @@ public final class Class<T> implements java.io.Serializable,
     @CallerSensitive
     public Class<?>[] getPermittedSubclasses() {
         Class<?>[] subClasses;
-        if (isArray() || isPrimitive() || (subClasses = getPermittedSubclasses0()).length == 0) {
-            return EMPTY_CLASS_ARRAY;
+        if (isArray() || isPrimitive() || (subClasses = getPermittedSubclasses0()) == null) {
+            return null;
         }
         if (subClasses.length > 0) {
             if (Arrays.stream(subClasses).anyMatch(c -> !isDirectSubType(c))) {
@@ -4469,7 +4473,9 @@ public final class Class<T> implements java.io.Serializable,
      * Returns {@code true} if and only if this {@code Class} object represents
      * a sealed class or interface. If this {@code Class} object represents a
      * primitive type, {@code void}, or an array type, this method returns
-     * {@code false}.
+     * {@code false}. A sealed class or interface has (possibly zero) permitted
+     * subclasses; {@link #getPermittedSubclasses()} returns a non-null but
+     * possibly empty value for a sealed class or interface.
      *
      * @return {@code true} if and only if this {@code Class} object represents a sealed class or interface.
      *
@@ -4483,7 +4489,7 @@ public final class Class<T> implements java.io.Serializable,
         if (isArray() || isPrimitive()) {
             return false;
         }
-        return getPermittedSubclasses().length != 0;
+        return getPermittedSubclasses() != null;
     }
 
     private native Class<?>[] getPermittedSubclasses0();
diff --git a/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclasses.jcod b/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclasses.jcod
index ba42c193518..8d4758edc96 100644
--- a/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclasses.jcod
+++ b/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclasses.jcod
@@ -164,7 +164,7 @@ class ExistingClassFile {
 } // end class ExistingClassFile
 
 // This class contains an empty PermittedSubclasses attribute.  Test that
-// this causes an exception to get thrown.
+// this does not cause an exception to get thrown.
 class NoSubclasses {
   0xCAFEBABE;
   65535; // minor version
@@ -223,17 +223,83 @@ class NoSubclasses {
   } // methods
 
   [2] { // Attributes
-    Attr(#11, 2) { // SourceFile at 0xD8
-      #12;
-    } // end SourceFile
-    ;
     Attr(#13, 2) { // PermittedSubclasses at 0xE0
       0x0000;
     } // end PermittedSubclasses
+    ;
+    Attr(#11, 2) { // SourceFile at 0xD8
+      #12;
+    } // end SourceFile
   } // Attributes
 } // end class NoSubclasses
 
 
+// This class extends NoSubClasses to show that a class with an empty
+// PermittedSubtypes attribute cannot be subclass-ed.
+class SubClass {
+  0xCAFEBABE;
+  65535; // minor version
+  60; // version
+  [13] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1     at 0x0A
+    class #4; // #2     at 0x0F
+    NameAndType #5 #6; // #3     at 0x12
+    Utf8 "NoSubclasses"; // #4     at 0x17
+    Utf8 "<init>"; // #5     at 0x26
+    Utf8 "()V"; // #6     at 0x2F
+    class #8; // #7     at 0x35
+    Utf8 "SubClass"; // #8     at 0x38
+    Utf8 "Code"; // #9     at 0x43
+    Utf8 "LineNumberTable"; // #10     at 0x4A
+    Utf8 "SourceFile"; // #11     at 0x5C
+    Utf8 "SubClass.java"; // #12     at 0x69
+  } // Constant Pool
+
+  0x0021; // access [ ACC_PUBLIC ACC_SUPER ]
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [0] { // Fields
+  } // Fields
+
+  [1] { // Methods
+    {  // method at 0x85
+      0x0001; // access
+      #5; // name_index       : <init>
+      #6; // descriptor_index : ()V
+      [1] { // Attributes
+        Attr(#9, 29) { // Code at 0x8D
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB70001B1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 6) { // LineNumberTable at 0xA4
+              [1] { // line_number_table
+                0  1; //  at 0xB0
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [1] { // Attributes
+    Attr(#11, 2) { // SourceFile at 0xB2
+      #12;
+    } // end SourceFile
+  } // Attributes
+} // end class SubClass
+
+
 
 // This class has a PermittedSubclasses attribute but an old class file version.
 // The PermittedSubclasses attribute should be ignored.
diff --git a/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclassesTest.java b/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclassesTest.java
index 67b0f0d2ec1..d77de99cf40 100644
--- a/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclassesTest.java
+++ b/test/hotspot/jtreg/runtime/sealedClasses/GetPermittedSubclassesTest.java
@@ -50,15 +50,16 @@ public class GetPermittedSubclassesTest {
 
     final class Final4 {}
 
-    public static void testSealedInfo(Class<?> c, String[] expected) {
+    public static void testSealedInfo(Class<?> c, String[] expected, boolean isSealed) {
         var permitted = c.getPermittedSubclasses();
 
-        if (permitted.length != expected.length) {
-            throw new RuntimeException(
-                "Unexpected number of permitted subclasses for: " + c.toString() + "(" + java.util.Arrays.asList(permitted));
-        }
+        if (isSealed) {
+            if (permitted.length != expected.length) {
+                throw new RuntimeException(
+                    "Unexpected number of permitted subclasses for: " + c.toString() +
+                    "(" + java.util.Arrays.asList(permitted));
+            }
 
-        if (permitted.length > 0) {
             if (!c.isSealed()) {
                 throw new RuntimeException("Expected sealed class: " + c.toString());
             }
@@ -83,63 +84,86 @@ public class GetPermittedSubclassesTest {
                 }
             }
         } else {
-            // Must not be sealed if no permitted subclasses.
-            if (c.isSealed()) {
+            // Must not be sealed.
+            if (c.isSealed() || permitted != null) {
                 throw new RuntimeException("Unexpected sealed class: " + c.toString());
             }
         }
     }
 
-    public static void testBadSealedClass(String className, String expectedCFEMessage) throws Throwable {
+    public static void testBadSealedClass(String className,
+                                          Class<?> expectedException,
+                                          String expectedCFEMessage) throws Throwable {
         try {
             Class.forName(className);
             throw new RuntimeException("Expected ClassFormatError exception not thrown for " + className);
         } catch (ClassFormatError cfe) {
+            if (ClassFormatError.class != expectedException) {
+                throw new RuntimeException(
+                    "Class " + className + " got unexpected exception: " + cfe.getMessage());
+            }
             if (!cfe.getMessage().contains(expectedCFEMessage)) {
                 throw new RuntimeException(
                     "Class " + className + " got unexpected ClassFormatError exception: " + cfe.getMessage());
             }
+        } catch (IncompatibleClassChangeError icce) {
+            if (IncompatibleClassChangeError.class != expectedException) {
+                throw new RuntimeException(
+                    "Class " + className + " got unexpected exception: " + icce.getMessage());
+            }
+            if (!icce.getMessage().contains(expectedCFEMessage)) {
+                throw new RuntimeException(
+                    "Class " + className + " got unexpected IncompatibleClassChangeError exception: " + icce.getMessage());
+            }
         }
     }
 
     public static void main(String... args) throws Throwable {
         testSealedInfo(SealedI1.class, new String[] {"GetPermittedSubclassesTest$NotSealed",
                                                      "GetPermittedSubclassesTest$Sub1",
-                                                     "GetPermittedSubclassesTest$Extender"});
+                                                     "GetPermittedSubclassesTest$Extender"},
+                                                     true);
 
-        testSealedInfo(Sealed1.class, new String[] {"GetPermittedSubclassesTest$Sub1"});
-        testSealedInfo(Final4.class, new String[] { });
-        testSealedInfo(NotSealed.class, new String[] { });
+        testSealedInfo(Sealed1.class, new String[] {"GetPermittedSubclassesTest$Sub1"}, true);
+        testSealedInfo(Final4.class, null, false);
+        testSealedInfo(NotSealed.class, null, false);
 
         // Test class with PermittedSubclasses attribute but old class file version.
-        testSealedInfo(OldClassFile.class, new String[] { });
+        testSealedInfo(OldClassFile.class, null, false);
 
         // Test class with an empty PermittedSubclasses attribute.
-        testBadSealedClass("NoSubclasses", "PermittedSubclasses attribute is empty");
+        testSealedInfo(NoSubclasses.class, new String[]{}, true);
+
+        // Test that a class with an empty PermittedSubclasses attribute cannot be subclass-ed.
+        testBadSealedClass("SubClass", IncompatibleClassChangeError.class,
+                           "SubClass cannot inherit from sealed class NoSubclasses");
 
         // Test returning only names of existing classes.
-        testSealedInfo(NoLoadSubclasses.class, new String[]{"ExistingClassFile" });
+        testSealedInfo(NoLoadSubclasses.class, new String[]{"ExistingClassFile" }, true);
 
         // Test that loading a class with a corrupted PermittedSubclasses attribute
         // causes a ClassFormatError.
-        testBadSealedClass("BadPermittedAttr",
-                          "Permitted subclass class_info_index 15 has bad constant type");
+        testBadSealedClass("BadPermittedAttr", ClassFormatError.class,
+                           "Permitted subclass class_info_index 15 has bad constant type");
 
         // Test that loading a sealed final class with a PermittedSubclasses
         // attribute causes a ClassFormatError.
-        testBadSealedClass("SealedButFinal", "PermittedSubclasses attribute in final class");
+        testBadSealedClass("SealedButFinal", ClassFormatError.class,
+                           "PermittedSubclasses attribute in final class");
 
-        // Test that loading a sealed class with a bad class name in its PermittedSubclasses
-        // attribute causes a ClassFormatError.
-        testBadSealedClass("BadPermittedSubclassEntry", "Illegal class name \"iDont;;Exist\" in class file");
+        // Test that loading a sealed class with an ill-formed class name in its
+        // PermittedSubclasses attribute causes a ClassFormatError.
+        testBadSealedClass("BadPermittedSubclassEntry", ClassFormatError.class,
+                           "Illegal class name \"iDont;;Exist\" in class file");
 
         // Test that loading a sealed class with an empty class name in its PermittedSubclasses
         // attribute causes a ClassFormatError.
-        testBadSealedClass("EmptyPermittedSubclassEntry", "Illegal class name \"\" in class file");
+        testBadSealedClass("EmptyPermittedSubclassEntry", ClassFormatError.class,
+                           "Illegal class name \"\" in class file");
 
         //test type enumerated in the PermittedSubclasses attribute,
         //which are not direct subtypes of the current class are not returned:
-        testSealedInfo(noSubclass.BaseC.class, new String[] {"noSubclass.ImplCIntermediate"});
-        testSealedInfo(noSubclass.BaseI.class, new String[] {"noSubclass.ImplIIntermediateI", "noSubclass.ImplIIntermediateC"});
+        testSealedInfo(noSubclass.BaseC.class, new String[] {"noSubclass.ImplCIntermediate"}, true);
+        testSealedInfo(noSubclass.BaseI.class, new String[] {"noSubclass.ImplIIntermediateI", "noSubclass.ImplIIntermediateC"}, true);
     }
 }
diff --git a/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java b/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java
index abecc2b0215..27d20567464 100644
--- a/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java
+++ b/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java
@@ -110,8 +110,7 @@ public class SealedClassesReflectionTest {
     @Test(dataProvider = "notSealedClasses")
     public void testNotSealedClasses(Class<?> cls) {
         assertTrue(!cls.isSealed());
-        assertTrue(cls.getPermittedSubclasses() != null);
-        assertTrue(cls.getPermittedSubclasses().length == 0);
+        assertTrue(cls.getPermittedSubclasses() == null);
     }
 
     @DataProvider(name = "non_sealedClasses")
@@ -128,8 +127,7 @@ public class SealedClassesReflectionTest {
         assertTrue(!cls.isSealed());
         assertTrue(!Modifier.isFinal(cls.getModifiers()));
         assertTrue((cls.getSuperclass() != null && cls.getSuperclass().isSealed()) || Arrays.stream(cls.getInterfaces()).anyMatch(Class::isSealed));
-        assertTrue(cls.getPermittedSubclasses() != null);
-        assertTrue(cls.getPermittedSubclasses().length == 0);
+        assertTrue(cls.getPermittedSubclasses() == null);
     }
 
     @DataProvider(name = "reflectionData")
diff --git a/test/langtools/tools/javac/sealed/SealedCompilationTests.java b/test/langtools/tools/javac/sealed/SealedCompilationTests.java
index 005fd137082..5f830d08544 100644
--- a/test/langtools/tools/javac/sealed/SealedCompilationTests.java
+++ b/test/langtools/tools/javac/sealed/SealedCompilationTests.java
@@ -633,7 +633,7 @@ public class SealedCompilationTests extends CompilationTestCase {
             float.class, float[].class, double.class, double[].class, char.class, char[].class, boolean.class, boolean[].class, void.class,
             String[].class}) {
             Assert.check(!c.isSealed());
-            Assert.check(c.getPermittedSubclasses().length == 0);
+            Assert.check(c.getPermittedSubclasses() == null);
         }
     }