diff --git src/wp-includes/class-wp-roles.php src/wp-includes/class-wp-roles.php
index 23e05d7..d9654f6 100644
--- src/wp-includes/class-wp-roles.php
+++ src/wp-includes/class-wp-roles.php
@@ -70,12 +70,24 @@
 	public $use_db = true;
 
 	/**
+	 * What role type to get from the database
+	 *
+	 * @since 4.8.0
+	 *
+	 * @access public
+	 * @var string
+	 */
+	public $role_type = 'user_roles';
+
+	/**
 	 * Constructor
 	 *
 	 * @since 2.0.0
 	 */
-	public function __construct() {
-		$this->_init();
+	public function __construct( $blog_id = null ) {
+		$this->use_db = empty( $GLOBALS['wp_user_roles'] );
+
+		$this->for_blog( $blog_id );
 	}
 
 	/**
@@ -99,33 +111,40 @@
 	 * Set up the object properties.
 	 *
 	 * The role key is set to the current prefix for the $wpdb object with
-	 * 'user_roles' appended. If the $wp_user_roles global is set, then it will
+	 * the role_type appended. If the $wp_user_roles global is set, then it will
 	 * be used and the role option will not be updated or used.
 	 *
 	 * @since 2.1.0
 	 * @access protected
 	 *
-	 * @global array $wp_user_roles Used to set the 'roles' property value.
+	 * @param string $role_key Optional role key.
 	 */
-	protected function _init() {
-		global $wp_user_roles, $wpdb;
+	protected function _init( $role_key = '' ) {
+		global $wpdb;
 
-		$this->role_key = $wpdb->get_blog_prefix() . 'user_roles';
-		if ( ! empty( $wp_user_roles ) ) {
-			$this->roles = $wp_user_roles;
-			$this->use_db = false;
+		// Fallback on current site's prefix if no role key was passed
+		if ( empty( $role_key ) ) {
+			$this->role_key = $wpdb->get_blog_prefix() . $this->role_type;
+		} else {
+			$this->role_key = $role_key;
+		}
+
+		// Use the $wp_user_roles global if one was set
+		if ( false === $this->use_db ) {
+			$this->roles = $GLOBALS['wp_user_roles'];
 		} else {
 			$this->roles = get_option( $this->role_key );
 		}
 
-		if ( empty( $this->roles ) )
-			return;
+		// Set role names & objects from the roles array
+		if ( ! empty( $this->roles ) ) {
+			$this->role_objects = array();
+			$this->role_names   = array();
 
-		$this->role_objects = array();
-		$this->role_names =  array();
-		foreach ( array_keys( $this->roles ) as $role ) {
-			$this->role_objects[$role] = new WP_Role( $role, $this->roles[$role]['capabilities'] );
-			$this->role_names[$role] = $this->roles[$role]['name'];
+			foreach ( array_keys( $this->roles ) as $role ) {
+				$this->role_objects[ $role ] = new WP_Role( $role, $this->roles[ $role ]['capabilities'] );
+				$this->role_names[ $role ]   = $this->roles[ $role ]['name'];
+			}
 		}
 
 		/**
@@ -142,15 +161,35 @@
 	 * Reinitialize the object
 	 *
 	 * Recreates the role objects. This is typically called only by switch_to_blog()
-	 * after switching wpdb to a new site ID.
+	 * after switching wpdb to a new site ID, and is skipped entirely when the
+	 * $wp_user_roles omega global is used.
 	 *
 	 * @since 3.5.0
-	 * @deprecated 4.7.0 Use new WP_Roles()
 	 * @access public
 	 */
 	public function reinit() {
-		_deprecated_function( __METHOD__, '4.7.0', 'new WP_Roles()' );
-		$this->_init();
+		if ( $this->use_db ) {
+			$this->_init();
+		}
+	}
+
+	/**
+	 * Set the site to operate on. Defaults to the current site.
+	 *
+	 * @since 4.8.0
+	 *
+	 * @global WPDB $wpdb
+	 *
+	 * @param int $blog_id Optional. The site ID to initialize for.
+	 */
+	public function for_blog( $blog_id = null ) {
+		global $wpdb;
+
+		// Set the role_key based on the prefix for the blog_id
+		$role_key = $wpdb->get_blog_prefix( $blog_id ) . $this->role_type;
+
+		// Initialize roles based on key
+		$this->_init( $role_key );
 	}
 
 	/**
diff --git src/wp-includes/ms-blogs.php src/wp-includes/ms-blogs.php
index 9188750..875a6ce 100644
--- src/wp-includes/ms-blogs.php
+++ src/wp-includes/ms-blogs.php
@@ -766,7 +766,7 @@
  * @return true Always returns True.
  */
 function switch_to_blog( $new_blog, $deprecated = null ) {
-	global $wpdb, $wp_roles;
+	global $wpdb;
 
 	$blog_id = get_current_blog_id();
 	if ( empty( $new_blog ) ) {
@@ -821,12 +821,6 @@
 		}
 	}
 
-	if ( did_action( 'init' ) ) {
-		$wp_roles = new WP_Roles();
-		$current_user = wp_get_current_user();
-		$current_user->for_blog( $new_blog );
-	}
-
 	/** This filter is documented in wp-includes/ms-blogs.php */
 	do_action( 'switch_blog', $new_blog, $prev_blog_id );
 	$GLOBALS['switched'] = true;
@@ -850,7 +844,7 @@
  * @return bool True on success, false if we're already on the current blog
  */
 function restore_current_blog() {
-	global $wpdb, $wp_roles;
+	global $wpdb;
 
 	if ( empty( $GLOBALS['_wp_switched_stack'] ) ) {
 		return false;
@@ -895,12 +889,6 @@
 		}
 	}
 
-	if ( did_action( 'init' ) ) {
-		$wp_roles = new WP_Roles();
-		$current_user = wp_get_current_user();
-		$current_user->for_blog( $blog );
-	}
-
 	/** This filter is documented in wp-includes/ms-blogs.php */
 	do_action( 'switch_blog', $blog, $prev_blog_id );
 
@@ -911,6 +899,21 @@
 }
 
 /**
+ * Switch WP_Roles & current_user roles & caps, usually when switching sites.
+ *
+ * @since 4.8.0
+ *
+ * @param int $new_blog_id
+ * @param int $old_blog_id
+ */
+function switch_wp_roles( $new_blog_id = 0, $old_blog_id = 0 ) {
+	if ( $new_blog_id != $old_blog_id ) {
+		wp_roles()->for_blog( $new_blog_id );
+		wp_get_current_user()->for_blog( $new_blog_id );
+	}
+}
+
+/**
  * Determines if switch_to_blog() is in effect
  *
  * @since 3.5.0
diff --git src/wp-includes/ms-default-filters.php src/wp-includes/ms-default-filters.php
old mode 100644
new mode 100755
index 08d752a..c3e218b
--- src/wp-includes/ms-default-filters.php
+++ src/wp-includes/ms-default-filters.php
@@ -32,6 +32,9 @@
 add_action( 'network_user_new_created_user',   'wp_send_new_user_notifications' );
 add_filter( 'sanitize_user', 'strtolower' );
 
+// Roles
+add_action( 'switch_blog', 'switch_wp_roles', 8, 2 );
+
 // Blogs
 add_filter( 'wpmu_validate_blog_signup', 'signup_nonce_check' );
 add_action( 'wpmu_new_blog', 'wpmu_log_new_registrations', 10, 2 );
