diff --git a/core/lib/Drupal/Component/Discovery/YamlDiscovery.php b/core/lib/Drupal/Component/Discovery/YamlDiscovery.php
index 1d147fd..002327d 100644
--- a/core/lib/Drupal/Component/Discovery/YamlDiscovery.php
+++ b/core/lib/Drupal/Component/Discovery/YamlDiscovery.php
@@ -8,6 +8,7 @@
 namespace Drupal\Component\Discovery;
 
 use Drupal\Component\Serialization\Yaml;
+use Drupal\Component\FileCache\FileCacheFactory;
 
 /**
  * Provides discovery for YAML files within a given set of directories.
@@ -47,10 +48,27 @@ public function __construct($name, array $directories) {
    */
   public function findAll() {
     $all = array();
-    foreach ($this->findFiles() as $provider => $file) {
-      // If a file is empty or its contents are commented out, return an empty
-      // array instead of NULL for type consistency.
-      $all[$provider] = Yaml::decode(file_get_contents($file)) ?: [];
+
+    $files = $this->findFiles();
+    $provider_by_files = array_flip($files);
+
+    $file_cache = FileCacheFactory::get('yaml_discovery:' . $this->name);
+
+    // Try to load from the file cache first.
+    foreach ($file_cache->getMultiple($files) as $file => $data) {
+      $all[$provider_by_files[$file]] = $data;
+      unset($provider_by_files[$file]);
+    }
+
+    // If there are files left that were not returned from the cache, load and
+    // parse them now. This list was flipped above and is keyed by filename.
+    if ($provider_by_files) {
+      foreach ($provider_by_files as $file => $provider) {
+        // If a file is empty or its contents are commented out, return an empty
+        // array instead of NULL for type consistency.
+        $all[$provider] = Yaml::decode(file_get_contents($file)) ?: [];
+        $file_cache->set($file, $all[$provider]);
+      }
     }
 
     return $all;
diff --git a/core/lib/Drupal/Component/FileCache/ApcuFileCacheBackend.php b/core/lib/Drupal/Component/FileCache/ApcuFileCacheBackend.php
new file mode 100644
index 0000000..25b6097
--- /dev/null
+++ b/core/lib/Drupal/Component/FileCache/ApcuFileCacheBackend.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\FileCache\ApcuFileCacheBackend.
+ */
+
+namespace Drupal\Component\FileCache;
+
+/**
+ * APCu backend for the file cache.
+ */
+class ApcuFileCacheBackend implements FileCacheBackendInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetch(array $cids) {
+    return apc_fetch($cids);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function store($cid, $data) {
+    apc_store($cid, $data);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($cid) {
+    apc_delete($cid);
+  }
+
+}
diff --git a/core/lib/Drupal/Component/FileCache/FileCache.php b/core/lib/Drupal/Component/FileCache/FileCache.php
new file mode 100644
index 0000000..761d490
--- /dev/null
+++ b/core/lib/Drupal/Component/FileCache/FileCache.php
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\FileCache\FileCache.
+ */
+
+namespace Drupal\Component\FileCache;
+
+/**
+ * Allows to cache data based on file modification dates.
+ */
+class FileCache implements FileCacheInterface {
+
+  /**
+   * Prefix that is used for cache entries.
+   *
+   * @var string
+   */
+  protected $prefix;
+
+  /**
+   * Static cache that contains already loaded cache entries.
+   *
+   * @var array
+   */
+  protected static $cached = [];
+
+  /**
+   * The collection identifier of this cache.
+   *
+   * @var string
+   */
+  protected $collection;
+
+  /**
+   * The cache backend backing this FileCache object.
+   *
+   * @var \Drupal\Component\FileCache\FileCacheBackendInterface
+   */
+  protected $cache;
+
+  /**
+   * Constructs a FileCache object.
+   *
+   * @param string $prefix
+   *   The cache prefix.
+   * @param string $collection
+   *   A collection identifier to ensure that the same files could be cached for
+   *   different purposes without clashing.
+   * @param string|null $cache_backend_class
+   *   (optional) The class that should be used as cache backend.
+   * @param array $cache_backend_configuration
+   *   (optional) The configuration for the backend class.
+   */
+  public function __construct($prefix, $collection, $cache_backend_class = NULL, array $cache_backend_configuration = []) {
+
+    if (empty($prefix)) {
+      throw new \InvalidArgumentException('Required prefix configuration is missing');
+    }
+
+    $this->prefix = $prefix;
+    $this->collection = $collection;
+
+    if (isset($cache_backend_class)) {
+      $this->cache = new $cache_backend_class($cache_backend_configuration);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($filepath) {
+    $filepaths = [$filepath];
+    $cached = $this->getMultiple($filepaths);
+    return isset($cached[$filepath]) ? $cached[$filepath] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMultiple(array $filepaths) {
+    $file_data = [];
+    $remaining_cids = [];
+
+    // First load from the static cache what we can.
+    foreach ($filepaths as $filepath) {
+      if (!file_exists($filepath)) {
+        continue;
+      }
+
+      $realpath = realpath($filepath);
+      // If the file exists but realpath returns nothing, it is using a stream
+      // wrapper, those are not supported.
+      if (empty($realpath)) {
+        continue;
+      }
+
+      $cid = $this->prefix . ':' . $this->collection . ':' . $realpath;
+      if (isset(static::$cached[$cid]) && static::$cached[$cid]['mtime'] == filemtime($filepath)) {
+        $file_data[$filepath] = static::$cached[$cid]['data'];
+      }
+      else {
+        // Collect a list of cache IDs that we still need to fetch from cache
+        // backend.
+        $remaining_cids[$cid] = $filepath;
+      }
+    }
+
+    // If there are any cache IDs left to fetch from the cache backend.
+    if ($remaining_cids && $this->cache) {
+      $cache_results = $this->cache->fetch(array_keys($remaining_cids)) ?: [];
+      foreach ($cache_results as $cid => $cached) {
+        $filepath = $remaining_cids[$cid];
+        if ($cached['mtime'] == filemtime($filepath)) {
+          $file_data[$cached['filepath']] = $cached['data'];
+          static::$cached[$cid] = $cached;
+        }
+      }
+    }
+
+    return $file_data;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($filepath, $data) {
+    $realpath = realpath($filepath);
+    $cached = [
+      'mtime' => filemtime($filepath),
+      'filepath' => $filepath,
+      'data' => $data,
+    ];
+
+    $cid = $this->prefix . ':' . $this->collection . ':' . $realpath;
+    static::$cached[$cid] = $cached;
+    if ($this->cache) {
+      $this->cache->store($cid, $cached);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($filepath) {
+    $realpath = realpath($filepath);
+    $cid = $this->prefix . ':' . $this->collection . ':' . $realpath;
+
+    unset(static::$cached[$cid]);
+    if ($this->cache) {
+      $this->cache->delete($cid);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Component/FileCache/FileCacheBackendInterface.php b/core/lib/Drupal/Component/FileCache/FileCacheBackendInterface.php
new file mode 100644
index 0000000..ce31615
--- /dev/null
+++ b/core/lib/Drupal/Component/FileCache/FileCacheBackendInterface.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\FileCache\FileCacheBackendInterface.
+ */
+
+namespace Drupal\Component\FileCache;
+
+/**
+ * Defines an interface inspired by APCu for FileCache backends.
+ */
+interface FileCacheBackendInterface {
+
+  /**
+   * Fetches data from the cache backend.
+   *
+   * @param array $cids
+   *   The cache IDs to fetch.
+   *
+   * @return array
+   *   An array containing cache entries keyed by cache ID.
+   */
+  public function fetch(array $cids);
+
+  /**
+   * Stores data into a cache backend.
+   *
+   * @param string $cid
+   *   The cache ID to store data to.
+   * @param mixed $data
+   *   The data to store.
+   */
+  public function store($cid, $data);
+
+  /**
+   * Deletes data from a cache backend.
+   *
+   * @param string $cid
+   *   The cache ID to delete.
+   */
+  public function delete($cid);
+
+}
diff --git a/core/lib/Drupal/Component/FileCache/FileCacheFactory.php b/core/lib/Drupal/Component/FileCache/FileCacheFactory.php
new file mode 100644
index 0000000..df391c0
--- /dev/null
+++ b/core/lib/Drupal/Component/FileCache/FileCacheFactory.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Component\FileCache\FileCacheFactory.
+ */
+
+namespace Drupal\Component\FileCache;
+
+/**
+ * Creates a FileCache object.
+ */
+class FileCacheFactory {
+
+  /**
+   * The configuration used to create FileCache objects.
+   *
+   * @var array $configuration
+   */
+  protected static $configuration;
+
+  /**
+   * The cache prefix.
+   *
+   * @var string
+   */
+  protected static $prefix;
+
+  /**
+   * Instantiates a FileCache object for a given collection identifier.
+   *
+   * @param string $collection
+   *   The collection identifier for this FileCache.
+   * @param array $default_configuration
+   *   (optional) The default configuration for this FileCache collection. This
+   *   can be used to e.g. specify default usage of a FileCache class.
+   *
+   * @return \Drupal\Component\FileCache\FileCacheInterface
+   *   The initialized FileCache object.
+   */
+  public static function get($collection, $default_configuration = []) {
+    $default_configuration += [
+      'class' => '\Drupal\Component\FileCache\FileCache',
+      'collection' => $collection,
+      'cache_backend_class' => NULL,
+      'cache_backend_configuration' => [],
+    ];
+
+    $configuration = [];
+    if (isset(static::$configuration[$collection])) {
+      $configuration = static::$configuration[$collection];
+    }
+    elseif (isset(static::$configuration['default'])) {
+      $configuration = static::$configuration['default'];
+    }
+
+    // Add defaults to the configuration.
+    $configuration = $configuration + $default_configuration;
+
+    $class = $configuration['class'];
+    return new $class(static::getPrefix(), $configuration['collection'], $configuration['cache_backend_class'], $configuration['cache_backend_configuration']);
+  }
+
+  /**
+   * Gets the configuration used for constructing future file cache objects.
+   *
+   * @return array
+   *   The configuration that is used.
+   */
+  public static function getConfiguration() {
+    return static::$configuration;
+  }
+
+  /**
+   * Sets the configuration to use for constructing future file cache objects.
+   *
+   * @param array $configuration
+   *   The configuration to use.
+   */
+  public static function setConfiguration($configuration) {
+    static::$configuration = $configuration;
+  }
+
+  /**
+   * Returns the cache prefix.
+   *
+   * @return string
+   *   The cache prefix.
+   */
+  public static function getPrefix() {
+    return static::$prefix;
+  }
+
+  /**
+   * Sets the cache prefix that should be used.
+   *
+   * Should be set to a secure, unique key to prevent cache pollution by a
+   * third party.
+   *
+   * @param string $prefix
+   *   The cache prefix.
+   */
+  public static function setPrefix($prefix) {
+    static::$prefix = $prefix;
+  }
+
+}
diff --git a/core/lib/Drupal/Component/FileCache/FileCacheInterface.php b/core/lib/Drupal/Component/FileCache/FileCacheInterface.php
new file mode 100644
index 0000000..e347783
--- /dev/null
+++ b/core/lib/Drupal/Component/FileCache/FileCacheInterface.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\FileCache\FileCacheInterface.
+ */
+
+namespace Drupal\Component\FileCache;
+
+/**
+ * Interface for objects that allow caching file data.
+ *
+ * Parsing YAML, annotations or similar data out of files can be a
+ * time-consuming process, especially since those files usually don't change
+ * and identical data is parsed over and over again.
+ *
+ * File cache is a self-contained caching layer for such processing, that relies
+ * on the file modification to ensure that cached data is still up to date and
+ * does not need to be invalidated externally.
+ */
+interface FileCacheInterface {
+
+  /**
+   * Gets data based on a filename.
+   *
+   * @param string $filepath
+   *   Path of the file that the cached data is based on.
+   *
+   * @return mixed|null
+   *   The data that was persisted with set() or NULL if there is no data
+   *   or the file has been modified.
+   */
+  public function get($filepath);
+
+  /**
+   * Gets data based on filenames.
+   *
+   * @param string[] $filepaths
+   *   List of file paths used as cache identifiers.
+   *
+   * @return array
+   *   List of cached data keyed by the passed in file paths.
+   */
+  public function getMultiple(array $filepaths);
+
+  /**
+   * Stores data based on a filename.
+   *
+   * @param string $filepath
+   *   Path of the file that the cached data is based on.
+   * @param mixed $data
+   *   The data that should be cached.
+   */
+  public function set($filepath, $data);
+
+  /**
+   * Deletes data from the cache.
+   *
+   * @param string $filepath
+   *   Path of the file that the cached data is based on.
+   */
+  public function delete($filepath);
+
+}
diff --git a/core/lib/Drupal/Component/FileCache/NullFileCache.php b/core/lib/Drupal/Component/FileCache/NullFileCache.php
new file mode 100644
index 0000000..84c64cc
--- /dev/null
+++ b/core/lib/Drupal/Component/FileCache/NullFileCache.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\FileCache\NullFileCache.
+ */
+
+namespace Drupal\Component\FileCache;
+
+/**
+ * Null implementation for the file cache.
+ */
+class NullFileCache implements FileCacheInterface {
+
+  /**
+   * Constructs a FileCache object.
+   *
+   * @param string $prefix
+   *   A prefix that is used as a prefix, should be set to a secure, unique key
+   *   to prevent cache pollution by a third party.
+   * @param string $collection
+   *   A collection identifier to ensure that the same files could be cached for
+   *   different purposes without clashing.
+   * @param string|null $cache_backend_class
+   *   (optional) The class that should be used as cache backend.
+   * @param array $cache_backend_configuration
+   *   (optional) The configuration for the backend class.
+   */
+  public function __construct($prefix, $collection, $cache_backend_class = NULL, array $cache_backend_configuration = []) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($filepath) {
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMultiple(array $filepaths) {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($filepath, $data) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($filepath) {
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php b/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
index bbc4e3d..30131fd 100644
--- a/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
+++ b/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\Cache;
 
-use \Drupal\Component\Utility\Crypt;
+use Drupal\Core\Site\Settings;
 
 class ApcuBackendFactory implements CacheFactoryInterface {
 
@@ -34,7 +34,7 @@ class ApcuBackendFactory implements CacheFactoryInterface {
    *   The cache tags checksum provider.
    */
   public function __construct($root, CacheTagsChecksumInterface $checksum_provider) {
-    $this->sitePrefix = Crypt::hashBase64($root . '/' . conf_path());
+    $this->sitePrefix = Settings::getApcuPrefix('cache_backend');
     $this->checksumProvider = $checksum_provider;
   }
 
diff --git a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
index d4ae53e..77484de 100644
--- a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
+++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\DependencyInjection;
 
+use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Component\Serialization\Yaml;
 use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -34,30 +35,22 @@ class YamlFileLoader
 {
 
     /**
-     * Statically cached yaml files.
-     *
-     * Especially during tests, YAML files are re-parsed often.
-     *
-     * @var array
+     * @var \Drupal\Core\DependencyInjection\ContainerBuilder $container
      */
-    static protected $yaml = array();
+    protected $container;
 
     /**
-     * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container
+     * File cache object.
+     *
+     * @var \Drupal\Component\FileCache\FileCacheInterface
      */
-    protected $container;
+    protected $fileCache;
 
-    public function __construct(ContainerBuilder $container)
-    {
-      $this->container = $container;
-    }
 
-    /**
-     * Resets the internal cache. This method is mostly useful for tests.
-     */
-    public static function reset()
+    public function __construct(ContainerBuilder $container)
     {
-        static::$yaml = array();
+        $this->container = $container;
+        $this->fileCache = FileCacheFactory::get('container_yaml_loader');
     }
 
     /**
@@ -67,10 +60,14 @@ public static function reset()
      */
     public function load($file)
     {
-        if (!isset(static::$yaml[$file])) {
-          static::$yaml[$file] = $this->loadFile($file);
+        // Load from the file cache, fall back to loading the file.
+        // @todo Refactor this to cache parsed definition objects in
+        //   https://www.drupal.org/node/2464053
+        $content = $this->fileCache->get($file);
+        if (!$content) {
+            $content = $this->loadFile($file);
+            $this->fileCache->set($file, $content);
         }
-        $content = static::$yaml[$file];
 
         // Not supported.
         //$this->container->addResource(new FileResource($path));
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 557dfd1..f1700f3 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core;
 
+use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Component\ProxyBuilder\ProxyDumper;
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\Timer;
@@ -251,7 +252,7 @@ public static function createFromRequest(Request $request, $class_loader, $envir
         && Settings::get('class_loader_auto_detect', TRUE)
         && Settings::get('hash_salt', FALSE)
         && function_exists('apc_fetch')) {
-      $prefix = 'drupal.' . hash('sha256', 'drupal.' . Settings::getHashSalt());
+      $prefix = Settings::getApcuPrefix('class_loader');
       $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader($prefix, $class_loader);
       $class_loader->unregister();
       $apc_loader->register();
@@ -412,6 +413,28 @@ public function boot() {
     if (!$this->sitePath) {
       throw new \Exception('Kernel does not have site path set before calling boot()');
     }
+
+    // Initialize the FileCacheFactory component. We have to do it here instead
+    // of in \Drupal\Component\FileCache\FileCacheFactory because we can not use
+    // the Settings object in a component.
+    $configuration = Settings::get('file_cache');
+
+    // Provide a default configuration, if not set.
+    if (!isset($configuration['default'])) {
+      $configuration['default'] = [
+        'class' => '\Drupal\Component\FileCache\FileCache',
+        'cache_backend_class' => NULL,
+        'cache_backend_configuration' => [],
+      ];
+      // @todo Use extension_loaded('apcu') for non-testbot
+      //  https://www.drupal.org/node/2447753.
+      if (function_exists('apc_fetch')) {
+        $configuration['default']['cache_backend_class'] = '\Drupal\Component\FileCache\ApcuFileCacheBackend';
+      }
+    }
+    FileCacheFactory::setConfiguration($configuration);
+    FileCacheFactory::setPrefix(hash('sha256', 'FileCache' . Settings::get('hash_salt')));
+
     // Initialize the container.
     $this->initializeContainer();
 
@@ -1338,4 +1361,5 @@ protected function addServiceFiles($service_yamls) {
     }
     return FALSE;
   }
+
 }
diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
index 4681977..fbd8f1b 100644
--- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
+++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Extension;
 
+use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Core\Extension\Discovery\RecursiveExtensionFilterIterator;
 use Drupal\Core\Site\Settings;
 
@@ -81,6 +82,13 @@ class ExtensionDiscovery {
   protected $root;
 
   /**
+   * The file cache object.
+   *
+   * @var \Drupal\Component\FileCache\FileCacheInterface
+   */
+  protected $fileCache;
+
+  /**
    * Constructs a new ExtensionDiscovery object.
    *
    * @param string $root
@@ -88,6 +96,7 @@ class ExtensionDiscovery {
    */
   public function __construct($root) {
     $this->root = $root;
+    $this->fileCache = FileCacheFactory::get('extension_discovery');
   }
 
   /**
@@ -406,6 +415,12 @@ protected function scanDirectory($dir, $include_tests) {
       if (!preg_match(static::PHP_FUNCTION_PATTERN, $fileinfo->getBasename('.info.yml'))) {
         continue;
       }
+
+      if ($cached_extension = $this->fileCache->get($fileinfo->getPathName())) {
+        $files[$cached_extension->getType()][$key] = $cached_extension;
+        continue;
+      }
+
       // Determine extension type from info file.
       $type = FALSE;
       $file = $fileinfo->openFile('r');
@@ -441,6 +456,7 @@ protected function scanDirectory($dir, $include_tests) {
       $extension->origin = $dir;
 
       $files[$type][$key] = $extension;
+      $this->fileCache->set($fileinfo->getPathName(), $extension);
     }
     return $files;
   }
diff --git a/core/lib/Drupal/Core/Site/Settings.php b/core/lib/Drupal/Core/Site/Settings.php
index a48946a..1956836 100644
--- a/core/lib/Drupal/Core/Site/Settings.php
+++ b/core/lib/Drupal/Core/Site/Settings.php
@@ -148,4 +148,28 @@ public static function getHashSalt() {
     return $hash_salt;
   }
 
+  /**
+   * Generates a prefix for APC user cache keys.
+   *
+   * A standardized prefix is useful to allow visual inspection of an APC user
+   * cache. This also allows WebTestBase to clear all the entries during test
+   * tear down.
+   *
+   * @param $identifier
+   *   An identifier for the prefix. For example, 'class_loader' or
+   *   'cache_backend'.
+   *
+   * @return string
+   *   The prefix for APC user cache keys.
+   *
+   *  @see \Drupal\simpletest\WebTestBase::tearDown()
+   */
+  public static function getApcuPrefix($identifier) {
+    $prefix = 'drupal.';
+    if ($apc_prefix = static::get('apc_prefix')) {
+      $prefix .= $apc_prefix . '.';
+    }
+    return $prefix . $identifier . hash('sha256', $prefix . $identifier . static::getHashSalt());
+  }
+
 }
diff --git a/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php b/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php
index 956d677..e47948b 100644
--- a/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php
+++ b/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\simpletest\Tests;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
@@ -76,9 +77,11 @@ public function testInternalBrowser() {
     // to the installer.
     // @see drupal_valid_test_ua()
     // Not using File API; a potential error must trigger a PHP warning.
-    unlink($this->siteDirectory . '/.htkey');
+    rename($this->siteDirectory . '/.htkey', $this->siteDirectory . '/.moved_htkey');
     $this->drupalGet(Url::fromUri('base:core/install.php', array('external' => TRUE, 'absolute' => TRUE))->toString());
     $this->assertResponse(403, 'Cannot access install.php.');
+    // Move the .htkey back again so APC user cache clearing in tear down works.
+    rename($this->siteDirectory . '/.moved_htkey', $this->siteDirectory . '/.htkey');
   }
 
   /**
@@ -147,4 +150,23 @@ public function testTestingThroughUI() {
     }
   }
 
+  /**
+   * Tests APC user cache clearing.
+   */
+  public function testApcUserCacheClear() {
+    if (!class_exists('\APCIterator')) {
+      $this->fail('The testApcUserCacheClear test method requires APC or APCu to be installed.');
+      return;
+    }
+    $this->drupalGet('admin/config/development/testing/apc_user_cache_clear');
+    $this->assertText('drupal.' . $this->databasePrefix, 'Correct prefix has been cleared.');
+    preg_match('/^Deleted ([0-9]*) items/', $this->getTextContent(), $matches);
+    $this->assertTrue($matches[1] > 500, SafeMarkup::format('Over 500 items cleared from APC user cache. The actual number is @items.', ['@items' => $matches[1]]));
+    rename($this->siteDirectory . '/.htkey', $this->siteDirectory . '/.moved_htkey');
+    $this->drupalGet('admin/config/development/testing/apc_user_cache_clear');
+    $this->assertResponse(403, 'Cannot access admin/config/development/testing/apc_user_cache_clear.');
+    // Move the .htkey back again so APC user cache clearing in tear down works.
+    rename($this->siteDirectory . '/.moved_htkey', $this->siteDirectory . '/.htkey');
+  }
+
 }
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index de20658..0fbbbb8 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\simpletest;
 
+use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Serialization\Yaml;
 use Drupal\Component\Utility\Crypt;
@@ -834,6 +835,10 @@ protected function setUp() {
       'value' => $this->originalProfile,
       'required' => TRUE,
     );
+    $settings['settings']['apc_prefix'] = (object) array(
+      'value' => $this->databasePrefix,
+      'required' => TRUE,
+    );
     $this->writeSettings($settings);
     // Allow for test-specific overrides.
     $settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSite . '/settings.testing.php';
@@ -1078,8 +1083,9 @@ protected function setContainerParameter($name, $value) {
     $services['parameters'][$name] = $value;
     file_put_contents($filename, Yaml::encode($services));
 
-    // Clear the YML file cache.
-    YamlFileLoader::reset();
+    // Ensure that the cache is deleted for the yaml file loader.
+    $file_cache = FileCacheFactory::get('container_yaml_loader');
+    $file_cache->delete($filename);
   }
 
   /**
@@ -1218,6 +1224,10 @@ protected function refreshVariables() {
    * created by setUp(), and resets the database prefix.
    */
   protected function tearDown() {
+    // Clear APC user caches if APC or APCu is installed.
+    if (!class_exists('\APCIterator')) {
+      $this->drupalGet('admin/config/development/testing/apc_user_cache_clear');
+    }
     // Destroy the testing kernel.
     if (isset($this->kernel)) {
       $this->kernel->shutdown();
diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php
index 90c260c..9756e50 100644
--- a/core/modules/system/src/Controller/SystemController.php
+++ b/core/modules/system/src/Controller/SystemController.php
@@ -18,6 +18,7 @@
 use Drupal\Core\Url;
 use Drupal\system\SystemManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
  * Returns responses for System routes.
@@ -456,4 +457,42 @@ public static function setLinkActiveClass(array $element, array $context) {
     return $element;
   }
 
+  /**
+   * Clears the APCu cache for a Simpletest child site.
+   *
+   * @see \Drupal\simpletest\WebTestBase::tearDown()
+   */
+  public function apcClear() {
+    $prefix = drupal_valid_test_ua();
+    // Deny access to the clearing the user cache if:
+    // - APC or APCu is not installed.
+    // - no Simpletest prefix available.
+    // - non alphanumerics in the prefix.
+    if (!class_exists('\APCIterator') || !$prefix || !ctype_alnum($prefix)) {
+      throw new AccessDeniedHttpException();
+    }
+    $prefix = 'drupal.' . $prefix;
+    $total_deleted = $counter = 0;
+    // Testing on PHP 5.5 with APCu 4.0.6 has shown that using APCIterator only
+    // deletes 939 items in one go.
+    // @todo investigate and possibly file bug report against APC/APCu
+    $apc_items = new \APCIterator('user', '/^' . preg_quote($prefix, '/') . '/');
+    do {
+      $total_deleted += $apc_items->getTotalCount();
+      if (function_exists('apc_delete')) {
+        apc_delete($apc_items);
+      }
+      elseif (function_exists('apcu_delete')) {
+        apcu_delete($apc_items);
+      }
+      $counter++;
+      $apc_items = new \APCIterator('user', '/^' . preg_quote($prefix, '/') . '/');
+    } while ($apc_items->getTotalCount() != 0 && $counter < 10);
+
+    // Display useful information in Simpletest's verbose browser output.
+    print "Deleted $total_deleted items from APC user cache using $prefix prefix.";
+    // Do nothing else so the APC classloader is not rebuilt this request.
+    exit;
+  }
+
 }
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 39755b6..b25efcc 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -474,3 +474,13 @@ system.entity_autocomplete:
     selection_settings: ''
   requirements:
     _access: 'TRUE'
+
+# A route to clear APCu for Simpletest. This has to be in the system module
+# because sites under test do not have Simpletest enabled. Note that this route
+# will thrown a access denied exception if drupal_valid_test_ua() returns FALSE.
+system.apc_user_cache_clear:
+  path: '/admin/config/development/testing/apc_user_cache_clear'
+  defaults:
+    _controller: '\Drupal\system\Controller\SystemController::apcClear'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/tests/Drupal/Tests/Component/FileCache/FileCacheFactoryTest.php b/core/tests/Drupal/Tests/Component/FileCache/FileCacheFactoryTest.php
new file mode 100644
index 0000000..206de2c
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/FileCache/FileCacheFactoryTest.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Component\FileCache\FileCacheFactoryTest.
+ */
+
+namespace Drupal\Tests\Component\FileCache;
+
+use Drupal\Component\FileCache\FileCacheFactory;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Component\FileCache\FileCacheFactory
+ * @group FileCache
+ */
+class FileCacheFactoryTest extends UnitTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $settings = [
+      'collection' => 'test-23',
+      'cache_backend_class' => '\Drupal\Tests\Component\FileCache\StaticFileCacheBackend',
+      'cache_backend_configuration' => [
+        'bin' => 'dog',
+      ],
+    ];
+    $configuration = FileCacheFactory::getConfiguration();
+    if (!$configuration) {
+      $configuration = [];
+    }
+    $configuration += [ 'test_foo_settings' => $settings ];
+    FileCacheFactory::setConfiguration($configuration);
+    FileCacheFactory::setPrefix('prefix');
+  }
+
+  /**
+   * @covers ::get
+   */
+  public function testGet() {
+    $file_cache = FileCacheFactory::get('test_foo_settings', []);
+
+    // Ensure the right backend and configuration is used.
+    $filename = __DIR__ . '/Fixtures/llama-23.txt';
+    $realpath = realpath($filename);
+    $cid = 'prefix:test-23:' . $realpath;
+
+    $file_cache->set($filename, 23);
+
+    $static_cache = new StaticFileCacheBackend(['bin' => 'dog']);
+    $result = $static_cache->fetch([$cid]);
+    $this->assertNotEmpty($result);
+
+    // Cleanup static caches.
+    $file_cache->delete($filename);
+  }
+
+  /**
+   * @covers ::get
+   *
+   * @expectedException \InvalidArgumentException
+   * @expectedExceptionMessage Required prefix configuration is missing
+   */
+  public function testGetNoPrefix() {
+    FileCacheFactory::setPrefix(NULL);
+    FileCacheFactory::get('test_foo_settings', []);
+  }
+
+  /**
+   * @covers ::getConfiguration
+   * @covers ::setConfiguration
+   */
+  public function testGetSetConfiguration() {
+    $configuration = FileCacheFactory::getConfiguration();
+    $configuration['test_foo_bar'] = ['bar' => 'llama'];
+    FileCacheFactory::setConfiguration($configuration);
+    $configuration = FileCacheFactory::getConfiguration();
+    $this->assertEquals(['bar' => 'llama'], $configuration['test_foo_bar']);
+  }
+
+  /**
+   * @covers ::getPrefix
+   * @covers ::setPrefix
+   */
+  public function testGetSetPrefix() {
+    $prefix = $this->randomMachineName();
+    FileCacheFactory::setPrefix($prefix);
+    $this->assertEquals($prefix, FileCacheFactory::getPrefix());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Component/FileCache/FileCacheTest.php b/core/tests/Drupal/Tests/Component/FileCache/FileCacheTest.php
new file mode 100644
index 0000000..9e51798
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/FileCache/FileCacheTest.php
@@ -0,0 +1,147 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Component\FileCache\FileCacheTest.
+ */
+
+namespace Drupal\Tests\Component\FileCache;
+
+use Drupal\Component\FileCache\FileCache;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Component\FileCache\FileCache
+ * @group FileCache
+ */
+class FileCacheTest extends UnitTestCase {
+
+  /**
+   * FileCache object used for the tests.
+   *
+   * @var \Drupal\Component\FileCache\FileCacheInterface
+   */
+  protected $fileCache;
+
+  /**
+   * Static FileCache object used for verification of tests.
+   *
+   * @var \Drupal\Component\FileCache\FileCacheBackendInterface
+   */
+  protected $staticFileCache;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->fileCache = new FileCache('prefix', 'test', '\Drupal\Tests\Component\FileCache\StaticFileCacheBackend', ['bin' => 'llama']);
+    $this->staticFileCache = new StaticFileCacheBackend(['bin' => 'llama']);
+  }
+
+  /**
+   * @covers ::get
+   * @covers ::__construct
+   */
+  public function testGet() {
+    // Test a cache miss.
+    $result = $this->fileCache->get(__DIR__ . '/Fixtures/no-llama-42.yml');
+    $this->assertNull($result);
+
+    // Test a cache hit.
+    $filename = __DIR__ . '/Fixtures/llama-42.txt';
+    $realpath = realpath($filename);
+    $cid = 'prefix:test:' . $realpath;
+    $data = [
+      'mtime' => filemtime($realpath),
+      'filepath' => $realpath,
+      'data' => 42,
+    ];
+
+    $this->staticFileCache->store($cid, $data);
+
+    $result = $this->fileCache->get($filename);
+    $this->assertEquals(42, $result);
+
+    // Cleanup static caches.
+    $this->fileCache->delete($filename);
+  }
+
+  /**
+   * @covers ::getMultiple
+   */
+  public function testGetMultiple() {
+    // Test a cache miss.
+    $result = $this->fileCache->getMultiple([__DIR__ . '/Fixtures/no-llama-42.yml']);
+    $this->assertEmpty($result);
+
+    // Test a cache hit.
+    $filename = __DIR__ . '/Fixtures/llama-42.txt';
+    $realpath = realpath($filename);
+    $cid = 'prefix:test:' . $realpath;
+    $data = [
+      'mtime' => filemtime($realpath),
+      'filepath' => $realpath,
+      'data' => 42,
+    ];
+
+    $this->staticFileCache->store($cid, $data);
+
+    $result = $this->fileCache->getMultiple([$filename]);
+    $this->assertEquals([$filename => 42], $result);
+
+    // Test a static cache hit.
+    $file2 = __DIR__ . '/Fixtures/llama-23.txt';
+    $this->fileCache->set($file2, 23);
+
+    $result = $this->fileCache->getMultiple([$filename, $file2]);
+    $this->assertEquals([$filename => 42, $file2 => 23], $result);
+
+    // Cleanup static caches.
+    $this->fileCache->delete($filename);
+    $this->fileCache->delete($file2);
+  }
+
+  /**
+   * @covers ::set
+   */
+  public function testSet() {
+    $filename = __DIR__ . '/Fixtures/llama-23.txt';
+    $realpath = realpath($filename);
+    $cid = 'prefix:test:' . $realpath;
+    $data = [
+      'mtime' => filemtime($realpath),
+      'filepath' => $realpath,
+      'data' => 23,
+    ];
+
+    $this->fileCache->set($filename, 23);
+    $result = $this->staticFileCache->fetch([$cid]);
+    $this->assertEquals([$cid => $data], $result);
+
+    // Cleanup static caches.
+    $this->fileCache->delete($filename);
+  }
+
+  /**
+   * @covers ::delete
+   */
+  public function testDelete() {
+    $filename = __DIR__ . '/Fixtures/llama-23.txt';
+    $realpath = realpath($filename);
+    $cid = 'prefix:test:' . $realpath;
+
+    $this->fileCache->set($filename, 23);
+
+    // Ensure data is removed after deletion.
+    $this->fileCache->delete($filename);
+
+    $result = $this->staticFileCache->fetch([$cid]);
+    $this->assertEquals([], $result);
+
+    $result = $this->fileCache->get($filename);
+    $this->assertNull($result);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Component/FileCache/Fixtures/llama-23.txt b/core/tests/Drupal/Tests/Component/FileCache/Fixtures/llama-23.txt
new file mode 100644
index 0000000..4099407
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/FileCache/Fixtures/llama-23.txt
@@ -0,0 +1 @@
+23
diff --git a/core/tests/Drupal/Tests/Component/FileCache/Fixtures/llama-42.txt b/core/tests/Drupal/Tests/Component/FileCache/Fixtures/llama-42.txt
new file mode 100644
index 0000000..d81cc07
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/FileCache/Fixtures/llama-42.txt
@@ -0,0 +1 @@
+42
diff --git a/core/tests/Drupal/Tests/Component/FileCache/StaticFileCacheBackend.php b/core/tests/Drupal/Tests/Component/FileCache/StaticFileCacheBackend.php
new file mode 100644
index 0000000..ed82b81
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/FileCache/StaticFileCacheBackend.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Component\FileCache\StaticFileCacheBackend.
+ */
+
+namespace Drupal\Tests\Component\FileCache;
+
+use Drupal\Component\FileCache\FileCacheBackendInterface;
+
+/**
+ * Allows to cache data based on file modification dates in a static cache.
+ */
+class StaticFileCacheBackend implements FileCacheBackendInterface {
+
+  /**
+   * Internal static cache.
+   *
+   * @var array
+   */
+  protected static $cache = [];
+
+  /**
+   * Bin used for storing the data in the static cache.
+   *
+   * @var string
+   */
+  protected $bin;
+
+  /**
+   * Constructs a PHP Storage FileCache backend.
+   *
+   * @param array $configuration
+   *   (optional) Configuration used to configure this object.
+   */
+  public function __construct($configuration) {
+    $this->bin = isset($configuration['bin']) ? $configuration['bin'] : 'file_cache';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetch(array $cids) {
+    $result = [];
+    foreach ($cids as $cid) {
+      if (isset(static::$cache[$this->bin][$cid])) {
+        $result[$cid] = static::$cache[$this->bin][$cid];
+      }
+    }
+
+    return $result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function store($cid, $data) {
+    static::$cache[$this->bin][$cid] = $data;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($cid) {
+    unset(static::$cache[$this->bin][$cid]);
+  }
+
+  /**
+   * Allows tests to reset the static cache to avoid side effects.
+   */
+  public static function reset() {
+    static::$cache = [];
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index c15bff2..9e70a73 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests;
 
+use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Component\Utility\Random;
 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
@@ -41,6 +42,10 @@ protected function setUp() {
     // a previous test does not leak into this test.
     \Drupal::unsetContainer();
 
+    // Ensure that the NullFileCache implementation is used for the FileCache as
+    // unit tests should not be relying on caches implicitly.
+    FileCacheFactory::setConfiguration(['default' => ['class' => '\Drupal\Component\FileCache\NullFileCache']]);
+
     $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
   }
 
