diff --git a/core/authorize.php b/core/authorize.php
index fc74bfb..c53475b 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -27,9 +27,8 @@
  * Global flag to identify update.php and authorize.php runs.
  *
  * Identifies update.php and authorize.php runs, avoiding unwanted operations
- * such as hook_init() invocations, css/js preprocessing and
- * translation, and solves some theming issues. The flag is checked in other
- * places in Drupal code (not just authorize.php).
+ * such as css/js preprocessing and translation, and solves some theming issues.
+ * The flag is checked in other places in Drupal code (not just authorize.php).
  */
 const MAINTENANCE_MODE = 'update';
 
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 5e8e2d1..83e84a6 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2143,7 +2143,7 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
  * all typical visitors and most pages of a site. It is critical that all
  * preprocessed files are added unconditionally on every page, even if the
  * files do not happen to be needed on a page. This is normally done by calling
- * drupal_add_css() in a hook_init() implementation.
+ * drupal_add_css() in a hook_page_build() implementation.
  *
  * Non-preprocessed files should only be added to the page when they are
  * actually needed.
@@ -2198,14 +2198,15 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
  *     enabled, this should be set to TRUE if the stylesheet is present on every
  *     page of the website for users for whom it is present at all. This
  *     defaults to FALSE. It is set to TRUE for stylesheets added via module and
- *     theme .info.yml files. Modules that add stylesheets within hook_init()
- *     implementations, or from other code that ensures that the stylesheet is
- *     added to all website pages, should also set this flag to TRUE. All
- *     stylesheets within the same group that have the 'every_page' flag set to
- *     TRUE and do not have 'preprocess' set to FALSE are aggregated together
- *     into a single aggregate file, and that aggregate file can be reused
- *     across a user's entire site visit, leading to faster navigation between
- *     pages. However, stylesheets that are only needed on pages less frequently
+ *     theme .info.yml files. Modules that add stylesheets within
+ *     hook_page_build() implementations, or from other code that ensures that
+ *     the stylesheet is added to all website pages, should also set this flag
+ *     to TRUE. All stylesheets within the same group that have the 'every_page'
+ *     flag set to TRUE and do not have 'preprocess' set to FALSE are aggregated
+ *     together into a single aggregate file, and that aggregate file can be
+ *     reused across a user's entire site visit, leading to faster navigation
+ *     between pages.
+ *     However, stylesheets that are only needed on pages less frequently
  *     visited, can be added by code that only runs for those particular pages,
  *     and that code should not set the 'every_page' flag. This minimizes the
  *     size of the aggregate file that the user needs to download when first
