The root GC cause lies in javax.cache.Caching which holds a static collection of CachingProvider-s shared between all Spring contexts if tests are run in the same JVM.
Spring contexts created during a test run share the same CachingProvider and therefore the same CacheManagers. When any of the contexts sharing a CachingProvider is closed, all related CacheManagers are closed too thus leaving remaining Spring contexts referring to the closed CachingProvider in an inconsistent state.
To address the issue, each request for a CacheManager should return a completely new instance not shared with other contexts.
I wrote a simple CachingProvider implementation that does just this and relies on existing CachingProviders. Please find the code below. 
The base class:
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.WeakHashMap;
import javax.cache.CacheManager;
import javax.cache.configuration.OptionalFeature;
import javax.cache.spi.CachingProvider;
/**
 * The abstract JCache compatible {@link CachingProvider} suitable for test purposes.
 *
 * <p>When using JCache and {@link org.hibernate.cache.jcache.JCacheRegionFactory}, {@link CachingProvider}-s
 * are shared between Spring contexts, which means that {@link CacheManager}-s are shared too. The class responsible
 * for storing loaded {@link CachingProvider}-s is {@link javax.cache.Caching}. If any cached Spring context is closed,
 * then all related {@link CacheManager}-s are closed as well, but since these {@link CacheManager}-s are shared with
 * remaining Spring contexts, we end up with in an inconsistent state.</p>
 *
 * <p>The solution is to make sure that each time a {@link CacheManager} for a particular config URI is requested, a new
 * instance not shared between Spring contexts is created</p>
 *
 * <p>The simplest approach is to create a new instance of {@link CachingProvider} for each {@link CacheManager} request
 * and manage them separately from {@link CachingProvider}-s loaded via {@link javax.cache.Caching}. This approach
 * allows reusing existing required {@link CachingProvider}-s and overcome any sharing issues.</p>
 *
 * <p>Tests relying on caching functionality MUST make sure that for regular caching the properties
 * {@code spring.cache.jcache.provider} and {@code spring.cache.jcache.config} are set and for 2nd-level cache
 * the properties {@code spring.jpa.properties.hibernate.javax.cache.provider} and
 * {@code spring.jpa.properties.hibernate.javax.cache.uri} are set. Please note that classpath URI-s for
 * the {@code spring.jpa.properties.hibernate.javax.cache.uri} property are supported by {@code hibernate-jcache} only
 * since 5.4.1, therefore with earlier versions this property should be set programmatically, for example via
 * {@link System#setProperty(String, String)}.</p>
 *
 * @see <a href="https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#caching-provider-jcache-cache-manager">Hibernate
 * JCache configuration</a>
 * @see org.hibernate.cache.jcache.JCacheRegionFactory
 * @see CachingProvider
 * @see javax.cache.Caching
 */
public abstract class AbstractTestJCacheCachingProvider implements CachingProvider {
    /**
     * The {@link CachingProvider}-s specific for a configuration {@link URI} for a specific {@link ClassLoader}.
     *
     * <p>All access MUST be handled in a <i>synchronized</i> manner.</p>
     */
    private final Map<ClassLoader, Map<URI, List<CachingProvider>>>
            classLoaderToUriToCachingProviders = new WeakHashMap<>();
    /**
     * {@inheritDoc}
     */
    @Override
    public CacheManager getCacheManager(URI uri, ClassLoader classLoader, Properties properties) {
        Objects.requireNonNull(uri, "The cache manager configuration URI must not be null.");
        Objects.requireNonNull(classLoader, "The class loader must not be null");
        final CachingProvider cachingProvider = createCachingProvider();
        synchronized (classLoaderToUriToCachingProviders) {
            classLoaderToUriToCachingProviders
                    .computeIfAbsent(classLoader, k -> new HashMap<>())
                    .computeIfAbsent(uri, k -> new ArrayList<>())
                    .add(cachingProvider);
        }
        return cachingProvider.getCacheManager(uri, classLoader, properties);
    }
    /**
     * Creates a {@link CachingProvider}.
     *
     * @return a created {@link CachingProvider}
     */
    protected abstract CachingProvider createCachingProvider();
    /**
     * {@inheritDoc}
     */
    @Override
    public ClassLoader getDefaultClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public URI getDefaultURI() {
        throw new UnsupportedOperationException("Please specify an explicit cache manager configuration URI.");
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Properties getDefaultProperties() {
        return new Properties();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public CacheManager getCacheManager(URI uri, ClassLoader classLoader) {
        return getCacheManager(uri, classLoader, null);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public CacheManager getCacheManager() {
        throw new UnsupportedOperationException("The cache manager configuration URI must be specified.");
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void close() {
        synchronized (classLoaderToUriToCachingProviders) {
            classLoaderToUriToCachingProviders.keySet().forEach(this::close);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void close(ClassLoader classLoader) {
        Objects.requireNonNull(classLoader, "The class loader must not be null");
        synchronized (classLoaderToUriToCachingProviders) {
            // Process all CachingProvider collections regardless of the configuration URI.
            classLoaderToUriToCachingProviders
                    .getOrDefault(classLoader, Collections.emptyMap())
                    .values().stream().flatMap(Collection::stream)
                    // Close all CachingProvider resources since we are sure that CachingProvider-s are not shared
                    // or reused.
                    .forEach(CachingProvider::close);
            classLoaderToUriToCachingProviders.remove(classLoader);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void close(URI uri, ClassLoader classLoader) {
        Objects.requireNonNull(uri, "The cache manager configuration URI must not be null");
        Objects.requireNonNull(classLoader, "The class loader must not be null");
        synchronized (classLoaderToUriToCachingProviders) {
            final Map<URI, List<CachingProvider>> uriToCachingProviders = classLoaderToUriToCachingProviders
                    .getOrDefault(classLoader, Collections.emptyMap());
            uriToCachingProviders
                    .getOrDefault(uri, Collections.emptyList())
                    // Close all CachingProvider resources since we are sure that CachingProvider-s are not shared
                    // or reused.
                    .forEach(CachingProvider::close);
            uriToCachingProviders.remove(uri);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSupported(OptionalFeature optionalFeature) {
        // Find the first available CachingProvider and delegate the request to it.
        synchronized (classLoaderToUriToCachingProviders) {
            return classLoaderToUriToCachingProviders.values().stream().findFirst()
                    .flatMap(uriToCachingProviders -> uriToCachingProviders.values().stream().findFirst())
                    .flatMap(cachingProviders -> cachingProviders.stream().findFirst())
                    .map(cachingProvider -> cachingProvider.isSupported(optionalFeature))
                    .orElse(false);
        }
    }
}
The Ehcache based implementation:
import javax.cache.spi.CachingProvider;
import org.ehcache.jsr107.EhcacheCachingProvider;
/**
 * The test {@link CachingProvider} based on {@link EhcacheCachingProvider}.
 */
public class TestEhcacheJCacheCachingProvider extends AbstractTestJCacheCachingProvider {
    @Override
    protected CachingProvider createCachingProvider() {
        return new EhcacheCachingProvider();
    }
}