diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 569b4802e2..e30a308231 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -146,8 +146,6 @@ add_library(core STATIC
     hle/kernel/board/nintendo/nx/secure_monitor.h
     hle/kernel/client_port.cpp
     hle/kernel/client_port.h
-    hle/kernel/client_session.cpp
-    hle/kernel/client_session.h
     hle/kernel/code_set.cpp
     hle/kernel/code_set.h
     hle/kernel/svc_results.h
@@ -170,6 +168,8 @@ add_library(core STATIC
     hle/kernel/k_affinity_mask.h
     hle/kernel/k_class_token.cpp
     hle/kernel/k_class_token.h
+    hle/kernel/k_client_session.cpp
+    hle/kernel/k_client_session.h
     hle/kernel/k_condition_variable.cpp
     hle/kernel/k_condition_variable.h
     hle/kernel/k_event.cpp
@@ -205,6 +205,10 @@ add_library(core STATIC
     hle/kernel/k_scoped_lock.h
     hle/kernel/k_scoped_resource_reservation.h
     hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+    hle/kernel/k_server_session.cpp
+    hle/kernel/k_server_session.h
+    hle/kernel/k_session.cpp
+    hle/kernel/k_session.h
     hle/kernel/k_shared_memory.cpp
     hle/kernel/k_shared_memory.h
     hle/kernel/k_slab_heap.h
@@ -233,12 +237,8 @@ add_library(core STATIC
     hle/kernel/process_capability.h
     hle/kernel/server_port.cpp
     hle/kernel/server_port.h
-    hle/kernel/server_session.cpp
-    hle/kernel/server_session.h
     hle/kernel/service_thread.cpp
     hle/kernel/service_thread.h
-    hle/kernel/session.cpp
-    hle/kernel/session.h
     hle/kernel/slab_helpers.h
     hle/kernel/svc.cpp
     hle/kernel/svc.h
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 8f12616bd4..99b7d3d82f 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -14,11 +14,9 @@
 #include "common/common_types.h"
 #include "core/hle/ipc.h"
 #include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/object.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
 #include "core/hle/result.h"
 
 namespace IPC {
@@ -137,9 +135,11 @@ public:
         if (context->Session()->IsDomain()) {
             context->AddDomainObject(std::move(iface));
         } else {
-            auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
-            context->AddMoveObject(client.get());
-            iface->ClientConnected(std::move(client), std::move(server));
+            auto* session = Kernel::KSession::Create(kernel);
+            session->Initialize(iface->GetServiceName());
+
+            context->AddMoveObject(&session->GetClientSession());
+            iface->ClientConnected(session);
         }
     }
 
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index d856b83e36..ce88da1c32 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -3,11 +3,10 @@
 // Refer to the license.txt file included.
 
 #include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/object.h"
 #include "core/hle/kernel/server_port.h"
-#include "core/hle/kernel/session.h"
 #include "core/hle/kernel/svc_results.h"
 
 namespace Kernel {
@@ -19,21 +18,22 @@ std::shared_ptr<ServerPort> ClientPort::GetServerPort() const {
     return server_port;
 }
 
-ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
+ResultVal<KClientSession*> ClientPort::Connect() {
     if (active_sessions >= max_sessions) {
         return ResultOutOfSessions;
     }
     active_sessions++;
 
-    auto [client, server] = Kernel::Session::Create(kernel, name);
+    auto* session = Kernel::KSession::Create(kernel);
+    session->Initialize(name + ":ClientPort");
 
     if (server_port->HasHLEHandler()) {
-        server_port->GetHLEHandler()->ClientConnected(client, std::move(server));
+        server_port->GetHLEHandler()->ClientConnected(session);
     } else {
-        server_port->AppendPendingSession(std::move(server));
+        server_port->AppendPendingSession(std::addressof(session->GetServerSession()));
     }
 
-    return MakeResult(std::move(client));
+    return MakeResult(std::addressof(session->GetClientSession()));
 }
 
 void ClientPort::ConnectionClosed() {
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index 77559ebf9e..0b20fef40f 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -13,7 +13,7 @@
 
 namespace Kernel {
 
-class ClientSession;
+class KClientSession;
 class KernelCore;
 class ServerPort;
 
@@ -43,7 +43,7 @@ public:
      * waiting on it to awake.
      * @returns ClientSession The client endpoint of the created Session pair, or error code.
      */
-    ResultVal<std::shared_ptr<ClientSession>> Connect();
+    ResultVal<KClientSession*> Connect();
 
     /**
      * Signifies that a previously active connection has been closed,
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
deleted file mode 100644
index fa9cad4982..0000000000
--- a/src/core/hle/kernel/client_session.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
-#include "core/hle/kernel/svc_results.h"
-#include "core/hle/result.h"
-
-namespace Kernel {
-
-ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
-
-ClientSession::~ClientSession() {
-    // This destructor will be called automatically when the last ClientSession handle is closed by
-    // the emulated application.
-    if (parent->Server()) {
-        parent->Server()->ClientDisconnected();
-    }
-}
-
-bool ClientSession::IsSignaled() const {
-    UNIMPLEMENTED();
-    return true;
-}
-
-ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel,
-                                                                std::shared_ptr<Session> parent,
-                                                                std::string name) {
-    std::shared_ptr<ClientSession> client_session{std::make_shared<ClientSession>(kernel)};
-
-    client_session->name = std::move(name);
-    client_session->parent = std::move(parent);
-
-    return MakeResult(std::move(client_session));
-}
-
-ResultCode ClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
-                                          Core::Timing::CoreTiming& core_timing) {
-    // Keep ServerSession alive until we're done working with it.
-    if (!parent->Server()) {
-        return ResultSessionClosed;
-    }
-
-    // Signal the server session that new data is available
-    return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing);
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index adfcd3c5be..ddb1e6fb2f 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -56,7 +56,10 @@ ResultVal<Handle> HandleTable::Create(Object* obj) {
     case HandleType::Event:
     case HandleType::Process:
     case HandleType::ReadableEvent:
-    case HandleType::WritableEvent: {
+    case HandleType::WritableEvent:
+    case HandleType::ClientSession:
+    case HandleType::ServerSession:
+    case HandleType::Session: {
         Handle handle{};
         Add(&handle, reinterpret_cast<KAutoObject*>(obj), {});
         return MakeResult<Handle>(handle);
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 1e831aaca6..d647d9dd37 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -19,12 +19,12 @@
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
+#include "core/hle/kernel/k_server_session.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/k_writable_event.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/object.h"
 #include "core/hle/kernel/process.h"
-#include "core/hle/kernel/server_session.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/kernel/time_manager.h"
 #include "core/memory.h"
@@ -35,24 +35,19 @@ SessionRequestHandler::SessionRequestHandler() = default;
 
 SessionRequestHandler::~SessionRequestHandler() = default;
 
-void SessionRequestHandler::ClientConnected(std::shared_ptr<ClientSession> client_session,
-                                            std::shared_ptr<ServerSession> server_session) {
-    server_session->SetHleHandler(shared_from_this());
-    client_sessions.push_back(std::move(client_session));
-    server_sessions.push_back(std::move(server_session));
+void SessionRequestHandler::ClientConnected(KSession* session) {
+    session->GetServerSession().SetHleHandler(shared_from_this());
+    sessions.push_back(session);
 }
 
-void SessionRequestHandler::ClientDisconnected(
-    const std::shared_ptr<ServerSession>& server_session) {
-    server_session->SetHleHandler(nullptr);
-    boost::range::remove_erase(server_sessions, server_session);
+void SessionRequestHandler::ClientDisconnected(KSession* session) {
+    session->GetServerSession().SetHleHandler(nullptr);
+    boost::range::remove_erase(sessions, session);
 }
 
 HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
-                                     std::shared_ptr<ServerSession> server_session_,
-                                     KThread* thread_)
-    : server_session(std::move(server_session_)),
-      thread(thread_), kernel{kernel_}, memory{memory_} {
+                                     KServerSession* server_session_, KThread* thread_)
+    : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
     cmd_buf[0] = 0;
 }
 
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 74a95bc76a..dc5c3b47d6 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -39,10 +39,10 @@ class HandleTable;
 class HLERequestContext;
 class KernelCore;
 class Process;
-class ClientSession;
-class ServerSession;
+class KServerSession;
 class KThread;
 class KReadableEvent;
+class KSession;
 class KWritableEvent;
 
 enum class ThreadWakeupReason;
@@ -72,22 +72,20 @@ public:
      * associated ServerSession alive for the duration of the connection.
      * @param server_session Owning pointer to the ServerSession associated with the connection.
      */
-    void ClientConnected(
-        std::shared_ptr<ClientSession> client_session, std::shared_ptr<ServerSession> server_session);
+    void ClientConnected(KSession* session);
 
     /**
      * Signals that a client has just disconnected from this HLE handler and releases the
      * associated ServerSession.
      * @param server_session ServerSession associated with the connection.
      */
-    void ClientDisconnected(const std::shared_ptr<ServerSession>& server_session);
+    void ClientDisconnected(KSession* session);
 
 protected:
     /// List of sessions that are connected to this handler.
     /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
     /// for the duration of the connection.
-    std::vector<std::shared_ptr<ClientSession>> client_sessions;
-    std::vector<std::shared_ptr<ServerSession>> server_sessions;
+    std::vector<KSession*> sessions;
 };
 
 /**
@@ -112,7 +110,7 @@ protected:
 class HLERequestContext {
 public:
     explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
-                               std::shared_ptr<ServerSession> session, KThread* thread);
+                               KServerSession* session, KThread* thread);
     ~HLERequestContext();
 
     /// Returns a pointer to the IPC command buffer for this request.
@@ -124,7 +122,7 @@ public:
      * Returns the session through which this request was made. This can be used as a map key to
      * access per-client data on services.
      */
-    const std::shared_ptr<Kernel::ServerSession>& Session() const {
+    Kernel::KServerSession* Session() {
         return server_session;
     }
 
@@ -288,7 +286,7 @@ private:
     void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
 
     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
-    std::shared_ptr<Kernel::ServerSession> server_session;
+    Kernel::KServerSession* server_session{};
     KThread* thread;
 
     // TODO(yuriks): Check common usage of this and optimize size accordingly
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index b292f7db25..84d509d522 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -12,6 +12,7 @@
 #include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_memory_layout.h"
 #include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/k_shared_memory.h"
 #include "core/hle/kernel/k_system_control.h"
 #include "core/hle/kernel/k_thread.h"
@@ -27,7 +28,8 @@ namespace Kernel::Init {
     HANDLER(Process, (SLAB_COUNT(Process)), ##__VA_ARGS__)                                         \
     HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__)                                         \
     HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__)                                           \
-    HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__)
+    HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__)                             \
+    HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__)
 
 namespace {
 
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
new file mode 100644
index 0000000000..0618dc2468
--- /dev/null
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -0,0 +1,31 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
+KClientSession::~KClientSession() = default;
+
+void KClientSession::Destroy() {
+    parent->OnClientClosed();
+    parent->Close();
+}
+
+void KClientSession::OnServerClosed() {}
+
+ResultCode KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+                                           Core::Timing::CoreTiming& core_timing) {
+    // Signal the server session that new data is available
+    return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing);
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/k_client_session.h
similarity index 56%
rename from src/core/hle/kernel/client_session.h
rename to src/core/hle/kernel/k_client_session.h
index 7a1d15d0c3..c4b193773d 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -1,4 +1,4 @@
-// Copyright 2019 yuzu emulator team
+// Copyright 2021 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -7,7 +7,9 @@
 #include <memory>
 #include <string>
 
+#include "core/hle/kernel/k_auto_object.h"
 #include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/result.h"
 
 union ResultCode;
@@ -23,15 +25,41 @@ class CoreTiming;
 namespace Kernel {
 
 class KernelCore;
-class Session;
+class KSession;
 class KThread;
 
-class ClientSession final : public KSynchronizationObject {
-public:
-    explicit ClientSession(KernelCore& kernel);
-    ~ClientSession() override;
+class KClientSession final
+    : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
 
-    friend class Session;
+public:
+    explicit KClientSession(KernelCore& kernel);
+    virtual ~KClientSession();
+
+    void Initialize(KSession* parent_, std::string&& name_) {
+        // Set member variables.
+        parent = parent_;
+        name = std::move(name_);
+    }
+
+    virtual void Destroy() override;
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+    constexpr KSession* GetParent() const {
+        return parent;
+    }
+
+    ResultCode SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+                               Core::Timing::CoreTiming& core_timing);
+
+    void OnServerClosed();
+
+    // DEPRECATED
+
+    static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
+    HandleType GetHandleType() const override {
+        return HANDLE_TYPE;
+    }
 
     std::string GetTypeName() const override {
         return "ClientSession";
@@ -41,25 +69,9 @@ public:
         return name;
     }
 
-    static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    ResultCode SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
-                               Core::Timing::CoreTiming& core_timing);
-
-    bool IsSignaled() const override;
-
-    void Finalize() override {}
-
 private:
-    static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
-                                                            std::shared_ptr<Session> parent,
-                                                            std::string name = "Unknown");
-
     /// The parent session, which links to the server endpoint.
-    std::shared_ptr<Session> parent;
+    KSession* parent{};
 
     /// Name of the client session (optional)
     std::string name;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
similarity index 70%
rename from src/core/hle/kernel/server_session.cpp
rename to src/core/hle/kernel/k_server_session.cpp
index bb247959c6..8cd2c283cd 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -11,43 +11,38 @@
 #include "core/core_timing.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/process.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
 #include "core/memory.h"
 
 namespace Kernel {
 
-ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
+KServerSession::KServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
 
-ServerSession::~ServerSession() {
+KServerSession::~KServerSession() {
     kernel.ReleaseServiceThread(service_thread);
 }
 
-ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
-                                                                std::shared_ptr<Session> parent,
-                                                                std::string name) {
-    std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
-
-    session->name = std::move(name);
-    session->parent = std::move(parent);
-    session->service_thread = kernel.CreateServiceThread(session->name);
-
-    return MakeResult(std::move(session));
+void KServerSession::Initialize(KSession* parent_, std::string&& name_) {
+    // Set member variables.
+    parent = parent_;
+    name = std::move(name_);
+    service_thread = kernel.CreateServiceThread(name);
 }
 
-bool ServerSession::IsSignaled() const {
-    // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
-    return !parent->Client();
+void KServerSession::Destroy() {
+    parent->OnServerClosed();
+
+    parent->Close();
 }
 
-void ServerSession::ClientDisconnected() {
+void KServerSession::OnClientClosed() {
     // We keep a shared pointer to the hle handler to keep it alive throughout
     // the call to ClientDisconnected, as ClientDisconnected invalidates the
     // hle_handler member itself during the course of the function executing.
@@ -55,19 +50,31 @@ void ServerSession::ClientDisconnected() {
     if (handler) {
         // Note that after this returns, this server session's hle_handler is
         // invalidated (set to null).
-        handler->ClientDisconnected(SharedFrom(this));
+        handler->ClientDisconnected(parent);
     }
 }
 
-void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
+bool KServerSession::IsSignaled() const {
+    ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+
+    // If the client is closed, we're always signaled.
+    if (parent->IsClientClosed()) {
+        return true;
+    }
+
+    // Otherwise, we're signaled if we have a request and aren't handling one.
+    return false;
+}
+
+void KServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
     domain_request_handlers.push_back(std::move(handler));
 }
 
-std::size_t ServerSession::NumDomainRequestHandlers() const {
+std::size_t KServerSession::NumDomainRequestHandlers() const {
     return domain_request_handlers.size();
 }
 
-ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
+ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
     if (!context.HasDomainMessageHeader()) {
         return RESULT_SUCCESS;
     }
@@ -106,21 +113,21 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
     return RESULT_SUCCESS;
 }
 
-ResultCode ServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
+ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
     u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
-    auto context = std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), thread);
+    auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread);
 
     context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
 
     if (auto strong_ptr = service_thread.lock()) {
-        strong_ptr->QueueSyncRequest(*this, std::move(context));
+        strong_ptr->QueueSyncRequest(*parent, std::move(context));
         return RESULT_SUCCESS;
     }
 
     return RESULT_SUCCESS;
 }
 
-ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
+ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
     ResultCode result = RESULT_SUCCESS;
     // If the session has been converted to a domain, handle the domain request
     if (IsDomain() && context.HasDomainMessageHeader()) {
@@ -149,8 +156,8 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
     return result;
 }
 
-ResultCode ServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
-                                            Core::Timing::CoreTiming& core_timing) {
+ResultCode KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+                                             Core::Timing::CoreTiming& core_timing) {
     return QueueSyncRequest(thread, memory);
 }
 
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/k_server_session.h
similarity index 70%
rename from src/core/hle/kernel/server_session.h
rename to src/core/hle/kernel/k_server_session.h
index 77ed18c603..ef81c4e30f 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -27,55 +27,34 @@ namespace Kernel {
 
 class HLERequestContext;
 class KernelCore;
-class Session;
+class KSession;
 class SessionRequestHandler;
 class KThread;
 
-/**
- * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
- * primitive for communication between different processes, and are used to implement service calls
- * to the various system services.
- *
- * To make a service call, the client must write the command header and parameters to the buffer
- * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
- * SVC call with its ClientSession handle. The kernel will read the command header, using it to
- * marshall the parameters to the process at the server endpoint of the session.
- * After the server replies to the request, the response is marshalled back to the caller's
- * TLS buffer and control is transferred back to it.
- */
-class ServerSession final : public KSynchronizationObject {
+class KServerSession final : public KSynchronizationObject {
+    KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
+
     friend class ServiceThread;
 
 public:
-    explicit ServerSession(KernelCore& kernel);
-    ~ServerSession() override;
+    explicit KServerSession(KernelCore& kernel);
+    virtual ~KServerSession() override;
 
-    friend class Session;
+    virtual void Destroy() override;
 
-    static ResultVal<std::shared_ptr<ServerSession>> Create(KernelCore& kernel,
-                                                            std::shared_ptr<Session> parent,
-                                                            std::string name = "Unknown");
+    void Initialize(KSession* parent_, std::string&& name_);
 
-    std::string GetTypeName() const override {
-        return "ServerSession";
+    constexpr KSession* GetParent() {
+        return parent;
     }
 
-    std::string GetName() const override {
-        return name;
+    constexpr const KSession* GetParent() const {
+        return parent;
     }
 
-    static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
+    virtual bool IsSignaled() const override;
 
-    Session* GetParent() {
-        return parent.get();
-    }
-
-    const Session* GetParent() const {
-        return parent.get();
-    }
+    void OnClientClosed();
 
     /**
      * Sets the HLE handler for the session. This handler will be called to service IPC requests
@@ -98,9 +77,6 @@ public:
     ResultCode HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
                                  Core::Timing::CoreTiming& core_timing);
 
-    /// Called when a client disconnection occurs.
-    void ClientDisconnected();
-
     /// Adds a new domain request handler to the collection of request handlers within
     /// this ServerSession instance.
     void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler);
@@ -124,9 +100,20 @@ public:
         convert_to_domain = true;
     }
 
-    bool IsSignaled() const override;
+    // DEPRECATED
 
-    void Finalize() override {}
+    std::string GetTypeName() const override {
+        return "ServerSession";
+    }
+
+    std::string GetName() const override {
+        return name;
+    }
+
+    static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
+    HandleType GetHandleType() const override {
+        return HANDLE_TYPE;
+    }
 
 private:
     /// Queues a sync request from the emulated application.
@@ -139,9 +126,6 @@ private:
     /// object handle.
     ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
 
-    /// The parent session, which links to the client endpoint.
-    std::shared_ptr<Session> parent;
-
     /// This session's HLE request handler (applicable when not a domain)
     std::shared_ptr<SessionRequestHandler> hle_handler;
 
@@ -156,6 +140,9 @@ private:
 
     /// Thread to dispatch service requests
     std::weak_ptr<ServiceThread> service_thread;
+
+    /// KSession that owns this KServerSession
+    KSession* parent{};
 };
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
new file mode 100644
index 0000000000..ca1cf18cd0
--- /dev/null
+++ b/src/core/hle/kernel/k_session.cpp
@@ -0,0 +1,67 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
+
+namespace Kernel {
+
+KSession::KSession(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel}, server{kernel}, client{kernel} {}
+KSession::~KSession() = default;
+
+void KSession::Initialize(std::string&& name_) {
+    // Increment reference count.
+    // Because reference count is one on creation, this will result
+    // in a reference count of two. Thus, when both server and client are closed
+    // this object will be destroyed.
+    Open();
+
+    // Create our sub sessions.
+    KAutoObject::Create(std::addressof(server));
+    KAutoObject::Create(std::addressof(client));
+
+    // Initialize our sub sessions.
+    server.Initialize(this, name_ + ":Server");
+    client.Initialize(this, name_ + ":Client");
+
+    // Set state and name.
+    SetState(State::Normal);
+    name = std::move(name_);
+
+    // Set our owner process.
+    process = kernel.CurrentProcess();
+    process->Open();
+
+    // Mark initialized.
+    initialized = true;
+}
+
+void KSession::Finalize() {}
+
+void KSession::OnServerClosed() {
+    if (GetState() == State::Normal) {
+        SetState(State::ServerClosed);
+        client.OnServerClosed();
+    }
+}
+
+void KSession::OnClientClosed() {
+    if (GetState() == State::Normal) {
+        SetState(State::ClientClosed);
+        server.OnClientClosed();
+    }
+}
+
+void KSession::PostDestroy(uintptr_t arg) {
+    // Release the session count resource the owner process holds.
+    Process* owner = reinterpret_cast<Process*>(arg);
+    owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1);
+    owner->Close();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
new file mode 100644
index 0000000000..1d24e80cdd
--- /dev/null
+++ b/src/core/hle/kernel/k_session.h
@@ -0,0 +1,108 @@
+// Copyright 2021 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <string>
+
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/slab_helpers.h"
+
+namespace Kernel {
+
+class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject);
+
+private:
+    enum class State : u8 {
+        Invalid = 0,
+        Normal = 1,
+        ClientClosed = 2,
+        ServerClosed = 3,
+    };
+
+public:
+    explicit KSession(KernelCore& kernel);
+    virtual ~KSession() override;
+
+    void Initialize(std::string&& name_);
+
+    virtual void Finalize() override;
+
+    virtual bool IsInitialized() const override {
+        return initialized;
+    }
+
+    virtual uintptr_t GetPostDestroyArgument() const override {
+        return reinterpret_cast<uintptr_t>(process);
+    }
+
+    static void PostDestroy(uintptr_t arg);
+
+    void OnServerClosed();
+
+    void OnClientClosed();
+
+    bool IsServerClosed() const {
+        return this->GetState() != State::Normal;
+    }
+
+    bool IsClientClosed() const {
+        return this->GetState() != State::Normal;
+    }
+
+    KClientSession& GetClientSession() {
+        return client;
+    }
+
+    KServerSession& GetServerSession() {
+        return server;
+    }
+
+    const KClientSession& GetClientSession() const {
+        return client;
+    }
+
+    const KServerSession& GetServerSession() const {
+        return server;
+    }
+
+    const ClientPort* GetParent() const {
+        return port;
+    }
+
+    // DEPRECATED
+
+    std::string GetName() const override {
+        return name;
+    }
+
+    static constexpr HandleType HANDLE_TYPE = HandleType::Session;
+    HandleType GetHandleType() const override {
+        return HANDLE_TYPE;
+    }
+
+private:
+    void SetState(State state) {
+        atomic_state = static_cast<u8>(state);
+    }
+
+    State GetState() const {
+        return static_cast<State>(atomic_state.load());
+    }
+
+private:
+    KServerSession server;
+    KClientSession client;
+    std::atomic<std::underlying_type<State>::type> atomic_state{
+        static_cast<std::underlying_type<State>::type>(State::Invalid)};
+    ClientPort* port{};
+    std::string name;
+    Process* process{};
+    bool initialized{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 850436eb3e..ecced1034c 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -33,11 +33,13 @@ class ClientPort;
 class GlobalSchedulerContext;
 class HandleTable;
 class KAutoObjectWithListContainer;
+class KClientSession;
 class KEvent;
 class KLinkedListNode;
 class KMemoryManager;
 class KResourceLimit;
 class KScheduler;
+class KSession;
 class KSharedMemory;
 class KThread;
 class KWritableEvent;
@@ -272,6 +274,10 @@ public:
             return slab_heap_container->linked_list_node;
         } else if constexpr (std::is_same_v<T, KWritableEvent>) {
             return slab_heap_container->writeable_event;
+        } else if constexpr (std::is_same_v<T, KClientSession>) {
+            return slab_heap_container->client_session;
+        } else if constexpr (std::is_same_v<T, KSession>) {
+            return slab_heap_container->session;
         }
     }
 
@@ -312,6 +318,8 @@ private:
         KSlabHeap<KSharedMemory> shared_memory;
         KSlabHeap<KLinkedListNode> linked_list_node;
         KSlabHeap<KWritableEvent> writeable_event;
+        KSlabHeap<KClientSession> client_session;
+        KSlabHeap<KSession> session;
     };
 
     std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 5d17346ade..8626b56fd3 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -5,10 +5,10 @@
 #include <tuple>
 #include "common/assert.h"
 #include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/k_server_session.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/object.h"
 #include "core/hle/kernel/server_port.h"
-#include "core/hle/kernel/server_session.h"
 #include "core/hle/kernel/svc_results.h"
 
 namespace Kernel {
@@ -16,17 +16,17 @@ namespace Kernel {
 ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
 ServerPort::~ServerPort() = default;
 
-ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
+ResultVal<KServerSession*> ServerPort::Accept() {
     if (pending_sessions.empty()) {
         return ResultNotFound;
     }
 
-    auto session = std::move(pending_sessions.back());
+    auto* session = pending_sessions.back();
     pending_sessions.pop_back();
-    return MakeResult(std::move(session));
+    return MakeResult(session);
 }
 
-void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) {
+void ServerPort::AppendPendingSession(KServerSession* pending_session) {
     pending_sessions.push_back(std::move(pending_session));
     if (pending_sessions.size() == 1) {
         NotifyAvailable();
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 29b4f25092..eebceaa2a1 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -17,7 +17,7 @@ namespace Kernel {
 
 class ClientPort;
 class KernelCore;
-class ServerSession;
+class KServerSession;
 class SessionRequestHandler;
 
 class ServerPort final : public KSynchronizationObject {
@@ -55,7 +55,7 @@ public:
      * Accepts a pending incoming connection on this port. If there are no pending sessions, will
      * return ERR_NO_PENDING_SESSIONS.
      */
-    ResultVal<std::shared_ptr<ServerSession>> Accept();
+    ResultVal<KServerSession*> Accept();
 
     /// Whether or not this server port has an HLE handler available.
     bool HasHLEHandler() const {
@@ -77,7 +77,7 @@ public:
 
     /// Appends a ServerSession to the collection of ServerSessions
     /// waiting to be accepted by this port.
-    void AppendPendingSession(std::shared_ptr<ServerSession> pending_session);
+    void AppendPendingSession(KServerSession* pending_session);
 
     bool IsSignaled() const override;
 
@@ -85,7 +85,7 @@ public:
 
 private:
     /// ServerSessions waiting to be accepted by the port
-    std::vector<std::shared_ptr<ServerSession>> pending_sessions;
+    std::vector<KServerSession*> pending_sessions;
 
     /// This session's HLE request handler template (optional)
     /// ServerSessions created from this port inherit a reference to this handler.
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index ee46f3e21b..04be8a5023 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -13,8 +13,8 @@
 #include "common/scope_exit.h"
 #include "common/thread.h"
 #include "core/core.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/server_session.h"
 #include "core/hle/kernel/service_thread.h"
 #include "core/hle/lock.h"
 #include "video_core/renderer_base.h"
@@ -26,7 +26,7 @@ public:
     explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
     ~Impl();
 
-    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+    void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
 
 private:
     std::vector<std::thread> threads;
@@ -69,18 +69,27 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
         });
 }
 
-void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
+void ServiceThread::Impl::QueueSyncRequest(KSession& session,
                                            std::shared_ptr<HLERequestContext>&& context) {
     {
         std::unique_lock lock{queue_mutex};
 
-        // ServerSession owns the service thread, so we cannot caption a strong pointer here in the
-        // event that the ServerSession is terminated.
-        std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)};
-        requests.emplace([weak_ptr, context{std::move(context)}]() {
-            if (auto strong_ptr = weak_ptr.lock()) {
-                strong_ptr->CompleteSyncRequest(*context);
+        // Open a reference to the session to ensure it is not closes while the service request
+        // completes asynchronously.
+        session.Open();
+
+        requests.emplace([session_ptr{&session}, context{std::move(context)}]() {
+            // Close the reference.
+            SCOPE_EXIT({ session_ptr->Close(); });
+
+            // If the session has been closed, we are done.
+            if (session_ptr->IsServerClosed()) {
+                return;
             }
+
+            // Complete the service request.
+            KScopedAutoObject server_session{&session_ptr->GetServerSession()};
+            server_session->CompleteSyncRequest(*context);
         });
     }
     condition.notify_one();
@@ -102,7 +111,7 @@ ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const
 
 ServiceThread::~ServiceThread() = default;
 
-void ServiceThread::QueueSyncRequest(ServerSession& session,
+void ServiceThread::QueueSyncRequest(KSession& session,
                                      std::shared_ptr<HLERequestContext>&& context) {
     impl->QueueSyncRequest(session, std::move(context));
 }
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index 025ab8fb54..6a7fd7c56f 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -11,14 +11,14 @@ namespace Kernel {
 
 class HLERequestContext;
 class KernelCore;
-class ServerSession;
+class KSession;
 
 class ServiceThread final {
 public:
     explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
     ~ServiceThread();
 
-    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+    void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
 
 private:
     class Impl;
diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp
deleted file mode 100644
index 8830d4e916..0000000000
--- a/src/core/hle/kernel/session.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/assert.h"
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/k_scoped_resource_reservation.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
-
-namespace Kernel {
-
-Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {}
-Session::~Session() {
-    // Release reserved resource when the Session pair was created.
-    kernel.GetSystemResourceLimit()->Release(LimitableResource::Sessions, 1);
-}
-
-Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
-    // Reserve a new session from the resource limit.
-    KScopedResourceReservation session_reservation(kernel.GetSystemResourceLimit(),
-                                                   LimitableResource::Sessions);
-    ASSERT(session_reservation.Succeeded());
-    auto session{std::make_shared<Session>(kernel)};
-    auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()};
-    auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()};
-
-    session->name = std::move(name);
-    session->client = client_session;
-    session->server = server_session;
-
-    session_reservation.Commit();
-    return std::make_pair(std::move(client_session), std::move(server_session));
-}
-
-bool Session::IsSignaled() const {
-    UNIMPLEMENTED();
-    return true;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
deleted file mode 100644
index fa3c5651af..0000000000
--- a/src/core/hle/kernel/session.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "core/hle/kernel/k_synchronization_object.h"
-
-namespace Kernel {
-
-class ClientSession;
-class ServerSession;
-
-/**
- * Parent structure to link the client and server endpoints of a session with their associated
- * client port.
- */
-class Session final : public KSynchronizationObject {
-public:
-    explicit Session(KernelCore& kernel);
-    ~Session() override;
-
-    using SessionPair = std::pair<std::shared_ptr<ClientSession>, std::shared_ptr<ServerSession>>;
-
-    static SessionPair Create(KernelCore& kernel, std::string name = "Unknown");
-
-    std::string GetName() const override {
-        return name;
-    }
-
-    static constexpr HandleType HANDLE_TYPE = HandleType::Session;
-    HandleType GetHandleType() const override {
-        return HANDLE_TYPE;
-    }
-
-    bool IsSignaled() const override;
-
-    void Finalize() override {}
-
-    std::shared_ptr<ClientSession> Client() {
-        if (auto result{client.lock()}) {
-            return result;
-        }
-        return {};
-    }
-
-    std::shared_ptr<ServerSession> Server() {
-        if (auto result{server.lock()}) {
-            return result;
-        }
-        return {};
-    }
-
-private:
-    std::string name;
-    std::weak_ptr<ClientSession> client;
-    std::weak_ptr<ServerSession> server;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index b83ee3e699..28c45e8a32 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -22,9 +22,9 @@
 #include "core/core_timing_util.h"
 #include "core/cpu_manager.h"
 #include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/k_address_arbiter.h"
+#include "core/hle/kernel/k_client_session.h"
 #include "core/hle/kernel/k_condition_variable.h"
 #include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_memory_block.h"
@@ -323,12 +323,12 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
 
     auto client_port = it->second;
 
-    std::shared_ptr<ClientSession> client_session;
+    KClientSession* client_session{};
     CASCADE_RESULT(client_session, client_port->Connect());
 
     // Return the client session
     auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
-    CASCADE_RESULT(*out_handle, handle_table.Create(client_session.get()));
+    CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
     return RESULT_SUCCESS;
 }
 
@@ -340,16 +340,14 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
 
 /// Makes a blocking IPC call to an OS service.
 static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
-    auto& kernel = system.Kernel();
-    const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
-    auto session = handle_table.Get<ClientSession>(handle);
-    if (!session) {
-        LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
-        return ResultInvalidHandle;
-    }
-
     LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
 
+    auto& kernel = system.Kernel();
+
+    KScopedAutoObject session =
+        kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
+    R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+
     auto thread = kernel.CurrentScheduler()->GetCurrentThread();
     {
         KScopedSchedulerLock lock(kernel);
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index cf8ad7598b..ae995df6b0 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -14,7 +14,6 @@
 #include "core/frontend/applets/web_browser.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_writable_event.h"
-#include "core/hle/kernel/server_session.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applet_ae.h"
 #include "core/hle/service/am/applet_oe.h"
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 49c1db42a4..6b3ebeb8f3 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -14,7 +14,6 @@
 #include "core/hardware_properties.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_shared_memory.h"
 #include "core/hle/kernel/k_writable_event.h"
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index b0cb07d245..c8519e2dba 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -4,7 +4,6 @@
 
 #include "common/logging/log.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/service/mm/mm_u.h"
 #include "core/hle/service/sm/sm.h"
 
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 9164455178..076f50b0bd 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -21,11 +21,8 @@ class System;
 }
 
 namespace Kernel {
-class ClientPort;
-class ServerPort;
-class ServerSession;
 class HLERequestContext;
-} // namespace Kernel
+}
 
 namespace Service {
 
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index b34fe4bc26..cb397fcc7e 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -5,9 +5,9 @@
 #include "common/assert.h"
 #include "common/logging/log.h"
 #include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/session.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/service/sm/controller.h"
 
 namespace Service::SM {
@@ -30,7 +30,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
     rb.Push(RESULT_SUCCESS);
-    rb.PushMoveObjects(ctx.Session()->GetParent()->Client().get());
+    rb.PushMoveObjects(ctx.Session()->GetParent()->GetClientSession());
 }
 
 void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 62f7a53587..66e41277f5 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -7,7 +7,9 @@
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/server_port.h"
 #include "core/hle/result.h"
 #include "core/hle/service/sm/controller.h"
@@ -89,13 +91,6 @@ ResultVal<std::shared_ptr<Kernel::ClientPort>> ServiceManager::GetServicePort(
     return MakeResult(it->second);
 }
 
-ResultVal<std::shared_ptr<Kernel::ClientSession>> ServiceManager::ConnectToService(
-    const std::string& name) {
-
-    CASCADE_RESULT(auto client_port, GetServicePort(name));
-    return client_port->Connect();
-}
-
 SM::~SM() = default;
 
 /**
@@ -130,19 +125,20 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
         return;
     }
 
-    auto [client, server] = Kernel::Session::Create(kernel, name);
+    auto* session = Kernel::KSession::Create(kernel);
+    session->Initialize(std::move(name));
 
     const auto& server_port = client_port.Unwrap()->GetServerPort();
     if (server_port->GetHLEHandler()) {
-        server_port->GetHLEHandler()->ClientConnected(client, server);
+        server_port->GetHLEHandler()->ClientConnected(session);
     } else {
-        server_port->AppendPendingSession(server);
+        server_port->AppendPendingSession(&session->GetServerSession());
     }
 
-    LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());
+    LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetObjectId());
     IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
     rb.Push(RESULT_SUCCESS);
-    rb.PushMoveObjects(client.get());
+    rb.PushMoveObjects(session->GetClientSession());
 }
 
 void SM::RegisterService(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 3f46ae44f5..8f6862fa9a 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -22,7 +22,7 @@ class System;
 
 namespace Kernel {
 class ClientPort;
-class ClientSession;
+class KClientSession;
 class KernelCore;
 class ServerPort;
 class SessionRequestHandler;
@@ -59,7 +59,6 @@ public:
                                                                    u32 max_sessions);
     ResultCode UnregisterService(const std::string& name);
     ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
-    ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
 
     template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
     std::shared_ptr<T> GetService(const std::string& service_name) const {
@@ -81,7 +80,7 @@ private:
     std::weak_ptr<SM> sm_interface;
     std::unique_ptr<Controller> controller_interface;
 
-    /// Map of registered services, retrieved using GetServicePort or ConnectToService.
+    /// Map of registered services, retrieved using GetServicePort.
     std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services;
 
     /// Kernel context
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 30283f2397..413a00ae02 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -9,7 +9,6 @@
 #include "core/hardware_properties.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/client_port.h"
-#include "core/hle/kernel/client_session.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/service/time/interface.h"