diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index 4c56bff..9249f8f 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -124,7 +124,7 @@ function get_base_table() {
         $relationships = $this->view->display_handler->getOption('relationships');
         if (!empty($relationships[$this->options['relationship']])) {
           $options = $relationships[$this->options['relationship']];
-          $data = views_fetch_data($options['table']);
+          $data = drupal_container()->get('views.views_data')->get($options['table']);
           $this->base_table = $data[$options['field']]['relationship']['base'];
         }
       }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
index 75ad533..c7cb38d 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
@@ -36,7 +36,7 @@ public function query() {
     $this->ensureMyTable();
     // First, relate our base table to the current base table to the
     // field, using the base table's id field to the field's column.
-    $views_data = views_fetch_data($this->table);
+    $views_data = drupal_container()->get('views.views_data')->get($this->table);
     $left_field = $views_data['table']['base']['field'];
 
     $first = array(
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
index 6056996..00c48d9 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
@@ -71,9 +71,6 @@ function setUp() {
       );
       $this->nodes[] = $this->drupalCreateNode($edit);
     }
-
-    // Reset views data cache.
-    $this->clearViewsCaches();
   }
 
   /**
@@ -82,7 +79,7 @@ function setUp() {
    * We check data structure for both node and node revision tables.
    */
   function testViewsData() {
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
 
     // Check the table and the joins of the first field.
     // Attached to node only.
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
index d63272a..c0f500b 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
@@ -74,14 +74,4 @@ function setUpInstances($bundle = 'page') {
     }
   }
 
