diff --git a/core/core.services.yml b/core/core.services.yml
index 14c665c..72f2c5a 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1209,6 +1209,8 @@ services:
     arguments: ['@private_key', '@cache.default']
   current_user:
     class: Drupal\Core\Session\AccountProxy
+    tags:
+      - { name: needs_destruction }
   session_configuration:
     class: Drupal\Core\Session\SessionConfiguration
     arguments: ['%session.storage.options%']
@@ -1225,7 +1227,7 @@ services:
     alias: session_handler.storage
   session_handler.storage:
     class: Drupal\Core\Session\SessionHandler
-    arguments: ['@request_stack', '@database']
+    arguments: ['@request_stack', '@database', '@current_user']
     tags:
       - { name: backend_overridable }
   session_handler.write_check:
diff --git a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
index 7db5d9b..69ad871 100644
--- a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
+++ b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
@@ -7,9 +7,15 @@
 
 namespace Drupal\Core\Authentication\Provider;
 
+use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Authentication\AuthenticationProviderInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Session\AnonymousUserSession;
+use Drupal\Core\Session\UserSession;
+use Drupal\Core\Site\Settings;
 use Drupal\Core\Session\SessionConfigurationInterface;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
 
 /**
  * Cookie based authentication provider.
@@ -24,13 +30,23 @@ class Cookie implements AuthenticationProviderInterface {
   protected $sessionConfiguration;
 
   /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
    * Constructs a new cookie authentication provider.
    *
    * @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
    *   The session configuration.
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
    */
-  public function __construct(SessionConfigurationInterface $session_configuration) {
+  public function __construct(SessionConfigurationInterface $session_configuration, Connection $connection) {
     $this->sessionConfiguration = $session_configuration;
+    $this->connection = $connection;
   }
 
   /**
@@ -44,13 +60,62 @@ public function applies(Request $request) {
    * {@inheritdoc}
    */
   public function authenticate(Request $request) {
-    if ($request->getSession()->start()) {
-      // @todo Remove global in https://www.drupal.org/node/2228393
-      global $_session_user;
-      return $_session_user;
-    }
+    $session = $request->getSession();
+    if ($session->start()) {
+      $values = $this->getSessionRecord($session);
 
+      // We found the client's session record and they are an authenticated,
+      // active user.
+      if ($values && $values['uid'] > 0 && $values['status'] == 1) {
+        // Add roles element to $user.
+        $rids = $this->getUserRoles($values['uid']);
+        $values['roles'] = array_merge(array(DRUPAL_AUTHENTICATED_RID), $rids);
+        $user = new UserSession($values);
+      }
+      elseif ($values) {
+        // The user is anonymous or blocked. Only preserve two fields from the
+        // {sessions} table.
+        $user = new UserSession(array(
+          'session' => $values['session'],
+          'access' => $values['access'],
+        ));
+      }
+      else {
+        // The session has expired.
+        $user = new AnonymousUserSession();
+      }
+      return $user;
+    }
     return NULL;
   }
 
+  /**
+   * Returns the user's session record.
+   *
+   * @param Symfony\Component\HttpFoundation\Session\SessionInterface $session
+   *   The session.
+   *
+   * @return array|NULL
+   *   The user's session record
+   */
+  protected function getSessionRecord(SessionInterface $session) {
+    return $this->connection->query("SELECT u.*, s.* FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.default_langcode = 1 AND s.sid = :sid", array(
+      ':sid' => Crypt::hashBase64($session->getId()),
+    ))->fetchAssoc();
+  }
+
+  /**
+   * Returns the user's roles.
+   *
+   * @param string $uid
+   *   The user's id.
+   *
+   * @return array|NULL
+   *   The user's roles
+   */
+  protected function getUserRoles($uid) {
+   return $this->connection->query("SELECT ur.roles_target_id as rid FROM {user__roles} ur WHERE ur.entity_id = :uid", array(
+     ':uid' => $uid,
+   ))->fetchCol();
+  }
 }
diff --git a/core/lib/Drupal/Core/Session/AccountProxy.php b/core/lib/Drupal/Core/Session/AccountProxy.php
index eae71e7..bc36c46 100644
--- a/core/lib/Drupal/Core/Session/AccountProxy.php
+++ b/core/lib/Drupal/Core/Session/AccountProxy.php
@@ -7,6 +7,9 @@
 
 namespace Drupal\Core\Session;
 
+use Drupal\Core\DestructableInterface;
+use Drupal\Core\Site\Settings;
+
 /**
  * A proxied implementation of AccountInterface.
  *
@@ -18,7 +21,7 @@
  * allows legacy code to change the current user where the user cannot be
  * directly injected into dependent code.
  */
