diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 7107f4f789..98fd5f1e41 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -52,6 +52,7 @@ add_library(common STATIC
     fiber.cpp
     fiber.h
     fixed_point.h
+    free_region_manager.h
     fs/file.cpp
     fs/file.h
     fs/fs.cpp
diff --git a/src/common/free_region_manager.h b/src/common/free_region_manager.h
new file mode 100644
index 0000000000..2e590d6094
--- /dev/null
+++ b/src/common/free_region_manager.h
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+#include <boost/icl/interval_set.hpp>
+
+namespace Common {
+
+class FreeRegionManager {
+public:
+    explicit FreeRegionManager() = default;
+    ~FreeRegionManager() = default;
+
+    void SetAddressSpace(void* start, size_t size) {
+        this->FreeBlock(start, size);
+    }
+
+    std::pair<void*, size_t> FreeBlock(void* block_ptr, size_t size) {
+        std::scoped_lock lk(m_mutex);
+
+        // Check to see if we are adjacent to any regions.
+        auto start_address = reinterpret_cast<uintptr_t>(block_ptr);
+        auto end_address = start_address + size;
+        auto it = m_free_regions.find({start_address - 1, end_address + 1});
+
+        // If we are, join with them, ensuring we stay in bounds.
+        if (it != m_free_regions.end()) {
+            start_address = std::min(start_address, it->lower());
+            end_address = std::max(end_address, it->upper());
+        }
+
+        // Free the relevant region.
+        m_free_regions.insert({start_address, end_address});
+
+        // Return the adjusted pointers.
+        block_ptr = reinterpret_cast<void*>(start_address);
+        size = end_address - start_address;
+        return {block_ptr, size};
+    }
+
+    void AllocateBlock(void* block_ptr, size_t size) {
+        std::scoped_lock lk(m_mutex);
+
+        auto address = reinterpret_cast<uintptr_t>(block_ptr);
+        m_free_regions.subtract({address, address + size});
+    }
+
+private:
+    std::mutex m_mutex;
+    boost::icl::interval_set<uintptr_t> m_free_regions;
+};
+
+} // namespace Common