diff --git a/core/includes/form.inc b/core/includes/form.inc
index 27f450e..9606ee2 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -530,14 +530,12 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
  * Fetches a form from the cache.
  */
 function form_get_cache($form_build_id, &$form_state) {
-  if ($cached = cache('form')->get('form_' . $form_build_id)) {
-    $form = $cached->data;
-
+  if ($form = Drupal::keyValueExpirable('form')->get($form_build_id)) {
     global $user;
     if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {
-      if ($cached = cache('form')->get('form_state_' . $form_build_id)) {
+      if ($stored_form_state = Drupal::keyValueExpirable('breaking_all_the_things_for_xjm')->get($form_build_id)) {
         // Re-populate $form_state for subsequent rebuilds.
-        $form_state = $cached->data + $form_state;
+        $form_state = $stored_form_state + $form_state;
 
         // If the original form is contained in include files, load the files.
         // @see form_load_include()
@@ -569,12 +567,12 @@ function form_set_cache($form_build_id, $form, $form_state) {
     if ($GLOBALS['user']->uid) {
       $form['#cache_token'] = drupal_get_token();
     }
-    cache('form')->set('form_' . $form_build_id, $form, REQUEST_TIME + $expire);
+    Drupal::keyValueExpirable('form')->setWithExpire($form_build_id, $form, $expire);
   }
 
   // Cache form state.
   if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
-    cache('form')->set('form_state_' . $form_build_id, $data, REQUEST_TIME + $expire);
+    Drupal::keyValueExpirable('form_state')->setWithExpire($form_build_id, $data, $expire);
   }
 }
 
@@ -916,15 +914,6 @@ function drupal_process_form($form_id, &$form, &$form_state) {
       // Execute form submit handlers.
       form_execute_handlers('submit', $form, $form_state);
 
-      // We'll clear out the cached copies of the form and its stored data
-      // here, as we've finished with them. The in-memory copies are still
-      // here, though.
-      $config = config('system.performance');
-      if (!$config->get('cache.page.use_internal') && !empty($form_state['values']['form_build_id'])) {
-        cache('form')->delete('form_' . $form_state['values']['form_build_id']);
-        cache('form')->delete('form_state_' . $form_state['values']['form_build_id']);
-      }
-
       // If batches were set in the submit handlers, we process them now,
       // possibly ending execution. We make sure we do not react to the batch
       // that is already being processed (if a batch operation performs a
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 5bde077..786c90d 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -374,6 +374,14 @@ function install_begin_request(&$install_state) {
       ))
       ->addMethodCall('setUserAgent', array('Drupal (+http://drupal.org/)'));
 
+    // Register the expirable key value store used by form cache.
+    $container
+      ->register('keyvalue.expirable', 'Drupal\Core\KeyValueStore\KeyValueExpirableFactory')
+      ->addArgument(new Reference('service_container'));
+    $container
+      ->register('keyvalue.expirable.null', 'Drupal\Core\KeyValueStore\KeyValueNullExpirableFactory');
+    $conf['keyvalue_expirable_default'] = 'keyvalue.expirable.null';
+
     Drupal::setContainer($container);
   }
 
diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueNullExpirableFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueNullExpirableFactory.php
new file mode 100644
index 0000000..27310da
--- /dev/null
+++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueNullExpirableFactory.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\KeyValueStore\KeyValueNullExpirableFactory.
+ */
+
+namespace Drupal\Core\KeyValueStore;
+
+/**
+ * Defines the key/value store factory for the null backend.
+ */
+class KeyValueNullExpirableFactory {
+
+  /**
+   * Constructs a new key/value expirable null storage object for a given
+   * collection name.
+   *
+   * @param string $collection
+   *   The name of the collection holding key and value pairs.
+   *
+   * @return \Drupal\Core\KeyValueStore\DatabaseStorageExpirable
+   *   A key/value store implementation for the given $collection.
+   */
+  public function get($collection) {
+    return new NullStorageExpirable($collection);
+  }
+}
diff --git a/core/lib/Drupal/Core/KeyValueStore/NullStorageExpirable.php b/core/lib/Drupal/Core/KeyValueStore/NullStorageExpirable.php
new file mode 100644
index 0000000..d66ab59
--- /dev/null
+++ b/core/lib/Drupal/Core/KeyValueStore/NullStorageExpirable.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\KeyValueStore\NullStorageExpirable.
+ */
+
+namespace Drupal\Core\KeyValueStore;
+
+/**
+ * Defines a null key/value store implementation.
+ */
+class NullStorageExpirable implements KeyValueStoreExpirableInterface {
+
+  /**
+   * The actual storage of key-value pairs.
+   *
+   * @var array
+   */
+  protected $data = array();
+
+  /**
+   * The name of the collection holding key and value pairs.
+   *
+   * @var string
+   */
+  protected $collection;
+
+  /**
+   * Creates a new expirable null key/value store.
+   */
+  public function __construct($collection) {
+    $this->collection = $collection;
+  }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::get().
+   */
+  public function get($key) {
+    return NULL;
+  }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getMultiple().
+   */
+  public function getMultiple(array $keys) {
+    return array();
+  }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getAll().
+   */
+  public function getAll() {
+    return array();
+  }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::set().
+   */
+  public function set($key, $value) { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::setIfNotExists().
+   */
+  public function setIfNotExists($key, $value) { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::setMultiple().
+   */
+  public function setMultiple(array $data) { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::delete().
+   */
+  public function delete($key) { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::deleteMultiple().
+   */
+  public function deleteMultiple(array $keys) { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::deleteAll().
+   */
+  public function deleteAll() { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getCollectionName().
+   */
+  public function getCollectionName() {
+    return $this->collection;
+  }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface::setMultipleWithExpire().
+   */
+  public function setMultipleWithExpire(array $data, $expire) { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface::setWithExpire().
+   */
+  public function setWithExpire($key, $value, $expire) { }
+
+  /**
+   * Implements Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface::setWithExpireIfNotExists().
+   */
+  public function setWithExpireIfNotExists($key, $value, $expire) { }
+
+}
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index e066efb..2fdf86f 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -684,8 +684,6 @@ function system_schema() {
   $schema['cache_bootstrap']['description'] = 'Cache table for data required to bootstrap Drupal, may be routed to a shared memory cache.';
   $schema['cache_config'] = $schema['cache'];
   $schema['cache_config']['description'] = 'Cache table for configuration data.';
-  $schema['cache_form'] = $schema['cache'];
-  $schema['cache_form']['description'] = 'Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.';
   $schema['cache_page'] = $schema['cache'];
   $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
   $schema['cache_menu'] = $schema['cache'];
@@ -2158,6 +2156,13 @@ function system_update_8051() {
 }
 
 /**
+ * Remove {cache_form} table.
+ */
+function system_update_8052() {
+  db_drop_table('cache_form');
+}
+
+/**
  * @} End of "defgroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
  */
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 13469b0..33e7fb5 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -3549,7 +3549,7 @@ function system_cron() {
   // Cleanup the flood.
   Drupal::service('flood')->garbageCollection();
 
-  $cache_bins = array_merge(module_invoke_all('cache_flush'), array('form', 'menu'));
+  $cache_bins = array_merge(module_invoke_all('cache_flush'), array('menu'));
   foreach ($cache_bins as $bin) {
     cache($bin)->garbageCollection();
   }
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php
index f0f4814..1b398e9 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php
@@ -38,7 +38,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('system', array('menu_router', 'variable'));
+    $this->installSchema('system', array('menu_router', 'variable', 'key_value_expire'));
   }
 
   function viewsData() {
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php
index fb154c1..0c1f32a 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php
@@ -39,7 +39,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('system', array('menu_router', 'variable'));
+    $this->installSchema('system', array('menu_router', 'variable', 'key_value_expire'));
   }
 
   function viewsData() {
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php
index e12c94a..a82d23f 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php
@@ -39,7 +39,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('system', array('menu_router', 'variable'));
+    $this->installSchema('system', array('menu_router', 'variable', 'key_value_expire'));
   }
 
   function viewsData() {
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php
index 57489bb..a573108 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php
@@ -38,7 +38,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('system', array('menu_router', 'variable'));
+    $this->installSchema('system', array('menu_router', 'variable', 'key_value_expire'));
   }
 
   function viewsData() {