@@ -3299,7 +3300,7 @@ function drupal_region_class($region) {
  * all typical visitors and most pages of a site. It is critical that all
  * preprocessed files are added unconditionally on every page, even if the
  * files are not needed on a page. This is normally done by calling
- * drupal_add_js() in a hook_init() implementation.
+ * drupal_add_js() in a hook_page_build() implementation.
  *
  * Non-preprocessed files should only be added to the page when they are
  * actually needed.
@@ -3342,9 +3343,9 @@ function drupal_region_class($region) {
  *     page of the website for users for whom it is present at all. This
  *     defaults to FALSE. It is set to TRUE for JavaScript files that are added
  *     via module and theme .info.yml files. Modules that add JavaScript within
- *     hook_init() implementations, or from other code that ensures that the
- *     JavaScript is added to all website pages, should also set this flag to
- *     TRUE. All JavaScript files within the same group and that have the
+ *     hook_page_build() implementations, or from other code that ensures that
+ *     the JavaScript is added to all website pages, should also set this flag
+ *     to TRUE. All JavaScript files within the same group and that have the
  *     'every_page' flag set to TRUE and do not have 'preprocess' set to FALSE
  *     are aggregated together into a single aggregate file, and that aggregate
  *     file can be reused across a user's entire site visit, leading to faster
@@ -4618,16 +4619,8 @@ function _drupal_bootstrap_full($skip = FALSE) {
   // Let all modules take action before the menu system handles the request.
   // We do not want this while running update.php.
   if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
-    // Prior to invoking hook_init(), initialize the theme (potentially a custom
-    // one for this page), so that:
-    // - Modules with hook_init() implementations that call theme() or
-    //   theme_get_registry() don't initialize the incorrect theme.
-    // - The theme can have hook_*_alter() implementations affect page building
-    //   (e.g., hook_form_alter(), hook_node_view_alter(), hook_page_alter()),
-    //   ahead of when rendering starts.
     menu_set_custom_theme();
     drupal_theme_initialize();
-    module_invoke_all('init');
   }
 }
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
index 242f935..39828ef 100644
--- a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
@@ -27,16 +27,8 @@ class LegacyRequestSubscriber implements EventSubscriberInterface {
    */
   public function onKernelRequestLegacy(GetResponseEvent $event) {
     if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
-      // Prior to invoking hook_init(), initialize the theme (potentially a
-      // custom one for this page), so that:
-      // - Modules with hook_init() implementations that call theme() or
-      //   theme_get_registry() don't initialize the incorrect theme.
-      // - The theme can have hook_*_alter() implementations affect page
-      //   building (e.g., hook_form_alter(), hook_node_view_alter(),
-      //   hook_page_alter()), ahead of when rendering starts.
       menu_set_custom_theme();
       drupal_theme_initialize();
-      module_invoke_all('init');
 
       // Tell Drupal it is now fully bootstrapped (for the benefit of code that
       // calls drupal_get_bootstrap_phase()), but without having
diff --git a/core/modules/action/tests/action_loop_test/action_loop_test.module b/core/modules/action/tests/action_loop_test/action_loop_test.module
index 36f6d9b..1ea6a9b 100644
--- a/core/modules/action/tests/action_loop_test/action_loop_test.module
+++ b/core/modules/action/tests/action_loop_test/action_loop_test.module
@@ -20,9 +20,9 @@ function action_loop_test_watchdog(array $log_entry) {
 }
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  */
-function action_loop_test_init() {
+function action_loop_test_page_build() {
   if (!empty($_GET['trigger_action_on_watchdog'])) {
     watchdog_skip_semaphore('action_loop_test', 'Triggering action loop');
   }
diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module
index cb97937..bf7ac37 100644
--- a/core/modules/language/tests/language_test/language_test.module
+++ b/core/modules/language/tests/language_test/language_test.module
@@ -9,9 +9,9 @@
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  */
-function language_test_init() {
+function language_test_page_build() {
   language_test_store_language_negotiation();
   if (isset(language(LANGUAGE_TYPE_INTERFACE)->langcode) && isset(language(LANGUAGE_TYPE_INTERFACE)->method_id)) {
     drupal_set_message(t('Language negotiation method: @name', array('@name' => language(LANGUAGE_TYPE_INTERFACE)->method_id)));
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index 1566740..561254e 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -126,14 +126,14 @@ function overlay_user_update($account) {
 }
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  *
  * Determine whether the current page request is destined to appear in the
  * parent window or in the overlay window, and format the page accordingly.
  *
  * @see overlay_set_mode()
  */
-function overlay_init() {
+function overlay_page_build() {
   global $user;
 
   $mode = overlay_get_mode();
@@ -601,7 +601,7 @@ function overlay_get_mode() {
  *     to 'parent' or 'child' in overlay_init() when certain conditions are
  *     met, other modules which want to override that behavior can do so by
  *     setting the mode to 'none' earlier in the page request - e.g., in their
- *     own hook_init() implementations, if they have a lower weight.
+ *     own hook_page_build() implementations, if they have a lower weight.
  *   This parameter is optional, and if omitted, the current mode will be
  *   returned with no action taken.
  *
diff --git a/core/modules/system/lib/Drupal/system/EventSubscriber/SystemEventSubscriber.php b/core/modules/system/lib/Drupal/system/EventSubscriber/SystemEventSubscriber.php
new file mode 100644
index 0000000..cacb66c
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/EventSubscriber/SystemEventSubscriber.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\EventSubscriber\SystemEventSubscriber.
+ */
+
+namespace Drupal\system\EventSubscriber;
+
+use Drupal\Core\Database\Database;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * System subscriber for controller requests.
+ */
+class SystemEventSubscriber implements EventSubscriberInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onRequest(GetResponseEvent $event) {
+    // Ignore slave database servers for this request.
+    //
+    // In Drupal's distributed database structure, new data is written to the master
+    // and then propagated to the slave servers.  This means there is a lag
+    // between when data is written to the master and when it is available on the slave.
+    // At these times, we will want to avoid using a slave server temporarily.
+    // For example, if a user posts a new node then we want to disable the slave
+    // server for that user temporarily to allow the slave server to catch up.
+    // That way, that user will see their changes immediately while for other
+    // users we still get the benefits of having a slave server, just with slightly
+    // stale data.  Code that wants to disable the slave server should use the
+    // db_set_ignore_slave() function to set $_SESSION['ignore_slave_server'] to
+    // the timestamp after which the slave can be re-enabled.
+    if (isset($_SESSION['ignore_slave_server'])) {
+      if ($_SESSION['ignore_slave_server'] >= REQUEST_TIME) {
+        Database::ignoreTarget('default', 'slave');
+      }
+      else {
+        unset($_SESSION['ignore_slave_server']);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onRequest');
+    return $events;
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/HookInitTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/HookInitTest.php
deleted file mode 100644
index 3863d22..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/HookInitTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Theme\HookInitTest.
- */
-
-namespace Drupal\system\Tests\Theme;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Functional test for initialization of the theme system in hook_init().
- */
-class HookInitTest extends WebTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('theme_test');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Theme initialization in hook_init()',
-      'description' => 'Tests that the theme system can be correctly initialized in hook_init().',
-      'group' => 'Theme',
-    );
-  }
-
-  /**
-   * Test that the theme system can generate output when called by hook_init().
-   */
-  function testThemeInitializationHookInit() {
-    $this->drupalGet('theme-test/hook-init');
-    // Verify that themed output generated in hook_init() appears.
-    $this->assertRaw('Themed output generated in hook_init()');
-    // Verify that the default theme's CSS still appears when the theme system
-    // is initialized in hook_init().
-    $this->assertRaw('stark/css/layout.css');
-  }
-}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 5360a83..b560e92 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1323,23 +1323,6 @@ function hook_forms($form_id, $args) {
 }
 
 /**
- * Perform setup tasks for non-cached page requests.
- *
- * This hook is run at the beginning of the page request. It is typically
- * used to set up global parameters that are needed later in the request.
- * When this hook is called, the theme and all modules are already loaded in
- * memory.
- *
- * This hook is not run on cached pages.
- *
- * Do not use this hook to add CSS/JS to pages, use hook_page_build() instead.
- *
- * @see hook_page_build()
- */
-function hook_init() {
-}
-
-/**
  * Alter an email message created with the drupal_mail() function.
  *
  * hook_mail_alter() allows modification of email messages created and sent
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 0d472e5..efa8919 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -7,7 +7,6 @@
 
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\Cache;
-use Drupal\Core\Database\Database;
 use Drupal\Core\Utility\ModuleInfo;
 use Drupal\Core\TypedData\Primitive;
 use Drupal\system\Plugin\Block\SystemMenuBlock;
@@ -2524,34 +2523,6 @@ function system_filetransfer_info() {
 }
 
 /**
- * Implements hook_init().
- */
-function system_init() {
-  // Ignore slave database servers for this request.
-  //
-  // In Drupal's distributed database structure, new data is written to the
-  // master and then propagated to the slave servers.  This means there is a
-  // lag between when data is written to the master and when it is available on
-  // the slave. At these times, we will want to avoid using a slave server
-  // temporarily. For example, if a user posts a new node then we want to
-  // disable the slave server for that user temporarily to allow the slave
-  // server to catch up. That way, that user will see their changes immediately
-  // while for other users we still get the benefits of having a slave server,
-  // just with slightly stale data.  Code that wants to disable the slave
-  // server should use the db_ignore_slave() function to set
-  // $_SESSION['ignore_slave_server'] to the timestamp after which the slave
-  // can be re-enabled.
-  if (isset($_SESSION['ignore_slave_server'])) {
-    if ($_SESSION['ignore_slave_server'] >= REQUEST_TIME) {
-      Database::ignoreTarget('default', 'slave');
-    }
-    else {
-      unset($_SESSION['ignore_slave_server']);
-    }
-  }
-}
-
-/**
  * Implements hook_page_build().
  */
 function system_page_build(&$page) {
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index c049b0a..bd0c69d 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -6,3 +6,7 @@ services:
   plugin.manager.system.plugin_ui:
     class: Drupal\system\Plugin\Type\PluginUIManager
     arguments: ['@container.namespaces']
+  system.event_subscriber:
+    class: Drupal\system\EventSubscriber\SystemEventSubscriber
+    tags:
+      - {name: event_subscriber}
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index 695c216..dd9e232 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -505,9 +505,9 @@ function menu_test_menu_trail_callback() {
 }
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  */
-function menu_test_init() {
+function menu_test_page_build() {
   // When requested by one of the MenuTrailTestCase tests, record the initial
   // active trail during Drupal's bootstrap (before the user is redirected to a
   // custom 403 or 404 page). See menu_test_custom_403_404_callback().
diff --git a/core/modules/system/tests/modules/system_test/system_test.module b/core/modules/system/tests/modules/system_test/system_test.module
index 31352d6..ffa4868 100644
--- a/core/modules/system/tests/modules/system_test/system_test.module
+++ b/core/modules/system/tests/modules/system_test/system_test.module
@@ -129,9 +129,9 @@ function system_test_modules_uninstalled($modules) {
 }
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  */
-function system_test_init() {
+function system_test_page_build() {
   // Used by FrontPageTestCase to get the results of drupal_is_front_page().
   $frontpage = state()->get('system_test.front_page_output') ?: 0;
   if ($frontpage && drupal_is_front_page()) {
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index d610a19..faef7b5 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -52,11 +52,6 @@ function theme_test_menu() {
     'theme callback' => '_theme_custom_theme',
     'type' => MENU_CALLBACK,
   );
-  $items['theme-test/hook-init'] = array(
-    'page callback' => 'theme_test_hook_init_page_callback',
-    'access callback' => TRUE,
-    'type' => MENU_CALLBACK,
-  );
   $items['theme-test/template-test'] = array(
     'page callback' => 'theme_test_template_test_page_callback',
     'access callback' => TRUE,
@@ -71,28 +66,6 @@ function theme_test_menu() {
 }
 
 /**
- * Implements hook_init().
- */
-function theme_test_init() {
-  if (arg(0) == 'theme-test' && arg(1) == 'hook-init') {
-    // First, force the theme registry to be rebuilt on this page request. This
-    // allows us to test a full initialization of the theme system in the code
-    // below.
-    drupal_theme_rebuild();
-    // Next, initialize the theme system by storing themed text in a global
-    // variable. We will use this later in theme_test_hook_init_page_callback()
-    // to test that even when the theme system is initialized this early, it is
-    // still capable of returning output and theming the page as a whole.
-    $GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in hook_init()'));
-  }
-  if (arg(0) == 'user' && arg(1) == 'autocomplete') {
-    // Register a fake registry loading callback. If it gets called by
-    // theme_get_registry(), the registry has not been initialized yet.
-    _theme_registry_callback('_theme_test_load_registry', array());
-  }
-}
-
-/**
  * Fake registry loading callback.
  */
 function _theme_test_load_registry() {
@@ -101,13 +74,6 @@ function _theme_test_load_registry() {
 }
 
 /**
- * Menu callback for testing themed output generated in hook_init().
- */
-function theme_test_hook_init_page_callback() {
-  return $GLOBALS['theme_test_output'];
-}
-
-/**
  * Menu callback for testing template overridding based on filename.
  */
 function theme_test_template_test_page_callback() {
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index ddbc398..7ab2249 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -96,9 +96,9 @@ function update_help($path, $arg) {
 }
 
 /**
- * Implements hook_init().
+ * Implements hook_page_build().
  */
-function update_init() {
+function update_page_build() {
   if (arg(0) == 'admin' && user_access('administer site configuration')) {
     switch (current_path()) {
       // These pages don't need additional nagging.
diff --git a/core/update.php b/core/update.php
index cee23c4..b8992ad 100644
--- a/core/update.php
+++ b/core/update.php
@@ -33,8 +33,8 @@
 /**
  * Global flag indicating that update.php is being run.
  *
- * When this flag is set, various operations do not take place, such as invoking
- * hook_init(), css/js preprocessing, and translation.
+ * When this flag is set, various operations do not take place, such as  css/js
+ * preprocessing, and translation.
  *
  * This constant is defined using define() instead of const so that PHP
  * versions older than 5.3 can display the proper PHP requirements instead of
