diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index 9548b3347bfc8..ef57f254a8ace 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -978,9 +978,9 @@ void Compile::Init(bool aliasing) {
   _immutable_memory = NULL; // filled in at first inquiry
 
 #ifdef ASSERT
-  _type_verify_symmetry = true;
   _phase_optimize_finished = false;
   _exception_backedge = false;
+  _type_verify = NULL;
 #endif
 
   // Globally visible Nodes
diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp
index f7f99ef682cca..c486a36828ae2 100644
--- a/src/hotspot/share/opto/compile.hpp
+++ b/src/hotspot/share/opto/compile.hpp
@@ -92,6 +92,7 @@ class UnstableIfTrap;
 class nmethod;
 class Node_Stack;
 struct Final_Reshape_Counts;
+class VerifyMeetResult;
 
 enum LoopOptsMode {
   LoopOptsDefault,
@@ -1211,7 +1212,7 @@ class Compile : public Phase {
   bool in_24_bit_fp_mode() const   { return _in_24_bit_fp_mode; }
 #endif // IA32
 #ifdef ASSERT
-  bool _type_verify_symmetry;
+  VerifyMeetResult* _type_verify;
   void set_exception_backedge() { _exception_backedge = true; }
   bool has_exception_backedge() const { return _exception_backedge; }
 #endif
diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp
index 08ebb8ceed903..0534544bf0fae 100644
--- a/src/hotspot/share/opto/type.cpp
+++ b/src/hotspot/share/opto/type.cpp
@@ -786,9 +786,155 @@ bool Type::is_nan()    const {
   return false;
 }
 
-void Type::check_symmetrical(const Type* t, const Type* mt) const {
 #ifdef ASSERT
-  const Type* mt2 = t->xmeet(this);
+class VerifyMeet;
+class VerifyMeetResult : public ArenaObj {
+  friend class VerifyMeet;
+  friend class Type;
+private:
+  class VerifyMeetResultEntry {
+  private:
+    const Type* _in1;
+    const Type* _in2;
+    const Type* _res;
+  public:
+    VerifyMeetResultEntry(const Type* in1, const Type* in2, const Type* res):
+            _in1(in1), _in2(in2), _res(res) {
+    }
+    VerifyMeetResultEntry():
+            _in1(NULL), _in2(NULL), _res(NULL) {
+    }
+
+    bool operator==(const VerifyMeetResultEntry& rhs) const {
+      return _in1 == rhs._in1 &&
+             _in2 == rhs._in2 &&
+             _res == rhs._res;
+    }
+
+    bool operator!=(const VerifyMeetResultEntry& rhs) const {
+      return !(rhs == *this);
+    }
+
+    static int compare(const VerifyMeetResultEntry& v1, const VerifyMeetResultEntry& v2) {
+      if ((intptr_t) v1._in1 < (intptr_t) v2._in1) {
+        return -1;
+      } else if (v1._in1 == v2._in1) {
+        if ((intptr_t) v1._in2 < (intptr_t) v2._in2) {
+          return -1;
+        } else if (v1._in2 == v2._in2) {
+          assert(v1._res == v2._res || v1._res == NULL || v2._res == NULL, "same inputs should lead to same result");
+          return 0;
+        }
+        return 1;
+      }
+      return 1;
+    }
+    const Type* res() const { return _res; }
+  };
+  uint _depth;
+  GrowableArray<VerifyMeetResultEntry> _cache;
+
+  // With verification code, the meet of A and B causes the computation of:
+  // 1- meet(A, B)
+  // 2- meet(B, A)
+  // 3- meet(dual(meet(A, B)), dual(A))
+  // 4- meet(dual(meet(A, B)), dual(B))
+  // 5- meet(dual(A), dual(B))
+  // 6- meet(dual(B), dual(A))
+  // 7- meet(dual(meet(dual(A), dual(B))), A)
+  // 8- meet(dual(meet(dual(A), dual(B))), B)
+  //
+  // In addition the meet of A[] and B[] requires the computation of the meet of A and B.
+  //
+  // The meet of A[] and B[] triggers the computation of:
+  // 1- meet(A[], B[][)
+  //   1.1- meet(A, B)
+  //   1.2- meet(B, A)
+  //   1.3- meet(dual(meet(A, B)), dual(A))
+  //   1.4- meet(dual(meet(A, B)), dual(B))
+  //   1.5- meet(dual(A), dual(B))
+  //   1.6- meet(dual(B), dual(A))
+  //   1.7- meet(dual(meet(dual(A), dual(B))), A)
+  //   1.8- meet(dual(meet(dual(A), dual(B))), B)
+  // 2- meet(B[], A[])
+  //   2.1- meet(B, A) = 1.2
+  //   2.2- meet(A, B) = 1.1
+  //   2.3- meet(dual(meet(B, A)), dual(B)) = 1.4
+  //   2.4- meet(dual(meet(B, A)), dual(A)) = 1.3
+  //   2.5- meet(dual(B), dual(A)) = 1.6
+  //   2.6- meet(dual(A), dual(B)) = 1.5
+  //   2.7- meet(dual(meet(dual(B), dual(A))), B) = 1.8
+  //   2.8- meet(dual(meet(dual(B), dual(A))), B) = 1.7
+  // etc.
+  // The number of meet operations performed grows exponentially with the number of dimensions of the arrays but the number
+  // of different meet operations is linear in the number of dimensions. The function below caches meet results for the
+  // duration of the meet at the root of the recursive calls.
+  //
+  const Type* meet(const Type* t1, const Type* t2) {
+    bool found = false;
+    const VerifyMeetResultEntry meet(t1, t2, NULL);
+    int pos = _cache.find_sorted<VerifyMeetResultEntry, VerifyMeetResultEntry::compare>(meet, found);
+    const Type* res = NULL;
+    if (found) {
+      res = _cache.at(pos).res();
+    } else {
+      res = t1->xmeet(t2);
+      _cache.insert_sorted<VerifyMeetResultEntry::compare>(VerifyMeetResultEntry(t1, t2, res));
+      found = false;
+      _cache.find_sorted<VerifyMeetResultEntry, VerifyMeetResultEntry::compare>(meet, found);
+      assert(found, "should be in table after it's added");
+    }
+    return res;
+  }
+
+  void add(const Type* t1, const Type* t2, const Type* res) {
+    _cache.insert_sorted<VerifyMeetResultEntry::compare>(VerifyMeetResultEntry(t1, t2, res));
+  }
+
+  bool empty_cache() const {
+    return _cache.length() == 0;
+  }
+public:
+  VerifyMeetResult(Compile* C) :
+          _depth(0), _cache(C->comp_arena(), 2, 0, VerifyMeetResultEntry()) {
+  }
+};
+
+void Type::assert_type_verify_empty() const {
+  assert(Compile::current()->_type_verify == NULL || Compile::current()->_type_verify->empty_cache(), "cache should have been discarded");
+}
+
+class VerifyMeet {
+private:
+  Compile* _C;
+public:
+  VerifyMeet(Compile* C) : _C(C) {
+    if (C->_type_verify == NULL) {
+      C->_type_verify = new (C->comp_arena())VerifyMeetResult(C);
+    }
+    _C->_type_verify->_depth++;
+  }
+
+  ~VerifyMeet() {
+    assert(_C->_type_verify->_depth != 0, "");
+    _C->_type_verify->_depth--;
+    if (_C->_type_verify->_depth == 0) {
+      _C->_type_verify->_cache.trunc_to(0);
+    }
+  }
+
+  const Type* meet(const Type* t1, const Type* t2) const {
+    return _C->_type_verify->meet(t1, t2);
+  }
+
+  void add(const Type* t1, const Type* t2, const Type* res) const {
+    _C->_type_verify->add(t1, t2, res);
+  }
+};
+
+void Type::check_symmetrical(const Type* t, const Type* mt, const VerifyMeet& verify) const {
+  Compile* C = Compile::current();
+  const Type* mt2 = verify.meet(t, this);
   if (mt != mt2) {
     tty->print_cr("=== Meet Not Commutative ===");
     tty->print("t           = ");   t->dump(); tty->cr();
@@ -798,8 +944,8 @@ void Type::check_symmetrical(const Type* t, const Type* mt) const {
     fatal("meet not commutative");
   }
   const Type* dual_join = mt->_dual;
-  const Type* t2t    = dual_join->xmeet(t->_dual);
-  const Type* t2this = dual_join->xmeet(this->_dual);
+  const Type* t2t    = verify.meet(dual_join,t->_dual);
+  const Type* t2this = verify.meet(dual_join,this->_dual);
 
   // Interface meet Oop is Not Symmetric:
   // Interface:AnyNull meet Oop:AnyNull == Interface:AnyNull
@@ -820,8 +966,8 @@ void Type::check_symmetrical(const Type* t, const Type* mt) const {
 
     fatal("meet not symmetric");
   }
-#endif
 }
+#endif
 
 //------------------------------meet-------------------------------------------
 // Compute the MEET of two types.  NOT virtual.  It enforces that meet is
@@ -836,32 +982,26 @@ const Type *Type::meet_helper(const Type *t, bool include_speculative) const {
     return result->make_narrowklass();
   }
 
+#ifdef ASSERT
+  Compile* C = Compile::current();
+  VerifyMeet verify(C);
+#endif
+
   const Type *this_t = maybe_remove_speculative(include_speculative);
   t = t->maybe_remove_speculative(include_speculative);
 
   const Type *mt = this_t->xmeet(t);
 #ifdef ASSERT
-  if (isa_narrowoop() || t->isa_narrowoop()) return mt;
-  if (isa_narrowklass() || t->isa_narrowklass()) return mt;
-  Compile* C = Compile::current();
-  if (!C->_type_verify_symmetry) {
+  verify.add(this_t, t, mt);
+  if (isa_narrowoop() || t->isa_narrowoop()) {
+    return mt;
+  }
+  if (isa_narrowklass() || t->isa_narrowklass()) {
     return mt;
   }
-  this_t->check_symmetrical(t, mt);
-  // In the case of an array, computing the meet above, caused the
-  // computation of the meet of the elements which at verification
-  // time caused the computation of the meet of the dual of the
-  // elements. Computing the meet of the dual of the arrays here
-  // causes the meet of the dual of the elements to be computed which
-  // would cause the meet of the dual of the dual of the elements,
-  // that is the meet of the elements already computed above to be
-  // computed. Avoid redundant computations by requesting no
-  // verification.
-  C->_type_verify_symmetry = false;
-  const Type *mt_dual = this_t->_dual->xmeet(t->_dual);
-  this_t->_dual->check_symmetrical(t->_dual, mt_dual);
-  assert(!C->_type_verify_symmetry, "shouldn't have changed");
-  C->_type_verify_symmetry = true;
+  this_t->check_symmetrical(t, mt, verify);
+  const Type *mt_dual = verify.meet(this_t->_dual, t->_dual);
+  this_t->_dual->check_symmetrical(t->_dual, mt_dual, verify);
 #endif
   return mt;
 }
diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp
index 3aadd24992e8b..2528e26b161de 100644
--- a/src/hotspot/share/opto/type.hpp
+++ b/src/hotspot/share/opto/type.hpp
@@ -70,6 +70,7 @@ class     TypeKlassPtr;
 class       TypeInstKlassPtr;
 class       TypeAryKlassPtr;
 class     TypeMetadataPtr;
+class VerifyMeet;
 
 //------------------------------Type-------------------------------------------
 // Basic Type object, represents a set of primitive Values.
@@ -170,7 +171,7 @@ class Type {
 
 
   const Type *meet_helper(const Type *t, bool include_speculative) const;
-  void check_symmetrical(const Type *t, const Type *mt) const;
+  void check_symmetrical(const Type* t, const Type* mt, const VerifyMeet& verify) const NOT_DEBUG_RETURN;
 
 protected:
   // Each class of type is also identified by its base.
@@ -181,9 +182,12 @@ class Type {
   const Type *hashcons();       // Hash-cons the type
   virtual const Type *filter_helper(const Type *kills, bool include_speculative) const;
   const Type *join_helper(const Type *t, bool include_speculative) const {
+    assert_type_verify_empty();
     return dual()->meet_helper(t->dual(), include_speculative)->dual();
   }
 
+  void assert_type_verify_empty() const NOT_DEBUG_RETURN;
+
 public:
 
   inline void* operator new( size_t x ) throw() {
diff --git a/test/hotspot/jtreg/compiler/types/TestArrayManyDimensions.java b/test/hotspot/jtreg/compiler/types/TestArrayManyDimensions.java
new file mode 100644
index 0000000000000..174790f5d795b
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/types/TestArrayManyDimensions.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2022, Red Hat, Inc. 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
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8297582
+ * @summary C2: very slow compilation due to type system verification code
+ *
+ * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation TestArrayManyDimensions
+ *
+ */
+
+import java.util.function.IntFunction;
+
+public class TestArrayManyDimensions {
+
+    static class A {
+
+    }
+
+    static class B extends A {
+    }
+
+    public static void main(String[] args) {
+        final IntFunction<String[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]> lambda = String[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]::new;
+        for (int i = 0; i < 20_000; i++) {
+            test1();
+            lambda.apply(2);
+            test2();
+        }
+    }
+
+    private static String[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] test1() {
+        return new String[2][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][];
+    }
+
+    private static A[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] test2() {
+        return new B[2][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][];
+    }
+}