diff --git a/core/core.services.yml b/core/core.services.yml
index 7ee598ee6fbeab1e39dee228a358c4f5e3450e18..297669a6665ceb8bfab085aceebfcaf603bb1a2d 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -7,6 +7,7 @@ parameters:
   # \Drupal\Core\Cache\ListCacheBinsPass::process() will override this but defining this allows the cache system to
   # function properly before that runs.
   cache_default_bin_backends: []
+  memory_cache_default_bin_backends: []
   session.storage.options:
     gc_probability: 1
     gc_divisor: 100
@@ -194,7 +195,7 @@ services:
 
   cache_factory:
     class: Drupal\Core\Cache\CacheFactory
-    arguments: ['@settings', '%cache_default_bin_backends%']
+    arguments: ['@settings', '%cache_default_bin_backends%', '%memory_cache_default_bin_backends%']
     calls:
       - [setContainer, ['@service_container']]
   Drupal\Core\Cache\CacheFactoryInterface: '@cache_factory'
@@ -239,6 +240,8 @@ services:
     arguments: ['@cache_tags.invalidator.checksum']
   cache.backend.memory:
     class: Drupal\Core\Cache\MemoryBackendFactory
+  cache.backend.memory.memory:
+    class: Drupal\Core\Cache\MemoryCache\MemoryCacheFactory
   # A special cache bin that does not persist beyond the length of the request.
   cache.static:
     class: Drupal\Core\Cache\CacheBackendInterface
@@ -289,9 +292,11 @@ services:
     factory: ['@cache_factory', 'get']
     arguments: [access_policy]
   cache.access_policy_memory:
-    class: Drupal\Core\Cache\MemoryCache\MemoryCache
+    class: Drupal\Core\Cache\CacheBackendInterface
     tags:
-      - { name: cache_tags_invalidator }
+      - { name: cache.bin.memory, default_backend: cache.backend.memory.memory }
+    factory: ['@cache_factory', 'get']
+    arguments: [access_policy_memory]
   cache.data:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
@@ -308,17 +313,10 @@ services:
     class: Drupal\Core\Cache\VariationCacheInterface
     factory: ['@variation_cache_factory', 'get']
     arguments: [access_policy]
-  # We cannot use the VariationCacheFactory here because that expects that the
-  # backend it should wrap a VariationCache around is a proper cache bin. The
-  # MemoryCache backend, however, is not so we cannot use the factory here.
-  #
-  # By instantiating a VariationCache via the container and tagging the cache
-  # it's wrapping as a cache tags invalidator, it does still behave the way we
-  # want it to, though.
   variation_cache.access_policy_memory:
-    class: Drupal\Core\Cache\VariationCache
-    arguments: ['@request_stack', '@cache.access_policy_memory', '@cache_contexts_manager']
-  Drupal\Core\Cache\VariationCacheInterface: '@variation_cache.access_policy_memory'
+    class: Drupal\Core\Cache\VariationCacheInterface
+    factory: ['@variation_cache_factory', 'get']
+    arguments: [access_policy_memory]
   Drupal\Core\Asset\AssetQueryStringInterface: '@asset.query_string'
   asset.query_string:
     class: Drupal\Core\Asset\AssetQueryString
diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php
index f93a4708027ca5a13dad4f8c1a7833c1fc0b1389..fdff7e2075a346b29ec1e56f833fc7a3546d45dd 100644
--- a/core/lib/Drupal/Core/Cache/Cache.php
+++ b/core/lib/Drupal/Core/Cache/Cache.php
@@ -126,6 +126,21 @@ public static function getBins() {
     return $bins;
   }
 
+  /**
+   * Gets all memory cache bin services.
+   *
+   * @return \Drupal\Core\Cache\CacheBackendInterface[]
+   *   An array of cache backend objects keyed by memory cache bins.
+   */
+  public static function getMemoryBins(): array {
+    $bins = [];
+    $container = \Drupal::getContainer();
+    foreach ($container->getParameter('memory_cache_bins') as $service_id => $bin) {
+      $bins[$bin] = $container->get($service_id);
+    }
+    return $bins;
+  }
+
   /**
    * Generates a hash from a query object, to be used as part of the cache key.
    *
diff --git a/core/lib/Drupal/Core/Cache/CacheFactory.php b/core/lib/Drupal/Core/Cache/CacheFactory.php
index eefbbae5d4fda6af7ff2d14582bee61e35d8be25..845014581b404bd87479e67513552c178a682342 100644
--- a/core/lib/Drupal/Core/Cache/CacheFactory.php
+++ b/core/lib/Drupal/Core/Cache/CacheFactory.php
@@ -33,6 +33,17 @@ class CacheFactory implements CacheFactoryInterface, ContainerAwareInterface {
    */
   protected $defaultBinBackends;
 
