docs: @SuppressWarnings({"unchecked", "checkstyle:cyclomaticcomplexity"})
      public static <T> T newInstance(final ClassLoader classLoaderHint, final String className) throws Exception {
          isNotNull(className, "className");
          final Class primitiveClass = tryPrimitiveClass(className);
          if (primitiveClass != null) {
              // Note: this will always throw java.lang.InstantiationException
              return (T) primitiveClass.newInstance();
          }
  
          // Note: Class.getClassLoader() and Thread.getContextClassLoader() are
          // allowed to return null
  
          // Note2: If classLoaderHint is not-null and the class' package is
          // HAZELCAST_BASE_PACKAGE, the thread's context class loader won't be
          // checked. We assume that if a classLoaderHint is given, the caller
          // knows the class should be found in that particular class loader. We
          // can't assume that all classes in `com.hazelcast` package are part of
          // this project, they can be samples, other HZ projects such as Jet etc.
  
          ClassLoader cl1 = classLoaderHint;
          if (cl1 == null) {
              cl1 = ClassLoaderUtil.class.getClassLoader();
          }
          if (cl1 == null) {
              cl1 = Thread.currentThread().getContextClassLoader();
          }
          ClassLoader cl2 = null;
          if ((className.startsWith(HAZELCAST_BASE_PACKAGE) || className.startsWith(HAZELCAST_ARRAY))
                  && cl1 != ClassLoaderUtil.class.getClassLoader()) {
              cl2 = ClassLoaderUtil.class.getClassLoader();
          }
          if (cl2 == null) {
              cl2 = Thread.currentThread().getContextClassLoader();
          }
          if (cl1 == cl2) {
              cl2 = null;
          }
          if (cl1 == null && cl2 != null) {
              cl1 = cl2;
              cl2 = null;
          }
  
          // check candidate class loaders in cache
          // note that cl1 might be null at this point: we'll use null key. In that case
          // we have no class loader to use
          if (cl1 != null) {
              Constructor<T> constructor = CONSTRUCTOR_CACHE.get(cl1, className);
  
              // If a constructor in question is not found in the preferred/hinted
              // class loader (cl1) constructor cache, that doesn't mean it can't
              // be provided by cl1. So we try to create an object instance using
              // cl1 first if its constructor is not marked as irresolvable in cl1
              // cache.
              //
              // This is important when both class loaders provide a class named
              // exactly the same. Such situations are not prohibited by JVM and
              // we have to resolve "conflicts" somehow, we prefer cl1 over cl2.
  
              if (constructor == IRRESOLVABLE_CONSTRUCTOR && cl2 != null) {
                  constructor = CONSTRUCTOR_CACHE.get(cl2, className);
              }
  
              if (constructor != null && constructor != IRRESOLVABLE_CONSTRUCTOR) {
                  return constructor.newInstance();
              }
          }
  
          // if not found in cache, try to query the class loaders and add constructor to cache
          try {
              return newInstance0(cl1, className);
          } catch (ClassNotFoundException e1) {
              if (cl2 != null) {
                  // Mark as irresolvable only if we were trying to give it a
                  // priority over cl2 to save cl1 cache space.
                  CONSTRUCTOR_CACHE.put(cl1, className, IRRESOLVABLE_CONSTRUCTOR);
  
                  try {
                      return newInstance0(cl2, className);
                  } catch (ClassNotFoundException e2) {
                      ignore(e2);
                  }
              }
              throw e1;
          }
      }