It works in a very similar way to how one user's apps are isolated from each other. Whereas apps' private data used to be stored in
/data/data, now it's stored in
/data/users/n, where n is the ID of the user profile. Each user gets their own directory, and each app that user uses gets a directory within that. POSIX file system permissions protect the data directories, not only from other apps, but also from other users.
Because Android uses the existing POSIX process, UID, and file system security mechanism, the same way it does for different apps, the implementation is already secure without needing anything special at the OS level, or a more complicated (and thus battery-hungry) solution such as virtualization. Virtualization would work poorly for this anyway: providing virtualized access to the network, Bluetooth, NFC hardware, etc. such that all users could control it would add a lot of unnecessary complexity to the driver stacks.
When both users run the same app (such as the launcher), there are actually two instances of that app. They share one APK file, but they have two separate processes running under different POSIX user IDs, and they each look in their own data directory. This way, nothing they might write to is shared between the two users, but read-only parts (such as the app's executable code from the APK file) are shared, reducing RAM and storage use. Both instances might be running at once, and switching users just changes which one is the foreground app on the screen. Nothing needs to be reloaded or restarted.
This works the same way whether the app is a built-in one, or one installed by the user. There's an awkward side-effect of that: when one user upgrades an app, the upgrade affects all users, even if the new version needs more permissions.
Only apps with the INTERACT_ACROSS_USERS permission are special. Such apps get access to APIs within the Android framework to look at all the users' data directories at once. The lock screen is an example of such an app: in one instance, it needs to be able to read each user's settings and profile picture, in order to show the "switch users" GUI.