+  /**
+   * A map of cache bin to default cache memory backend service name.
+   *
+   * All bin-specific mappings in $settings take precedence over this, but it
+   * can be used to optimize cache storage for a Drupal installation without
+   * cache customizations in settings.php.
+   *
+   * @var array
+   */
+  protected $memoryDefaultBinBackends;
+
   /**
    * Constructs CacheFactory object.
    *
@@ -41,10 +52,14 @@ class CacheFactory implements CacheFactoryInterface, ContainerAwareInterface {
    * @param array $default_bin_backends
    *   (optional) A mapping of bin to backend service name. Mappings in
    *   $settings take precedence over this.
+   * @param array $memory_default_bin_backends
+   *   (optional) A mapping of bin to backend service name. Mappings in
+   *   $settings take precedence over this.
    */
-  public function __construct(Settings $settings, array $default_bin_backends = []) {
+  public function __construct(Settings $settings, array $default_bin_backends = [], array $memory_default_bin_backends = []) {
     $this->settings = $settings;
     $this->defaultBinBackends = $default_bin_backends;
+    $this->memoryDefaultBinBackends = $memory_default_bin_backends;
   }
 
   /**
@@ -72,6 +87,9 @@ public function get($bin) {
     elseif (isset($this->defaultBinBackends[$bin])) {
       $service_name = $this->defaultBinBackends[$bin];
     }
+    elseif (isset($this->memoryDefaultBinBackends[$bin])) {
+      $service_name = $this->memoryDefaultBinBackends[$bin];
+    }
     // Third, use configured default backend.
     elseif (isset($cache_settings['default'])) {
       $service_name = $cache_settings['default'];
diff --git a/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php b/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
index 820da9886bda0b2d38cddc5c71e7ba8a68cbeb1b..d2a8f1e5512a8627bb11b34e4afd2bd49e4d1fc3 100644
--- a/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
+++ b/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
@@ -68,10 +68,12 @@ public function addInvalidator(CacheTagsInvalidatorInterface $invalidator) {
    */
   protected function getInvalidatorCacheBins() {
     $bins = [];
-    foreach ($this->container->getParameter('cache_bins') as $service_id => $bin) {
-      $service = $this->container->get($service_id);
-      if ($service instanceof CacheTagsInvalidatorInterface) {
-        $bins[$bin] = $service;
+    foreach (['cache_bins', 'memory_cache_bins'] as $parameter) {
+      foreach ($this->container->getParameter($parameter) as $service_id => $bin) {
+        $service = $this->container->get($service_id);
+        if ($service instanceof CacheTagsInvalidatorInterface) {
+          $bins[$bin] = $service;
+        }
       }
     }
     return $bins;
diff --git a/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php b/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
index 32bac09c086d3aae00767b1558c599ed5113ced4..55a4777881cff6dd1ce1f2f4c529fecdd250a9e0 100644
--- a/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
+++ b/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
@@ -16,17 +16,29 @@ class ListCacheBinsPass implements CompilerPassInterface {
    * Collects the cache bins into the cache_bins parameter.
    */
   public function process(ContainerBuilder $container) {
-    $cache_bins = [];
-    $cache_default_bin_backends = [];
-    foreach ($container->findTaggedServiceIds('cache.bin') as $id => $attributes) {
-      $bin = substr($id, strpos($id, '.') + 1);
-      $cache_bins[$id] = $bin;
-      if (isset($attributes[0]['default_backend'])) {
-        $cache_default_bin_backends[$bin] = $attributes[0]['default_backend'];
+    $cache_info['cache']['bins'] = [];
+    $cache_info['cache']['default_bin_backends'] = [];
+    $cache_info['memory_cache']['bins'] = [];
+    $cache_info['memory_cache']['default_bin_backends'] = [];
+
+    $tag_info = [
+      'cache.bin' => 'cache',
+      'cache.bin.memory' => 'memory_cache',
+    ];
+    foreach ($tag_info as $service_tag => $section) {
+      foreach ($container->findTaggedServiceIds($service_tag) as $id => $attributes) {
+        $bin = substr($id, strpos($id, '.') + 1);
+        $cache_info[$section]['bins'][$id] = $bin;
+        if (isset($attributes[0]['default_backend'])) {
+          $cache_info[$section]['default_bin_backends'][$bin] = $attributes[0]['default_backend'];
+        }
       }
     }
-    $container->setParameter('cache_bins', $cache_bins);
-    $container->setParameter('cache_default_bin_backends', $cache_default_bin_backends);
+
+    $container->setParameter('cache_bins', $cache_info['cache']['bins']);
+    $container->setParameter('cache_default_bin_backends', $cache_info['cache']['default_bin_backends']);
+    $container->setParameter('memory_cache_bins', $cache_info['memory_cache']['bins']);
+    $container->setParameter('memory_cache_default_bin_backends', $cache_info['memory_cache']['default_bin_backends']);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Cache/MemoryCache/MemoryCacheFactory.php b/core/lib/Drupal/Core/Cache/MemoryCache/MemoryCacheFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f2fcb7beabd60535a24b6b3183cfe7e755a3fbc
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/MemoryCache/MemoryCacheFactory.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\Core\Cache\MemoryCache;
+
+use Drupal\Core\Cache\CacheFactoryInterface;
+
+class MemoryCacheFactory implements CacheFactoryInterface {
+
+  /**
+   * Instantiated memory cache bins.
+   *
+   * @var \Drupal\Core\Cache\MemoryBackend[]
+   */
+  protected $bins = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($bin) {
+    if (!isset($this->bins[$bin])) {
+      $this->bins[$bin] = new MemoryCache();
+    }
+    return $this->bins[$bin];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php b/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php
index 5c7ef11f99b8024778e1da39fbbf8e0adc670d3e..0e00b111345a1a2897ad67bdc641163e8e193acd 100644
--- a/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php
+++ b/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php
@@ -30,6 +30,11 @@ protected function refreshVariables() {
         $backend->reset();
       }
     }
+    foreach (Cache::getMemoryBins() as $backend) {
+      if (is_callable([$backend, 'reset'])) {
+        $backend->reset();
+      }
+    }
 
     \Drupal::service('config.factory')->reset();
     \Drupal::service('state')->resetCache();
diff --git a/core/modules/book/book.services.yml b/core/modules/book/book.services.yml
index d15986f20cabdfc7ef52d26af3076b40dbaf8141..1552c2cf5f7d265d95b0edd416666d0d586e62c6 100644
--- a/core/modules/book/book.services.yml
+++ b/core/modules/book/book.services.yml
@@ -41,7 +41,11 @@ services:
     arguments: ['@book.outline_storage', '@entity_type.manager', '@string_translation']
     lazy: true
   book.memory_cache:
-    class: Drupal\Core\Cache\MemoryCache\MemoryCache
+    class: Drupal\Core\Cache\MemoryCache\MemoryCacheInterface
+    tags:
+      - { name: cache.bin.memory, default_backend: cache.backend.memory.memory }
+    factory: ['@cache_factory', 'get']
+    arguments: [memory_cache]
   book.backend_chained_cache:
     class: Drupal\Core\Cache\BackendChain
     calls:
@@ -50,4 +54,4 @@ services:
     tags:
       # This tag ensures that Drupal's cache_tags.invalidator service
       # invalidates also this cache data.
-      - { name: cache.bin }
+      - { name: cache.bin.memory }
diff --git a/core/modules/jsonapi/jsonapi.services.yml b/core/modules/jsonapi/jsonapi.services.yml
index 1397335257c2b02a7af89133f312557c2bda4c84..d032175d46123082119703f1f7d9d62e984387b1 100644
--- a/core/modules/jsonapi/jsonapi.services.yml
+++ b/core/modules/jsonapi/jsonapi.services.yml
@@ -125,8 +125,11 @@ services:
 
   # Cache.
   cache.jsonapi_memory:
-    class: Drupal\Core\Cache\MemoryCache\MemoryCache
-    public: false
+    class: Drupal\Core\Cache\MemoryCache\MemoryCacheInterface
+    tags:
+      - { name: cache.bin.memory, default_backend: cache.backend.memory.memory }
+    factory: ['@cache_factory', 'get']
+    arguments: [jsonapi_memory]
 
   # A chained cache with an in-memory cache as the first layer and a database-
   # backed cache as the fallback is used. The first layer (memory) is necessary
@@ -138,7 +141,7 @@ services:
     calls:
       - [appendBackend, ['@cache.jsonapi_memory']]
       - [appendBackend, ['@cache.default']]
-    tags: [{ name: cache.bin }]
+    tags: [{ name: cache.bin.memory }]
   cache.jsonapi_normalizations:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
index a9600c2bb197f82347e87c69de36c6c6c3a63d12..02f6731e94ff568157401f3e876f2db6d2f60603 100644
--- a/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
@@ -41,10 +41,20 @@ public function testInvalidateTags() {
     // a fatal error.
     $non_invalidator_cache_bin = $this->createMock('\Drupal\Core\Cache\CacheBackendInterface');
 
+    // Repeat the above for memory cache bins.
+    $invalidator_memory_cache_bin = $this->createMock('\Drupal\Core\Cache\CacheTagsInvalidator');
+    $invalidator_memory_cache_bin->expects($this->once())
+      ->method('invalidateTags')
+      ->with(['node:1']);
+    $non_invalidator_memory_cache_bin = $this->createMock('\Drupal\Core\Cache\CacheBackendInterface');
+
     $container = new Container();
     $container->set('cache.invalidator_cache_bin', $invalidator_cache_bin);
     $container->set('cache.non_invalidator_cache_bin', $non_invalidator_cache_bin);
+    $container->set('cache.invalidator_memory_cache_bin', $invalidator_memory_cache_bin);
+    $container->set('cache.non_invalidator_memory_cache_bin', $non_invalidator_memory_cache_bin);
     $container->setParameter('cache_bins', ['cache.invalidator_cache_bin' => 'invalidator_cache_bin', 'cache.non_invalidator_cache_bin' => 'non_invalidator_cache_bin']);
+    $container->setParameter('memory_cache_bins', ['cache.invalidator_memory_cache_bin' => 'invalidator_memory_cache_bin', 'cache.non_invalidator_memory_cache_bin' => 'non_invalidator_memory_cache_bin']);
     $cache_tags_invalidator->setContainer($container);
 
     $invalidator = $this->createMock('\Drupal\Core\Cache\CacheTagsInvalidator');