-class AccountProxy implements AccountProxyInterface {
+class AccountProxy implements AccountProxyInterface, DestructableInterface {
 
   /**
    * The instantiated account.
@@ -183,6 +186,18 @@ public function setInitialAccountId($account_id) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function destruct() {
+    // Update last access time (no more than once per 180 seconds).
+    if ($this->account->isAuthenticated() && REQUEST_TIME - $this->account->getLastAccessedTime() > Settings::get('session_write_interval', 180)) {
+      /** @var \Drupal\user\UserStorageInterface $storage */
+      $storage = \Drupal::entityManager()->getStorage('user');
+      $storage->updateLastAccessTimestamp($this->account, REQUEST_TIME);
+    }
+  }
+
+  /**
    * Load a user entity.
    *
    * The entity manager requires additional initialization code and cache
diff --git a/core/lib/Drupal/Core/Session/SessionHandler.php b/core/lib/Drupal/Core/Session/SessionHandler.php
index 8d9aa96..60a2c15 100644
--- a/core/lib/Drupal/Core/Session/SessionHandler.php
+++ b/core/lib/Drupal/Core/Session/SessionHandler.php
@@ -34,11 +34,11 @@ class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
   protected $connection;
 
   /**
-   * An associative array of obsolete sessions with session id as key, and db-key as value.
+   * The current user account.
    *
-   * @var array
+   * @var \Drupal\Core\Session\AccountInterface
    */
-  protected $obsoleteSessionIds = array();
+  protected $currentUser;
 
   /**
    * Constructs a new SessionHandler instance.
@@ -47,10 +47,13 @@ class SessionHandler extends AbstractProxy implements \SessionHandlerInterface {
    *   The request stack.
    * @param \Drupal\Core\Database\Connection $connection
    *   The database connection.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user account.
    */
