diff --git a/core/core.services.yml b/core/core.services.yml
index 14c665c..bfa5aca 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -414,6 +414,9 @@ services:
       - { name: service_collector, tag: 'module_install.uninstall_validator', call: addUninstallValidator }
     arguments: ['@app.root', '@module_handler', '@kernel']
     lazy: true
+  module_listing:
+    class: Drupal\Core\Extension\ModuleExtensionList
+    arguments: ['@app.root', 'module', '@state', '@info_parser', '@module_handler']
   content_uninstall_validator:
     class: Drupal\Core\Entity\ContentUninstallValidator
     tags:
diff --git a/core/lib/Drupal/Core/Extension/ExtensionList.php b/core/lib/Drupal/Core/Extension/ExtensionList.php
new file mode 100644
index 0000000..8b3b47f
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ExtensionList.php
@@ -0,0 +1,162 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Extension\ExtensionList.
+ */
+
+namespace Drupal\Core\Extension;
+use Drupal\Core\State\StateInterface;
+
+/**
+ * Provides available extensions.
+ */
+class ExtensionList {
+
+  /**
+   * The type of the extension, either module or theme.
+   *
+   * @var string
+   */
+  protected $type;
+
+  /**
+   * The app root.
+   *
+   * @var string
+   */
+  protected $root;
+
+  /**
+   * The state.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * Default information added to every extension.
+   *
+   * @var array
+   */
+  protected $defaults = [];
+
+  /**
+   * The info parser.
+   *
+   * @var \Drupal\Core\Extension\InfoParserInterface
+   */
+  protected $infoParser;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a new ExtensionList instance.
+   *
+   * @param string $root
+   *   The app root.
+   * @param string $type
+   *   The extension type.
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state.
+   * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
+   *   The info parser
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct($root, $type, StateInterface $state, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler) {
+    $this->root = $root;
+    $this->type = $type;
+    $this->state = $state;
+    $this->infoParser = $info_parser;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * Returns the human readable name of the extension.
+   *
+   * @param string $name
+   *   The extension name.
+   *
+   * @return string
+   *   The human readable name of the extension.
+   */
+  public function getName($name) {
+    $extensions = $this->listExtensions();
+    if (isset($extensions[$name])) {
+      return $extensions[$name]->info['name'];
+    }
+    return 'n/a';
+  }
+
+  /**
+   * Returns all available extensions.
+   *
+   * @return \Drupal\Core\Extension\Extension[]
+   */
+  public function listExtensions() {
+    if (isset($this->extensions)) {
+      return $this->extensions;
+    }
+    if ($extensions = $this->state->get('core.extension_listing.' . $this->type)) {
+      return $extensions;
+    }
+    $extensions = $this->doListExtensions();
+    $this->state->get('core.extension_listing.' . $this->type);
+    $this->extensions = $extensions;
+    return $this->extensions;
+  }
+
+  /**
+   * Scans the available extensions.
+   *
+   * Overriding this method gives other code the chance to add additional
+   * entries to this raw listing.
+   *
+   * @param \Drupal\Core\Extension\ExtensionDiscovery $discovery
+   *   The extension discovery.
+   *
+   * @return \Drupal\Core\Extension\Extension[]
+   */
+  protected function doScanExtensions(ExtensionDiscovery $discovery) {
+    return $discovery->scan($this->type);
+  }
+
+  /**
+   * Build the actual list of extensions before caching it.
+   *
+   * @return \Drupal\Core\Extension\Extension[]
+   */
+  protected function doListExtensions() {
+    $listing = new ExtensionDiscovery($this->root);
+
+    // Find extensions.
+    $extensions = $this->doScanExtensions($listing);
+
+    // Read info files for each extension.
+    foreach ($extensions as $key => $extension) {
+      // Look for the info file.
+      $extension->info = $this->infoParser->parse($extension->getPathname());
+
+      // Add the info file modification time, so it becomes available for
+      // contributed extensions to use for ordering extension lists.
+      $extension->info['mtime'] = $extension->getMTime();
+
+      // Merge in defaults and save.
+      $extensions[$key]->info = $extension->info + $this->defaults;
+
+      // Invoke hook_system_info_alter() to give installed modules a chance to
+      // modify the data in the .info.yml files if necessary.
+      // @todo Remove $type argument, obsolete with $module->getType().
+      $this->moduleHandler->alter('system_info', $extensions[$key]->info, $extensions[$key], $this->type);
+    }
+
+    return $extensions;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleExtensionList.php b/core/lib/Drupal/Core/Extension/ModuleExtensionList.php
new file mode 100644
index 0000000..10570ee
--- /dev/null
+++ b/core/lib/Drupal/Core/Extension/ModuleExtensionList.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Extension\ModuleExtensionList.
+ */
+
+namespace Drupal\Core\Extension;
+
+class ModuleExtensionList extends ExtensionList {
+
+  protected $defaults = [
+    'dependencies' => [],
+    'description' => '',
+    'package' => 'Other',
+    'version' => NULL,
+    'php' => DRUPAL_MINIMUM_PHP,
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doScanExtensions(ExtensionDiscovery $discovery) {
+    $extensions = parent::doScanExtensions($discovery);
+
+    // Find installation profiles.
+    $profiles = $discovery->scan('profile');
+
+    // Include the installation profile in modules that are loaded.
+    if ($profile = drupal_get_profile()) {
+      $extensions[$profile] = $profiles[$profile];
+      // Installation profile hooks are always executed last.
+      $extensions[$profile]->weight = 1000;
+    }
+
+    return $extensions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doListExtensions() {
+    $extensions = parent::doListExtensions();
+
+    if ($profile = drupal_get_profile()) {
+      // Installation profiles are hidden by default, unless explicitly specified
+      // otherwise in the .info.yml file.
+      if (!isset($extensions[$profile]->info['hidden'])) {
+        $extensions[$profile]->info['hidden'] = TRUE;
+      }
+
+      if (isset($extensions[$profile])) {
+        // The installation profile is required, if it's a valid module.
+        $extensions[$profile]->info['required'] = TRUE;
+        // Add a default distribution name if the profile did not provide one.
+        // @see install_profile_info()
+        // @see drupal_install_profile_distribution_name()
+        if (!isset($extensions[$profile]->info['distribution']['name'])) {
+          $extensions[$profile]->info['distribution']['name'] = 'Drupal';
+        }
+      }
+    }
+
+    // It is possible that a module was marked as required by
+    // hook_system_info_alter() and modules that it depends on are not required.
+    foreach ($extensions as $extension) {
+      $this->moduleDataEnsureRequired($extension, $extensions);
+    }
+
+    return $extensions;
+  }
+
+  /**
+   * Ensures that dependencies of required modules are also required.
+   *
+   * @param \Drupal\Core\Extension\Extension $module
+   *   The module info.
+   * @param \Drupal\Core\Extension\Extension[] $modules
+   *   The array of all module info.
+   */
+  protected function moduleDataEnsureRequired(Extension $module, array $modules = []) {
+    if (!empty($module->info['required'])) {
+      foreach ($module->info['dependencies'] as $dependency) {
+        $dependency_name = ModuleHandler::parseDependency($dependency)['name'];
+        if (!isset($modules[$dependency_name]->info['required'])) {
+          $modules[$dependency_name]->info['required'] = TRUE;
+          $modules[$dependency_name]->info['explanation'] = t('Dependency of required module @module', array('@module' => $module->info['name']));
+          // Ensure any dependencies it has are required.
+         $this->moduleDataEnsureRequired($modules[$dependency_name], $modules);
+        }
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index bb79ae4..3c4a0bb 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -710,8 +710,7 @@ public function getModuleDirectories() {
    * {@inheritdoc}
    */
   public function getName($module) {
-    $module_data = system_rebuild_module_data();
-    return $module_data[$module]->info['name'];
+    return \Drupal::service('module_listing')->getName($module);
   }
 
 }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index d2bda7f..93e4772 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -882,95 +882,7 @@ function system_get_info($type, $name = NULL) {
  *   An associative array of module information.
  */
 function _system_rebuild_module_data() {
-  $listing = new ExtensionDiscovery(\Drupal::root());
-  // Find modules
-  $modules = $listing->scan('module');
-
-  // Find installation profiles.
-  $profiles = $listing->scan('profile');
-
-  // Include the installation profile in modules that are loaded.
-  if ($profile = drupal_get_profile()) {
-    $modules[$profile] = $profiles[$profile];
-    // Installation profile hooks are always executed last.
-    $modules[$profile]->weight = 1000;
-  }
-
-  // Set defaults for module info.
-  $defaults = array(
-    'dependencies' => array(),
-    'description' => '',
-    'package' => 'Other',
-    'version' => NULL,
-    'php' => DRUPAL_MINIMUM_PHP,
-  );
-
-  // Read info files for each module.
-  foreach ($modules as $key => $module) {
-    // Look for the info file.
-    $module->info = \Drupal::service('info_parser')->parse($module->getPathname());
-
-    // Add the info file modification time, so it becomes available for
-    // contributed modules to use for ordering module lists.
-    $module->info['mtime'] = $module->getMTime();
-
-    // Merge in defaults and save.
-    $modules[$key]->info = $module->info + $defaults;
-
-    // Installation profiles are hidden by default, unless explicitly specified
-    // otherwise in the .info.yml file.
-    if ($key == $profile && !isset($modules[$key]->info['hidden'])) {
-      $modules[$key]->info['hidden'] = TRUE;
-    }
-
-    // Invoke hook_system_info_alter() to give installed modules a chance to
-    // modify the data in the .info.yml files if necessary.
-    // @todo Remove $type argument, obsolete with $module->getType().
-    $type = 'module';
-    \Drupal::moduleHandler()->alter('system_info', $modules[$key]->info, $modules[$key], $type);
-  }
-
-  // It is possible that a module was marked as required by
-  // hook_system_info_alter() and modules that it depends on are not required.
-  foreach ($modules as $module) {
-    _system_rebuild_module_data_ensure_required($module, $modules);
-  }
-
-
-  if ($profile && isset($modules[$profile])) {
-    // The installation profile is required, if it's a valid module.
-    $modules[$profile]->info['required'] = TRUE;
-    // Add a default distribution name if the profile did not provide one.
-    // @see install_profile_info()
-    // @see drupal_install_profile_distribution_name()
-    if (!isset($modules[$profile]->info['distribution']['name'])) {
-      $modules[$profile]->info['distribution']['name'] = 'Drupal';
-    }
-  }
-
-  return $modules;
-}
-
-/**
- * Ensures that dependencies of required modules are also required.
- *
- * @param \Drupal\Core\Extension\Extension $module
- *   The module info.
- * @param \Drupal\Core\Extension\Extension[] $modules
- *   The array of all module info.
- */
-function _system_rebuild_module_data_ensure_required($module, &$modules) {
-  if (!empty($module->info['required'])) {
-    foreach ($module->info['dependencies'] as $dependency) {
-      $dependency_name = ModuleHandler::parseDependency($dependency)['name'];
-      if (!isset($modules[$dependency_name]->info['required'])) {
-        $modules[$dependency_name]->info['required'] = TRUE;
-        $modules[$dependency_name]->info['explanation'] = t('Dependency of required module @module', array('@module' => $module->info['name']));
-        // Ensure any dependencies it has are required.
-        _system_rebuild_module_data_ensure_required($modules[$dependency_name], $modules);
-      }
-    }
-  }
+  return \Drupal::service('module_listing')->listExtensions();
 }
 
 /**