-  /**
-   * Clear all views caches and static caches which are required for the patch.
-   */
-  function clearViewsCaches() {
-    // Reset views data cache.
-    drupal_static_reset('_views_fetch_data_cache');
-    drupal_static_reset('_views_fetch_data_recursion_protected');
-    drupal_static_reset('_views_fetch_data_fully_loaded');
-  }
-
 }
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
index caacce2..3f5aea2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
@@ -52,8 +52,6 @@ protected function setUp() {
 
     $this->setUpInstances();
 
-    $this->clearViewsCaches();
-
     // Create some nodes.
     $this->nodes = array();
     for ($i = 0; $i < 3; $i++) {
diff --git a/core/modules/views/includes/cache.inc b/core/modules/views/includes/cache.inc
deleted file mode 100644
index 135787f..0000000
--- a/core/modules/views/includes/cache.inc
+++ /dev/null
@@ -1,158 +0,0 @@
-<?php
-
-/**
- * @file
- * Load Views' data so that it knows what is available to build queries from.
- */
-
-/**
- * Fetch Views' data from the cache.
- *
- * $param string|null $table
- *   (optional) The name of the table for which to fetch Views' data. If
- *   NULL, data for all tables will be retrieved
- * @param bool $reset
- *   (optional) Whether to rebuild the cache. Defaults to FALSE.
- *
- * @return array
- *   An associative array of views data for the given table. If $table is
- *   NULL, the array will be keyed by table name, with each key corresponding
- *   to the data array for that table.
- *
- * @see hook_views_data()
- */
-function _views_fetch_data($table = NULL, $reset = FALSE) {
-  $cache = &drupal_static(__FUNCTION__ . '_cache');
-  $recursion_protection = &drupal_static(__FUNCTION__ . '_recursion_protected');
-  $fully_loaded = &drupal_static(__FUNCTION__ . '_fully_loaded');
-  if ($reset) {
-    $cache = NULL;
-    $fully_loaded = FALSE;
-  }
-  if ($table) {
-    if (!isset($cache[$table])) {
-      $cid = 'views_data:' . $table;
-      $data = views_cache_get($cid, TRUE);
-      if (!empty($data->data)) {
-        $cache[$table] = $data->data;
-      }
-      else {
-        // No cache entry, rebuild.
-        $cache = _views_fetch_data_build();
-        $fully_loaded = TRUE;
-      }
-    }
-    if (isset($cache[$table])) {
-      return $cache[$table];
-    }
-  }
-  else {
-    if (!$fully_loaded) {
-      $data = views_cache_get('views_data', TRUE);
-      if (!empty($data->data)) {
-        $cache = $data->data;
-      }
-
-      if (empty($cache)) {
-        $cache = _views_fetch_data_build();
-      }
-      $fully_loaded = TRUE;
-    }
-    return $cache;
-  }
-  // Return an empty array if there is no match.
-  return array();
-}
-
-/**
- * Build, set the views data cache if empty and return the views data.
- *
- * @return array
- *   The views_data of all tables.
- */
-function _views_fetch_data_build() {
-  $cache = module_invoke_all('views_data');
-  foreach (module_implements('views_data_alter') as $module) {
-    $function = $module . '_views_data_alter';
-    $function($cache);
-  }
-  _views_data_process_entity_types($cache);
-
-  // Keep a record with all data.
-  views_cache_set('views_data', $cache, TRUE);
-  // Save data in seperate cache entries.
-  foreach ($cache as $key => $data) {
-    $cid = 'views_data:' . $key;
-    views_cache_set($cid, $data, TRUE);
-  }
-  return $cache;
-}
-
-/**
- * Links tables having an 'entity type' specified to the respective generic entity-type tables.
- */
-function _views_data_process_entity_types(&$data) {
-  foreach ($data as $table_name => $table_info) {
-    // Add in a join from the entity-table if an entity-type is given.
-    if (!empty($table_info['table']['entity type'])) {
-      $entity_table = 'views_entity_' . $table_info['table']['entity type'];
-
-      $data[$entity_table]['table']['join'][$table_name] = array(
-        'left_table' => $table_name,
-      );
-      $data[$entity_table]['table']['entity type'] = $table_info['table']['entity type'];
-      // Copy over the default table group if we have none yet.
-      if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) {
-        $data[$entity_table]['table']['group'] = $table_info['table']['group'];
-      }
-    }
-  }
-}
-
-/**
- * Set a cached item in the views cache.
- *
- * This is just a convenience wrapper around cache_set().
- *
- * @param $cid
- *   The cache ID of the data to store.
- * @param $data
- *   The data to store in the cache. Complex data types will be automatically serialized before insertion.
- *   Strings will be stored as plain text and not serialized.
- * @param $use_language
- *   If TRUE, the data will be cached specific to the currently active language.
- */
-function views_cache_set($cid, $data, $use_language = FALSE) {
-  if (config('views.settings')->get('skip_cache')) {
-    return;
-  }
-  if ($use_language) {
-    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
-  }
-
-  cache('views_info')->set($cid, $data);
-}
-
-/**
- * Return data from the persistent views cache.
- *
- * This is just a convenience wrapper around cache_get().
- *
- * @param int $cid
- *   The cache ID of the data to retrieve.
- * @param bool $use_language
- *   If TRUE, the data will be requested specific to the currently active language.
- *
- * @return stdClass|bool
- *   The cache or FALSE on failure.
- */
-function views_cache_get($cid, $use_language = FALSE) {
-  if (config('views.settings')->get('skip_cache')) {
-    return FALSE;
-  }
-  if ($use_language) {
-    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
-  }
-
-  return cache('views_info')->get($cid);
-}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Type/DefaultWizardDeriver.php b/core/modules/views/lib/Drupal/views/Plugin/Type/DefaultWizardDeriver.php
index 864dab1..4af19bb 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Type/DefaultWizardDeriver.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Type/DefaultWizardDeriver.php
@@ -38,7 +38,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
     $base_tables = array_keys(views_fetch_base_tables());
     $this->derivatives = array();
     foreach ($base_tables as $table) {
-      $views_info = views_fetch_data($table);
+      $views_info = drupal_container()->get('views.views_data')->get($table);
       if (empty($views_info['table']['wizard_id'])) {
         $this->derivatives[$table] = array(
           'id' => 'standard',
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
index ef039c8..914cc09 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
@@ -775,7 +775,7 @@ public static function getTimezone() {
    * @return Drupal\views\Plugin\views\join\JoinPluginBase
    */
   public static function getTableJoin($table, $base_table) {
-    $data = views_fetch_data($table);
+    $data = drupal_container()->get('views.views_data')->get($table);
     if (isset($data['table']['join'][$base_table])) {
       $join_info = $data['table']['join'][$base_table];
       if (!empty($join_info['join_id'])) {
@@ -820,10 +820,10 @@ public function getEntityType() {
     // If the user has configured a relationship on the handler take that into
     // account.
     if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') {
-      $views_data = views_fetch_data($this->view->relationship->table);
+      $views_data = drupal_container()->get('views.views_data')->get($this->view->relationship->table);
     }
     else {
-      $views_data = views_fetch_data($this->view->storage->get('base_table'));
+      $views_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
     }
 
     if (isset($views_data['table']['entity type'])) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 05295e5..27b51c2 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -776,7 +776,7 @@ public function getPlugin($type) {
 
     // Query plugins allow specifying a specific query class per base table.
     if ($type == 'query') {
-      $views_data = views_fetch_data($this->view->storage->get('base_table'));
+      $views_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
       $name = isset($views_data['table']['base']['query_id']) ? $views_data['table']['base']['query_id'] : 'views_query';
     }
 
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
index 103ea43..db664a5 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
@@ -1442,7 +1442,7 @@ function execute(ViewExecutable $view) {
     $count_query->addMetaData('view', $view);
 
     if (empty($this->options['disable_sql_rewrite'])) {
-      $base_table_data = views_fetch_data($this->view->storage->get('base_table'));
+      $base_table_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
       if (isset($base_table_data['table']['base']['access query tag'])) {
         $access_tag = $base_table_data['table']['base']['access query tag'];
         $query->addTag($access_tag);
@@ -1540,7 +1540,7 @@ function execute(ViewExecutable $view) {
   function get_entity_tables() {
     // Start with the base table.
     $entity_tables = array();
-    $base_table_data = views_fetch_data($this->view->storage->get('base_table'));
+    $base_table_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
     if (isset($base_table_data['table']['entity type'])) {
       $entity_tables[$this->view->storage->get('base_table')] = array(
         'base' => $this->view->storage->get('base_table'),
@@ -1551,7 +1551,7 @@ function get_entity_tables() {
     }
     // Include all relationships.
     foreach ($this->view->relationship as $relationship_id => $relationship) {
-      $table_data = views_fetch_data($relationship->definition['base']);
+      $table_data = drupal_container()->get('views.views_data')->get($relationship->definition['base']);
       if (isset($table_data['table']['entity type'])) {
         $entity_tables[$relationship->alias] = array(
           'base' => $relationship->definition['base'],
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
index f3a1f4d..0b085c6 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
@@ -93,7 +93,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     foreach ($sorts as $sort_id => $sort) {
       $sort_options[$sort_id] = "$sort[group]: $sort[title]";
     }
-    $base_table_data = views_fetch_data($this->definition['base']);
+    $base_table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
 
     $form['subquery_sort'] = array(
       '#type' => 'select',
@@ -219,7 +219,7 @@ function left_query($options) {
     $temp_view->args[] = '**CORRELATED**';
 
     // Add the base table ID field.
-    $views_data = views_fetch_data($this->definition['base']);
+    $views_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
     $base_field = $views_data['table']['base']['field'];
     $temp_view->addItem('default', 'field', $this->definition['base'], $this->definition['field']);
 
@@ -341,7 +341,7 @@ function condition_namespace($string) {
    */
   public function query() {
     // Figure out what base table this relationship brings to the party.
-    $table_data = views_fetch_data($this->definition['base']);
+    $table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
     $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
 
     $this->ensureMyTable();
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php
index f1641aa..f960c9c 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php
@@ -116,7 +116,7 @@ public function buildOptionsForm(&$form, &$form_state) {
    */
   public function query() {
     // Figure out what base table this relationship brings to the party.
-    $table_data = views_fetch_data($this->definition['base']);
+    $table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
     $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
 
     $this->ensureMyTable();
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
index c46071f..96c2781 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
@@ -85,7 +85,7 @@ public function buildOptionsForm(&$form, &$form_state) {
         $relationship_handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
 
         // If this relationship is valid for this type, add it to the list.
-        $data = views_fetch_data($relationship['table']);
+        $data = drupal_container()->get('views.views_data')->get($relationship['table']);
         $base = $data[$relationship['field']]['relationship']['base'];
         if ($base == $this->base_table) {
           $relationship_handler->init($executable, $relationship);
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
index 609b34c..28cb722 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
@@ -754,7 +754,7 @@ protected function default_display_options() {
     // Add a least one field so the view validates and the user has a preview.
     // The base field can provide a default in its base settings; otherwise,
     // choose the first field with a field handler.
-    $data = views_fetch_data($this->base_table);
+    $data = drupal_container()->get('views.views_data')->get($this->base_table);
     if (isset($data['table']['base']['defaults']['field'])) {
       $field = $data['table']['base']['defaults']['field'];
     }
@@ -835,7 +835,7 @@ protected function default_display_filters_user(array $form, array &$form_state)
           }
         }
       }
-      $table_data = views_fetch_data($table);
+      $table_data = drupal_container()->get('views.views_data')->get($table);
       // If the 'in' operator is being used, map the values to an array.
       $handler = $table_data[$bundle_key]['filter']['id'];
       $handler_definition = drupal_container()->get('plugin.manager.views.filter')->getDefinition($handler);
@@ -921,7 +921,7 @@ protected function default_display_sorts_user($form, $form_state) {
       // created from node, but the wizard type is another base table, make
       // sure it is not added. This usually don't happen if you have js
       // enabled.
-      $data = views_fetch_data($table);
+      $data = drupal_container()->get('views.views_data')->get($table);
       if (isset($data[$column]['sort'])) {
         $sorts[$column] = array(
           'id' => $column,
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php
index 85861cd..7be896a 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php
@@ -53,7 +53,7 @@ public static function getInfo() {
    */
   public function testHandlers() {
     $object_types = array_keys(ViewExecutable::viewsHandlerTypes());
-    foreach (views_fetch_data() as $base_table => $info) {
+    foreach (drupal_container()->get('views.views_data')->get() as $base_table => $info) {
       if (!isset($info['table']['base'])) {
         continue;
       }
diff --git a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
index 9034e75..5ecedec 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
@@ -219,7 +219,7 @@ protected function formatViewOptions(array $views = array()) {
    * Ensure that a certain handler is a instance of a certain table/field.
    */
   function assertInstanceHandler($handler, $table, $field, $id) {
-    $table_data = views_fetch_data($table);
+    $table_data = drupal_container()->get('views.views_data')->get($table);
     $field_data = $table_data[$field][$id];
 
     $this->assertEqual($field_data['id'], $handler->getPluginId());
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
index d86bd81..031a949 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
@@ -31,16 +31,12 @@ public function testViewsFetchData() {
     $table_name = 'views_test_data';
     $expected_data = $this->viewsData();
 
-    $data = views_fetch_data($table_name);
+    $data = drupal_container()->get('views.views_data')->get($table_name);
     $this->assertEqual($data, $expected_data[$table_name], 'Make sure fetching views data by table works as expected.');
 
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
     $this->assertTrue(isset($data[$table_name]), 'Make sure the views_test_data info appears in the total views data.');
     $this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.');
-
-    $data = views_fetch_data(NULL, TRUE);
-    $this->assertTrue(isset($data[$table_name]), 'Make sure the views_fetch_data appears in the total views data with reset = TRUE.');
-    $this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.');
   }
 
   /**
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index fdc4ce2..ab0a0d6 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -960,7 +960,7 @@ public function initQuery() {
     }
 
     // Create and initialize the query object.
-    $views_data = views_fetch_data($this->storage->get('base_table'));
+    $views_data = drupal_container()->get('views.views_data')->get($this->storage->get('base_table'));
     $this->storage->set('base_field', !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '');
     if (!empty($views_data['table']['base']['database'])) {
       $this->base_database = $views_data['table']['base']['database'];
diff --git a/core/modules/views/lib/Drupal/views/ViewsBundle.php b/core/modules/views/lib/Drupal/views/ViewsBundle.php
index 7a85ff0..050b997 100644
--- a/core/modules/views/lib/Drupal/views/ViewsBundle.php
+++ b/core/modules/views/lib/Drupal/views/ViewsBundle.php
@@ -9,6 +9,7 @@
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\Reference;
 use Drupal\views\ViewExecutable;
 
 /**
@@ -32,6 +33,16 @@ public function build(ContainerBuilder $container) {
           ->addArgument($type);
       }
     }
+
+    $container
+      ->register('cache.views_info', 'Drupal\Core\Cache\CacheBackendInterface')
+      ->setFactoryClass('Drupal\Core\Cache\CacheFactory')
+      ->setFactoryMethod('get')
+      ->addArgument('views_info');
+
+    $container->register('views.views_data', 'Drupal\views\ViewsDataCache')
+      ->addArgument(new Reference('cache.views_info'))
+      ->addArgument(new Reference('config.factory'));
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/ViewsDataCache.php b/core/modules/views/lib/Drupal/views/ViewsDataCache.php
new file mode 100644
index 0000000..684e1f2
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.php
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\ViewsDataCache.
+ */
+
+namespace Drupal\views;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Cache\CacheBackendInterface;
+
+/**
+ * Class to manage and lazy load cached views data.
+ */
+class ViewsDataCache {
+
+  /**
+   * The base cache ID to use.
+   *
+   * @var string
+   */
+  protected $baseCid = 'views_data';
+
+  /**
+   * This views cache bin to use.
+   *
+   * @var string
+   */
+  protected $bin;
+
+  /**
+   * Storage for the data itself.
+   *
+   * @var array
+   */
+  protected $storage = array();
+
+  /**
+   * The configuration factory object.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $config;
+
+  /**
+   * The current language code.
+   *
+   * @var string
+   */
+  protected $langcode;
+
+  /**
+   * Whether the data has been fully loaded in this request.
+   *
+   * @var bool
+   */
+  protected $fullyLoaded = FALSE;
+
+  /**
+   * Whether or not to skip data caching and rebuild data each time.
+   *
+   * @var bool
+   */
+  protected $skipCache;
+
+  /**
+   * Whether the cache should be rebuilt. This is set when getData() is called.
+   *
+   * @var bool
+   */
+  protected $rebuildCache;
+
+  public function __construct(CacheBackendInterface $bin, ConfigFactory $config) {
+    $this->config = $config;
+    $this->bin = $bin;
+
+    $this->langcode = language(LANGUAGE_TYPE_INTERFACE)->langcode;
+    $this->skipCache = $this->config->get('views.settings')->get('skip_cache');
+  }
+
+  /**
+   * Gets cached data for a particular key, or rebuilds if necessary.
+   *
+   * @param string $key
+   *   The key of the cache entry to retrieve.
+   *
+   * @return array $data
+   *   The cached data.
+   */
+  public function get($key = NULL) {
+    if ($key) {
+      if (!isset($this->storage[$key])) {
+        $cid = $this->baseCid . ':' . $key;
+        $data = $this->cacheGet($cid);
+        if (!empty($data->data)) {
+          $this->storage[$key] = $data->data;
+        }
+        else {
+          // No cache entry, rebuild.
+          $this->storage = $this->getData();
+          $this->fullyLoaded = TRUE;
+        }
+      }
+      if (isset($this->storage[$key])) {
+        return $this->storage[$key];
+      }
+    }
+    else {
+      if (!$this->fullyLoaded) {
+        $data = $this->cacheGet($this->baseCid);
+        if (!empty($data->data)) {
+          $this->storage = $data->data;
+        }
+        else {
+          $this->storage = $this->getData();
+        }
+        $this->fullyLoaded = TRUE;
+      }
+    }
+
+    // Return an empty array if there is no match.
+    return $this->storage;
+  }
+
+  /**
+   * Sets the data in the cache in for a cache key.
+   *
+   * @param string $key
+   *   The cache key to set.
+   * @param mixed $value
+   *   The value to set for this key.
+   */
+  public function set($key, $value) {
+    if ($this->skipCache) {
+      return FALSE;
+    }
+
+    $key .= ':' . $this->langcode;
+
+    $this->bin->set($key, $value);
+  }
+
+  /**
+   * Gets data from the cache bin.
+   *
+   * @param string $cid
+   *   The cache ID to return.
+   *
+   * @return mixed
+   *   The cached data, if any. This will immediately return FALSE if the
+   *   $skipCache property is TRUE.
+   */
+  protected function cacheGet($cid) {
+    if ($this->skipCache) {
+      return FALSE;
+    }
+
+    $cid .= ':' . $this->langcode;
+
+    return $this->bin->get($cid);
+  }
+
+  /**
+   * Gets all data invoked by hook_views_data.
+   *
+   * @return array
+   *   An array of all data.
+   */
+  protected function getData() {
+    $data = module_invoke_all('views_data');
+    drupal_alter('views_data', $data);
+
+    $this->processEntityTypes($data);
+
+    $this->rebuildCache = TRUE;
+
+    return $data;
+  }
+
+  /**
+   * Links tables with 'entity type' to respective generic entity-type tables.
+   *
+   * @param array $data
+   *   The array of data to alter entity data for, passed by reference.
+   */
+  protected function processEntityTypes(array &$data) {
+    foreach ($data as $table_name => $table_info) {
+      // Add in a join from the entity-table if an entity-type is given.
+      if (!empty($table_info['table']['entity type'])) {
+        $entity_table = 'views_entity_' . $table_info['table']['entity type'];
+
+        $data[$entity_table]['table']['join'][$table_name] = array(
+          'left_table' => $table_name,
+        );
+        $data[$entity_table]['table']['entity type'] = $table_info['table']['entity type'];
+        // Copy over the default table group if we have none yet.
+        if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) {
+          $data[$entity_table]['table']['group'] = $table_info['table']['group'];
+        }
+      }
+    }
+  }
+
+  /**
+   * Destructs the ViewDataCache object.
+   */
+  public function __destruct() {
+    if ($this->rebuildCache && !empty($this->storage)) {
+      // Keep a record with all data.
+      $this->set($this->baseCid, $this->storage);
+      // Save data in seperate cache entries.
+      foreach ($this->storage as $table => $data) {
+        $cid = $this->baseCid . ':' . $table;
+        $this->set($cid, $data);
+      }
+    }
+  }
+
+}
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index 5906e76..6e9dae8 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -101,7 +101,7 @@
  * Describe data tables (or the equivalent) to Views.
  *
  * The data described with this hook is fetched and retrieved by
- * views_fetch_data().
+ * drupal_container()->get('views.views_data')->get().
  *
  * @return array
  *   An associative array describing the data structure. Primary key is the
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 514a78a..00fccb4 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -1154,8 +1154,8 @@ function views_library_info() {
 function views_get_handler($table, $field, $type, $override = NULL) {
   // Get the plugin manager for this type.
   $manager = drupal_container()->get("plugin.manager.views.$type");
+  $data = drupal_container()->get('views.views_data')->get($table);
 
-  $data = views_fetch_data($table);
   if (isset($data[$field][$type])) {
     $definition = $data[$field][$type];
     foreach (array('group', 'title', 'title short', 'help', 'real field', 'real table') as $key) {
@@ -1194,27 +1194,6 @@ function views_get_handler($table, $field, $type, $override = NULL) {
 }
 
 /**
- * Fetch Views' data from the cache
- *
- * $param string|null $table
- *   (optional) The name of the table for which to fetch Views' data. If
- *   NULL, data for all tables will be retrieved
- * @param bool $reset
- *   (optional) Whether to rebuild the cache. Defaults to FALSE.
- *
- * @return array
- *   An associative array of views data for the given table. If $table is
- *   NULL, the array will be keyed by table name, with each key corresponding
- *   to the data array for that table.
- *
- * @see hook_views_data()
- */
-function views_fetch_data($table = NULL, $reset = FALSE) {
-  views_include('cache');
-  return _views_fetch_data($table, $reset);
-}
-
-/**
  * Fetch a list of all base tables available
  *
  * @return array
@@ -1224,7 +1203,7 @@ function views_fetch_base_tables() {
   static $base_tables = array();
   if (empty($base_tables)) {
     $tables = array();
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
     foreach ($data as $table => $info) {
       if (!empty($info['table']['base'])) {
         $tables[$table] = array(
@@ -2208,3 +2187,51 @@ function views_config_import_delete($name, $new_config, $old_config) {
   entity_delete($view);
   return TRUE;
 }
+
+/**
+ * Set a cached item in the views cache.
+ *
+ * This is just a convenience wrapper around cache_set().
+ *
+ * @param $cid
+ *   The cache ID of the data to store.
+ * @param $data
+ *   The data to store in the cache. Complex data types will be automatically serialized before insertion.
+ *   Strings will be stored as plain text and not serialized.
+ * @param $use_language
+ *   If TRUE, the data will be cached specific to the currently active language.
+ */
+function views_cache_set($cid, $data, $use_language = FALSE) {
+  if (config('views.settings')->get('skip_cache')) {
+    return;
+  }
+  if ($use_language) {
+    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+  }
+
+  cache('views_info')->set($cid, $data);
+}
+
+/**
+ * Return data from the persistent views cache.
+ *
+ * This is just a convenience wrapper around cache_get().
+ *
+ * @param int $cid
+ *   The cache ID of the data to retrieve.
+ * @param bool $use_language
+ *   If TRUE, the data will be requested specific to the currently active language.
+ *
+ * @return stdClass|bool
+ *   The cache or FALSE on failure.
+ */
+function views_cache_get($cid, $use_language = FALSE) {
+  if (config('views.settings')->get('skip_cache')) {
+    return FALSE;
+  }
+  if ($use_language) {
+    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+  }
+
+  return cache('views_info')->get($cid);
+}
diff --git a/core/modules/views/views_ui/admin.inc b/core/modules/views/views_ui/admin.inc
index c2775e4..0010bc9 100644
--- a/core/modules/views/views_ui/admin.inc
+++ b/core/modules/views/views_ui/admin.inc
@@ -1440,7 +1440,7 @@ function views_ui_config_item_form($form, &$form_state) {
         }
 
         // If this relationship is valid for this type, add it to the list.
-        $data = views_fetch_data($relationship['table']);
+        $data = drupal_container()->get('views.views_data')->get($relationship['table']);
         $base = $data[$relationship['field']]['relationship']['base'];
         $base_fields = views_fetch_fields($base, $form_state['type'], $executable->display_handler->useGroupBy());
         if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
@@ -2154,7 +2154,7 @@ function _views_sort_types($a, $b) {
 function views_fetch_fields($base, $type, $grouping = FALSE, $sub_type = NULL) {
   static $fields = array();
   if (empty($fields)) {
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
     $start = microtime(TRUE);
     // This constructs this ginormous multi dimensional array to
     // collect the important data about fields. In the end,
@@ -2286,6 +2286,7 @@ function views_ui_form_button_was_clicked($element, &$form_state) {
  */
 function views_ui_field_list() {
   $views = views_get_all_views();
+  $data = drupal_container()->get('views.views_data');
 
   // Fetch all fieldapi fields which are used in views
   // Therefore search in all views, displays and handler-types.
@@ -2298,12 +2299,12 @@ function views_ui_field_list() {
       if ($executable->setDisplay($display_id)) {
         foreach ($handler_types as $type => $info) {
           foreach ($executable->getItems($type, $display_id) as $item) {
-            $data = views_fetch_data($item['table']);
-            if (isset($data[$item['field']]) && isset($data[$item['field']][$type])
-              && $data = $data[$item['field']][$type]) {
+            $table_data = $data->get($item['table']);
+            if (isset($table_data[$item['field']]) && isset($table_data[$item['field']][$type])
+              && $field_data = $table_data[$item['field']][$type]) {
               // The final check that we have a fieldapi field now.
-              if (isset($data['field_name'])) {
-                $fields[$data['field_name']][$view->get('name')] = $view->get('name');
+              if (isset($field_data['field_name'])) {
+                $fields[$field_data['field_name']][$view->get('name')] = $view->get('name');
               }
             }
           }