-  public function __construct(RequestStack $request_stack, Connection $connection) {
+  public function __construct(RequestStack $request_stack, Connection $connection, AccountInterface $current_user) {
     $this->requestStack = $request_stack;
     $this->connection = $connection;
+    $this->currentUser = $current_user;
   }
 
   /**
@@ -64,58 +67,34 @@ public function open($save_path, $name) {
    * {@inheritdoc}
    */
   public function read($sid) {
-    // @todo Remove global in https://www.drupal.org/node/2228393
-    global $_session_user;
-
-    // Handle the case of first time visitors and clients that don't store
-    // cookies (eg. web crawlers).
-    $cookies = $this->requestStack->getCurrentRequest()->cookies;
-    if (empty($sid) || !$cookies->has($this->getName())) {
-      $_session_user = new UserSession();
+    if (empty($sid)) {
       return '';
     }
 
-    $values = $this->connection->query("SELECT u.*, s.* FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.default_langcode = 1 AND s.sid = :sid", array(
-      ':sid' => Crypt::hashBase64($sid),
-    ))->fetchAssoc();
-
-    // We found the client's session record and they are an authenticated,
-    // active user.
-    if ($values && $values['uid'] > 0 && $values['status'] == 1) {
-      // Add roles element to $user.
-      $rids = $this->connection->query("SELECT ur.roles_target_id as rid FROM {user__roles} ur WHERE ur.entity_id = :uid", array(
-        ':uid' => $values['uid'],
-      ))->fetchCol();
-      $values['roles'] = array_merge(array(AccountInterface::AUTHENTICATED_ROLE), $rids);
-      $_session_user = new UserSession($values);
-    }
-    elseif ($values) {
-      // The user is anonymous or blocked. Only preserve two fields from the
-      // {sessions} table.
-      $_session_user = new UserSession(array(
-        'session' => $values['session'],
-        'access' => $values['access'],
-      ));
+    // Read the session data from the database.
+    $record = $this->connection->select('sessions', 's')
+      ->fields('s')
+      ->condition('sid', Crypt::hashBase64($sid))
+      ->execute()
+      ->fetchAssoc();
+
+    if (isset($record['session'])) {
+      return $record['session'];
     }
     else {
-      // The session has expired.
-      $_session_user = new UserSession();
+      return '';
     }
-
-    return $_session_user->session;
   }
 
   /**
    * {@inheritdoc}
    */
   public function write($sid, $value) {
-    $user = \Drupal::currentUser();
-
     // The exception handler is not active at this point, so we need to do it
     // manually.
     try {
       $fields = array(
-        'uid' => $user->id(),
+        'uid' => $this->currentUser->id(),
         'hostname' => $this->requestStack->getCurrentRequest()->getClientIP(),
         'session' => $value,
         'timestamp' => REQUEST_TIME,
@@ -124,13 +103,6 @@ public function write($sid, $value) {
         ->keys(array('sid' => Crypt::hashBase64($sid)))
         ->fields($fields)
         ->execute();
-
-      // Likewise, do not update access time more than once per 180 seconds.
-      if ($user->isAuthenticated() && REQUEST_TIME - $user->getLastAccessedTime() > Settings::get('session_write_interval', 180)) {
-        /** @var \Drupal\user\UserStorageInterface $storage */
-        $storage = \Drupal::entityManager()->getStorage('user');
-        $storage->updateLastAccessTimestamp($user, REQUEST_TIME);
-      }
       return TRUE;
     }
     catch (\Exception $exception) {
@@ -156,21 +128,11 @@ public function close() {
    * {@inheritdoc}
    */
   public function destroy($sid) {
-
-
     // Delete session data.
     $this->connection->delete('sessions')
       ->condition('sid', Crypt::hashBase64($sid))
       ->execute();
 
-    // Reset $_SESSION and current user to prevent a new session from being
-    // started in \Drupal\Core\Session\SessionManager::save().
-    $_SESSION = array();
-    \Drupal::currentUser()->setAccount(new AnonymousUserSession());
-
-    // Unset the session cookies.
-    $this->deleteCookie($this->getName());
-
     return TRUE;
   }
 
@@ -189,19 +151,4 @@ public function gc($lifetime) {
     return TRUE;
   }
 
-  /**
-   * Deletes a session cookie.
-   *
-   * @param string $name
-   *   Name of session cookie to delete.
-   */
-  protected function deleteCookie($name) {
-    $cookies = $this->requestStack->getCurrentRequest()->cookies;
-    if ($cookies->has($name)) {
-      $params = session_get_cookie_params();
-      setcookie($name, '', REQUEST_TIME - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
-      $cookies->remove($name);
-    }
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/SessionManager.php
index 7c3ac21..ec3ecdf 100644
--- a/core/lib/Drupal/Core/Session/SessionManager.php
+++ b/core/lib/Drupal/Core/Session/SessionManager.php
@@ -121,10 +121,6 @@ public function start() {
     }
 
     if (empty($result)) {
-      // @todo Remove global in https://www.drupal.org/node/2228393
-      global $_session_user;
-      $_session_user = new AnonymousUserSession();
-
       // Randomly generate a session identifier for this request. This is
       // necessary because \Drupal\user\SharedTempStoreFactory::get() wants to
       // know the future session ID of a lazily started session in advance.
@@ -191,7 +187,7 @@ public function save() {
       // There is no session data to store, destroy the session if it was
       // previously started.
       if ($this->getSaveHandler()->isActive()) {
-        session_destroy();
+        $this->destroy();
       }
     }
     else {
@@ -211,8 +207,6 @@ public function save() {
    * {@inheritdoc}
    */
   public function regenerate($destroy = FALSE, $lifetime = NULL) {
-    $user = \Drupal::currentUser();
-
     // Nothing to do if we are not allowed to change the session.
     if ($this->isCli()) {
       return;
@@ -260,6 +254,22 @@ public function delete($uid) {
   /**
    * {@inheritdoc}
    */
+  public function destroy() {
+    session_destroy();
+
+    // Unset the session cookies.
+    $session_name = $this->getName();
+    $cookies = $this->requestStack->getCurrentRequest()->cookies;
+    if ($cookies->has($session_name)) {
+      $params = session_get_cookie_params();
+      setcookie($session_name, '', REQUEST_TIME - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
+      $cookies->remove($session_name);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function setWriteSafeHandler(WriteSafeSessionHandlerInterface $handler) {
     $this->writeSafeHandler = $handler;
   }
diff --git a/core/lib/Drupal/Core/Session/SessionManagerInterface.php b/core/lib/Drupal/Core/Session/SessionManagerInterface.php
index d194002..c755687 100644
--- a/core/lib/Drupal/Core/Session/SessionManagerInterface.php
+++ b/core/lib/Drupal/Core/Session/SessionManagerInterface.php
@@ -23,6 +23,11 @@
   public function delete($uid);
 
   /**
+   * Destroys the current session and removes session cookies.
+   */
+  public function destroy();
+
+  /**
    * Sets the write safe session handler.
    *
    * @todo: This should be removed once all database queries are removed from
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 2497ec0..184c275 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -544,8 +544,6 @@ function template_preprocess_username(&$variables) {
  * @see hook_user_login()
  */
 function user_login_finalize(UserInterface $account) {
-  \Drupal::currentUser()->setAccount($account);
-  \Drupal::logger('user')->notice('Session opened for %name.', array('%name' => $account->getUsername()));
   // Update the user table timestamp noting user has logged in.
   // This is also used to invalidate one-time login links.
   $account->setLastLoginTime(REQUEST_TIME);
@@ -557,7 +555,9 @@ function user_login_finalize(UserInterface $account) {
   // This is called before hook_user_login() in case one of those functions
   // fails or incorrectly does a redirect which would leave the old session
   // in place.
+  \Drupal::currentUser()->setAccount($account);
   \Drupal::service('session')->migrate();
+  \Drupal::logger('user')->notice('Session opened for %name.', array('%name' => $account->getUsername()));
 
   \Drupal::moduleHandler()->invokeAll('user_login', array($account));
 }
@@ -1416,7 +1416,8 @@ function user_logout() {
   // Session::invalidate(). Regrettably this method is currently broken and may
   // lead to the creation of spurious session records in the database.
   // @see https://github.com/symfony/symfony/issues/12375
-  session_destroy();
+  \Drupal::service('session_manager')->destroy();
+  $user->setAccount(new AnonymousUserSession());
 }
 
 /**
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index b520772..792eec0 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -17,7 +17,7 @@ services:
       - { name: access_check, applies_to: _user_is_logged_in }
   authentication.cookie:
     class: Drupal\Core\Authentication\Provider\Cookie
-    arguments: ['@session_configuration']
+    arguments: ['@session_configuration', '@database']
     tags:
       - { name: authentication_provider, priority: 0 }
   user.data:
