diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs
index cebfbde121..0dd5355f42 100644
--- a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs
+++ b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs
@@ -1,6 +1,7 @@
 using ARMeilleure.CodeGen.Linking;
 using ARMeilleure.CodeGen.RegisterAllocators;
 using ARMeilleure.IntermediateRepresentation;
+using Ryujinx.Common.Memory;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -59,7 +60,7 @@ namespace ARMeilleure.CodeGen.Arm64
 
         public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
         {
-            _stream = new MemoryStream();
+            _stream = MemoryStreamManager.Shared.GetStream();
 
             AllocResult = allocResult;
 
diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs
index c15deadc53..2ea4208b33 100644
--- a/ARMeilleure/CodeGen/X86/Assembler.cs
+++ b/ARMeilleure/CodeGen/X86/Assembler.cs
@@ -1,5 +1,6 @@
 using ARMeilleure.CodeGen.Linking;
 using ARMeilleure.IntermediateRepresentation;
+using Ryujinx.Common.Memory;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -1285,7 +1286,7 @@ namespace ARMeilleure.CodeGen.X86
             // Write the code, ignoring the dummy bytes after jumps, into a new stream.
             _stream.Seek(0, SeekOrigin.Begin);
 
-            using var codeStream = new MemoryStream();
+            using var codeStream = MemoryStreamManager.Shared.GetStream();
             var assembler = new Assembler(codeStream, HasRelocs);
 
             bool hasRelocs = HasRelocs;
diff --git a/ARMeilleure/CodeGen/X86/CodeGenContext.cs b/ARMeilleure/CodeGen/X86/CodeGenContext.cs
index eee71bd78a..899487241c 100644
--- a/ARMeilleure/CodeGen/X86/CodeGenContext.cs
+++ b/ARMeilleure/CodeGen/X86/CodeGenContext.cs
@@ -1,5 +1,6 @@
 using ARMeilleure.CodeGen.RegisterAllocators;
 using ARMeilleure.IntermediateRepresentation;
+using Ryujinx.Common.Memory;
 using System.IO;
 using System.Numerics;
 
@@ -22,7 +23,7 @@ namespace ARMeilleure.CodeGen.X86
 
         public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
         {
-            _stream = new MemoryStream();
+            _stream = MemoryStreamManager.Shared.GetStream();
             _blockLabels = new Operand[blocksCount];
 
             AllocResult = allocResult;
diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs
index 276ec788b9..0b23fd043b 100644
--- a/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/ARMeilleure/Translation/PTC/Ptc.cs
@@ -6,6 +6,7 @@ using ARMeilleure.Memory;
 using Ryujinx.Common;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using System;
 using System.Buffers.Binary;
 using System.Collections.Generic;
@@ -150,10 +151,10 @@ namespace ARMeilleure.Translation.PTC
 
         private void InitializeCarriers()
         {
-            _infosStream = new MemoryStream();
+            _infosStream = MemoryStreamManager.Shared.GetStream();
             _codesList = new List<byte[]>();
-            _relocsStream = new MemoryStream();
-            _unwindInfosStream = new MemoryStream();
+            _relocsStream = MemoryStreamManager.Shared.GetStream();
+            _unwindInfosStream = MemoryStreamManager.Shared.GetStream();
         }
 
         private void DisposeCarriers()
diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs
index 030ccff5fa..391e29c76f 100644
--- a/ARMeilleure/Translation/PTC/PtcProfiler.cs
+++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs
@@ -1,6 +1,7 @@
 using ARMeilleure.State;
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using System;
 using System.Buffers.Binary;
 using System.Collections.Concurrent;
@@ -182,7 +183,7 @@ namespace ARMeilleure.Translation.PTC
                     return false;
                 }
 
-                using (MemoryStream stream = new MemoryStream())
+                using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
                 {
                     Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
 
@@ -274,7 +275,7 @@ namespace ARMeilleure.Translation.PTC
 
             outerHeader.SetHeaderHash();
 
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             {
                 Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
 
diff --git a/Directory.Packages.props b/Directory.Packages.props
index eda7d395ad..14d440753b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -22,6 +22,7 @@
     <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
     <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+    <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
     <PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
     <PackageVersion Include="NUnit" Version="3.13.3" />
     <PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
diff --git a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
index ea404d2a22..21da6fc012 100644
--- a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
+++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
@@ -12,19 +12,5 @@ namespace Ryujinx.Common
         {
             return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
         }
-
-        public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
-            where T : unmanaged
-        {
-            ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
-
-            writer.Write(data);
-        }
-
-        public static void Write(this BinaryWriter writer, UInt128 value)
-        {
-            writer.Write((ulong)value);
-            writer.Write((ulong)(value >> 64));
-        }
     }
 }
diff --git a/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
new file mode 100644
index 0000000000..fddc8c1b74
--- /dev/null
+++ b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
@@ -0,0 +1,28 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Common
+{
+    public static class BinaryWriterExtensions
+    {
+        public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
+            where T : unmanaged
+        {
+            ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
+
+            writer.Write(data);
+        }
+
+        public static void Write(this BinaryWriter writer, UInt128 value)
+        {
+            writer.Write((ulong)value);
+            writer.Write((ulong)(value >> 64));
+        }
+
+        public static void Write(this BinaryWriter writer, MemoryStream stream)
+        {
+            stream.CopyTo(writer.BaseStream);
+        }
+    }
+}
diff --git a/Ryujinx.Common/Extensions/StreamExtensions.cs b/Ryujinx.Common/Extensions/StreamExtensions.cs
new file mode 100644
index 0000000000..f6fc870abf
--- /dev/null
+++ b/Ryujinx.Common/Extensions/StreamExtensions.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Buffers.Binary;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Common
+{
+    public static class StreamExtensions
+    {
+        /// <summary>
+        /// Writes a <cref="ReadOnlySpan<int>" /> to this stream.
+        ///
+        /// This default implementation converts each buffer value to a stack-allocated
+        /// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />.
+        /// </summary>
+        /// <param name="stream">The stream to be written to</param>
+        /// <param name="buffer">The buffer of values to be written</param>
+        public static void Write(this Stream stream, ReadOnlySpan<int> buffer)
+        {
+            if (buffer.Length == 0)
+            {
+                return;
+            }
+
+            if (BitConverter.IsLittleEndian)
+            {
+                ReadOnlySpan<byte> byteBuffer = MemoryMarshal.Cast<int, byte>(buffer);
+                stream.Write(byteBuffer);
+            }
+            else
+            {
+                Span<byte> byteBuffer = stackalloc byte[sizeof(int)];
+
+                foreach (int value in buffer)
+                {
+                    BinaryPrimitives.WriteInt32LittleEndian(byteBuffer, value);
+                    stream.Write(byteBuffer);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Writes a four-byte signed integer to this stream. The current position
+        /// of the stream is advanced by four.
+        /// </summary>
+        /// <param name="stream">The stream to be written to</param>
+        /// <param name="value">The value to be written</param>
+        public static void Write(this Stream stream, int value)
+        {
+            Span<byte> buffer = stackalloc byte[sizeof(int)];
+            BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
+            stream.Write(buffer);
+        }
+
+        /// <summary>
+        /// Writes an eight-byte signed integer to this stream. The current position
+        /// of the stream is advanced by eight.
+        /// </summary>
+        /// <param name="stream">The stream to be written to</param>
+        /// <param name="value">The value to be written</param>
+        public static void Write(this Stream stream, long value)
+        {
+            Span<byte> buffer = stackalloc byte[sizeof(long)];
+            BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
+            stream.Write(buffer);
+        }
+
+        /// <summary>
+        // Writes a four-byte unsigned integer to this stream. The current position
+        // of the stream is advanced by four.
+        /// </summary>
+        /// <param name="stream">The stream to be written to</param>
+        /// <param name="value">The value to be written</param>
+        public static void Write(this Stream stream, uint value)
+        {
+            Span<byte> buffer = stackalloc byte[sizeof(uint)];
+            BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
+            stream.Write(buffer);
+        }
+
+        /// <summary>
+        /// Writes an eight-byte unsigned integer to this stream. The current
+        /// position of the stream is advanced by eight.
+        /// </summary>
+        /// <param name="stream">The stream to be written to</param>
+        /// <param name="value">The value to be written</param>
+        public static void Write(this Stream stream, ulong value)
+        {
+            Span<byte> buffer = stackalloc byte[sizeof(ulong)];
+            BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
+            stream.Write(buffer);
+        }
+
+        /// <summary>
+        /// Writes the contents of source to stream by calling source.CopyTo(stream).
+        /// Provides consistency with other Stream.Write methods.
+        /// </summary>
+        /// <param name="stream">The stream to be written to</param>
+        /// <param name="source">The stream to be read from</param>
+        public static void Write(this Stream stream, Stream source)
+        {
+            source.CopyTo(stream);
+        }
+
+        /// <summary>
+        /// Writes a sequence of bytes to the Stream.
+        /// </summary>
+        /// <param name="stream">The stream to be written to.</param>
+        /// <param name="value">The byte to be written</param>
+        /// <param name="count">The number of times the value should be written</param>
+        public static void WriteByte(this Stream stream, byte value, int count)
+        {
+            if (count <= 0)
+            {
+                return;
+            }
+
+            const int BlockSize = 16;
+
+            int blockCount = count / BlockSize;
+            if (blockCount > 0)
+            {
+                Span<byte> span = stackalloc byte[BlockSize];
+                span.Fill(value);
+                for (int x = 0; x < blockCount; x++)
+                {
+                    stream.Write(span);
+                }
+            }
+
+            int nonBlockBytes = count % BlockSize;
+            for (int x = 0; x < nonBlockBytes; x++)
+            {
+                stream.WriteByte(value);
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Common/Memory/MemoryStreamManager.cs b/Ryujinx.Common/Memory/MemoryStreamManager.cs
new file mode 100644
index 0000000000..68b8299938
--- /dev/null
+++ b/Ryujinx.Common/Memory/MemoryStreamManager.cs
@@ -0,0 +1,99 @@
+using Microsoft.IO;
+using System;
+
+namespace Ryujinx.Common.Memory
+{
+    public static class MemoryStreamManager
+    {
+        private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager();
+
+        /// <summary>
+        /// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x
+        /// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use
+        /// and b) return them as <c>RecyclableMemoryStream</c> so we don't have to cast.
+        /// </summary>
+        public static class Shared
+        {
+            /// <summary>
+            /// Retrieve a new <c>MemoryStream</c> object with no tag and a default initial capacity.
+            /// </summary>
+            /// <returns>A <c>RecyclableMemoryStream</c></returns>
+            public static RecyclableMemoryStream GetStream()
+                => new RecyclableMemoryStream(_shared);
+
+            /// <summary>
+            /// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided
+            /// buffer. The provided buffer is not wrapped or used after construction.
+            /// </summary>
+            /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
+            /// <param name="buffer">The byte buffer to copy data from</param>
+            /// <returns>A <c>RecyclableMemoryStream</c></returns>
+            public static RecyclableMemoryStream GetStream(byte[] buffer)
+                => GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length);
+
+            /// <summary>
+            /// Retrieve a new <c>MemoryStream</c> object with the given tag and with contents copied from the provided
+            /// buffer. The provided buffer is not wrapped or used after construction.
+            /// </summary>
+            /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
+            /// <param name="buffer">The byte buffer to copy data from</param>
+            /// <returns>A <c>RecyclableMemoryStream</c></returns>
+            public static RecyclableMemoryStream GetStream(ReadOnlySpan<byte> buffer)
+                => GetStream(Guid.NewGuid(), null, buffer);
+
+            /// <summary>
+            /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
+            /// buffer. The provided buffer is not wrapped or used after construction.
+            /// </summary>
+            /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
+            /// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
+            /// <param name="tag">A tag which can be used to track the source of the stream</param>
+            /// <param name="buffer">The byte buffer to copy data from</param>
+            /// <returns>A <c>RecyclableMemoryStream</c></returns>
+            public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan<byte> buffer)
+            {
+                RecyclableMemoryStream stream = null;
+                try
+                {
+                    stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length);
+                    stream.Write(buffer);
+                    stream.Position = 0;
+                    return stream;
+                }
+                catch
+                {
+                    stream?.Dispose();
+                    throw;
+                }
+            }
+
+            /// <summary>
+            /// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
+            /// buffer. The provided buffer is not wrapped or used after construction.
+            /// </summary>
+            /// <remarks>The new stream's position is set to the beginning of the stream when returned</remarks>
+            /// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
+            /// <param name="tag">A tag which can be used to track the source of the stream</param>
+            /// <param name="buffer">The byte buffer to copy data from</param>
+            /// <param name="offset">The offset from the start of the buffer to copy from</param>
+            /// <param name="count">The number of bytes to copy from the buffer</param>
+            /// <returns>A <c>RecyclableMemoryStream</c></returns>
+            public static RecyclableMemoryStream GetStream(Guid id, string tag, byte[] buffer, int offset, int count)
+            {
+                RecyclableMemoryStream stream = null;
+                try
+                {
+                    stream = new RecyclableMemoryStream(_shared, id, tag, count);
+                    stream.Write(buffer, offset, count);
+                    stream.Position = 0;
+                    return stream;
+                }
+                catch
+                {
+                    stream?.Dispose();
+                    throw;
+                }
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index c307f524ed..c02b11e0ce 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -7,6 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
     <PackageReference Include="MsgPack.Cli" />
     <PackageReference Include="System.Management" />
   </ItemGroup>
diff --git a/Ryujinx.Common/Utilities/EmbeddedResources.cs b/Ryujinx.Common/Utilities/EmbeddedResources.cs
index e7c8d7d701..22b55f1617 100644
--- a/Ryujinx.Common/Utilities/EmbeddedResources.cs
+++ b/Ryujinx.Common/Utilities/EmbeddedResources.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.Common.Utilities;
 using System;
 using System.IO;
 using System.Linq;
@@ -38,12 +40,7 @@ namespace Ryujinx.Common
                     return null;
                 }
 
-                using (var mem = new MemoryStream())
-                {
-                    stream.CopyTo(mem);
-
-                    return mem.ToArray();
-                }
+                return StreamUtils.StreamToBytes(stream);
             }
         }
 
@@ -56,12 +53,7 @@ namespace Ryujinx.Common
                     return null;
                 }
 
-                using (var mem = new MemoryStream())
-                {
-                    await stream.CopyToAsync(mem);
-
-                    return mem.ToArray();
-                }
+                return await StreamUtils.StreamToBytesAsync(stream);
             }
         }
 
diff --git a/Ryujinx.Common/Utilities/StreamUtils.cs b/Ryujinx.Common/Utilities/StreamUtils.cs
index 9bd03ec907..da97188d8b 100644
--- a/Ryujinx.Common/Utilities/StreamUtils.cs
+++ b/Ryujinx.Common/Utilities/StreamUtils.cs
@@ -1,4 +1,8 @@
-using System.IO;
+using Microsoft.IO;
+using Ryujinx.Common.Memory;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace Ryujinx.Common.Utilities
 {
@@ -6,12 +10,22 @@ namespace Ryujinx.Common.Utilities
     {
         public static byte[] StreamToBytes(Stream input)
         {
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             {
                 input.CopyTo(stream);
 
                 return stream.ToArray();
             }
         }
+
+        public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
+        {
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
+            {
+                await input.CopyToAsync(stream, cancellationToken);
+
+                return stream.ToArray();
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs
index 64ff360e52..194a0c35de 100644
--- a/Ryujinx.Cpu/MemoryHelper.cs
+++ b/Ryujinx.Cpu/MemoryHelper.cs
@@ -1,4 +1,6 @@
-using Ryujinx.Memory;
+using Microsoft.IO;
+using Ryujinx.Common.Memory;
+using Ryujinx.Memory;
 using System;
 using System.IO;
 using System.Runtime.CompilerServices;
@@ -40,7 +42,7 @@ namespace Ryujinx.Cpu
 
         public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
         {
-            using (MemoryStream ms = new MemoryStream())
+            using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
             {
                 for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
                 {
@@ -54,7 +56,7 @@ namespace Ryujinx.Cpu
                     ms.WriteByte(value);
                 }
 
-                return Encoding.ASCII.GetString(ms.ToArray());
+                return Encoding.ASCII.GetString(ms.GetReadOnlySequence());
             }
         }
     }
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
index 5b430e1af6..77e526672c 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Shader;
 using Ryujinx.Graphics.Shader.Translation;
@@ -11,16 +13,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
     {
         public static byte[] Pack(ShaderSource[] sources)
         {
-            using MemoryStream output = new MemoryStream();
-            using BinaryWriter writer = new BinaryWriter(output);
+            using MemoryStream output = MemoryStreamManager.Shared.GetStream();
 
-            writer.Write(sources.Length);
+            output.Write(sources.Length);
 
-            for (int i = 0; i < sources.Length; i++)
+            foreach (ShaderSource source in sources)
             {
-                writer.Write((int)sources[i].Stage);
-                writer.Write(sources[i].BinaryCode.Length);
-                writer.Write(sources[i].BinaryCode);
+                output.Write((int)source.Stage);
+                output.Write(source.BinaryCode.Length);
+                output.Write(source.BinaryCode);
             }
 
             return output.ToArray();
diff --git a/Ryujinx.HLE/FileSystem/ContentManager.cs b/Ryujinx.HLE/FileSystem/ContentManager.cs
index 4e39400815..9facdd0b77 100644
--- a/Ryujinx.HLE/FileSystem/ContentManager.cs
+++ b/Ryujinx.HLE/FileSystem/ContentManager.cs
@@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
 using LibHac.Tools.Ncm;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.Exceptions;
 using Ryujinx.HLE.HOS.Services.Ssl;
@@ -637,12 +638,12 @@ namespace Ryujinx.HLE.FileSystem
 
         private Stream GetZipStream(ZipArchiveEntry entry)
         {
-            MemoryStream dest = new MemoryStream();
+            MemoryStream dest = MemoryStreamManager.Shared.GetStream();
 
-            Stream src = entry.Open();
-
-            src.CopyTo(dest);
-            src.Dispose();
+            using (Stream src = entry.Open())
+            {
+                src.CopyTo(dest);
+            }
 
             return dest;
         }
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
index 2d5509ff8d..952afcd5f6 100644
--- a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.HOS.Services.Am.AppletAE;
 using System;
 using System.Collections.Generic;
@@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
 
         private byte[] BuildResponseOld(WebCommonReturnValue result)
         {
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter writer = new BinaryWriter(stream))
             {
                 writer.WriteStruct(result);
@@ -80,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
         }
         private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
         {
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter writer = new BinaryWriter(stream))
             {
                 writer.WriteStruct(new WebArgHeader
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
index 5cdfb31438..5d5a26c23b 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.HOS.Services.Am.AppletAE;
 using Ryujinx.HLE.HOS.Services.Hid;
 using Ryujinx.HLE.HOS.Services.Hid.Types;
@@ -123,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Applets
 
         private byte[] BuildResponse(ControllerSupportResultInfo result)
         {
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter writer = new BinaryWriter(stream))
             {
                 writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
@@ -134,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Applets
 
         private byte[] BuildResponse()
         {
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter writer = new BinaryWriter(stream))
             {
                 writer.Write((ulong)ResultCode.Success);
diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
index cec9f213ea..a8119a470b 100644
--- a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
@@ -1,4 +1,5 @@
-using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
 using Ryujinx.HLE.HOS.Services.Am.AppletAE;
 using System;
 using System.IO;
@@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Applets
         {
             UserProfile currentUser = _system.AccountManager.LastOpenedUser;
 
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter writer = new BinaryWriter(stream))
             {
                 writer.Write((ulong)PlayerSelectResult.Success);
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
index e6ed46138e..c7ef7e9cfa 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
@@ -1,3 +1,6 @@
+using Microsoft.IO;
+using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using System;
 using System.IO;
 
@@ -18,20 +21,27 @@ namespace Ryujinx.HLE.HOS.Ipc
 
             HasPId = (word & 1) != 0;
 
-            ToCopy = new int[(word >> 1) & 0xf];
-            ToMove = new int[(word >> 5) & 0xf];
-
             PId = HasPId ? reader.ReadUInt64() : 0;
 
-            for (int index = 0; index < ToCopy.Length; index++)
+            int toCopySize = (word >> 1) & 0xf;
+            int[] toCopy = toCopySize == 0 ? Array.Empty<int>() : new int[toCopySize];
+
+            for (int index = 0; index < toCopy.Length; index++)
             {
-                ToCopy[index] = reader.ReadInt32();
+                toCopy[index] = reader.ReadInt32();
             }
 
-            for (int index = 0; index < ToMove.Length; index++)
+            ToCopy = toCopy;
+
+            int toMoveSize = (word >> 5) & 0xf;
+            int[] toMove = toMoveSize == 0 ? Array.Empty<int>() : new int[toMoveSize];
+
+            for (int index = 0; index < toMove.Length; index++)
             {
-                ToMove[index] = reader.ReadInt32();
+                toMove[index] = reader.ReadInt32();
             }
+
+            ToMove = toMove;
         }
 
         public IpcHandleDesc(int[] copy, int[] move)
@@ -57,36 +67,27 @@ namespace Ryujinx.HLE.HOS.Ipc
             return new IpcHandleDesc(Array.Empty<int>(), handles);
         }
 
-        public byte[] GetBytes()
+        public RecyclableMemoryStream GetStream()
         {
-            using (MemoryStream ms = new MemoryStream())
+            RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
+
+            int word = HasPId ? 1 : 0;
+
+            word |= (ToCopy.Length & 0xf) << 1;
+            word |= (ToMove.Length & 0xf) << 5;
+
+            ms.Write(word);
+
+            if (HasPId)
             {
-                BinaryWriter writer = new BinaryWriter(ms);
-
-                int word = HasPId ? 1 : 0;
-
-                word |= (ToCopy.Length & 0xf) << 1;
-                word |= (ToMove.Length & 0xf) << 5;
-
-                writer.Write(word);
-
-                if (HasPId)
-                {
-                    writer.Write(PId);
-                }
-
-                foreach (int handle in ToCopy)
-                {
-                    writer.Write(handle);
-                }
-
-                foreach (int handle in ToMove)
-                {
-                    writer.Write(handle);
-                }
-
-                return ms.ToArray();
+                ms.Write(PId);
             }
+
+            ms.Write(ToCopy);
+            ms.Write(ToMove);
+
+            ms.Position = 0;
+            return ms;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
index 55044da404..4e8f2fbfd1 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
@@ -1,4 +1,8 @@
+using Microsoft.IO;
+using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
@@ -32,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Ipc
             ObjectIds = new List<int>();
         }
 
-        public IpcMessage(byte[] data, long cmdPtr) : this()
+        public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
         {
-            using (MemoryStream ms = new MemoryStream(data))
+            using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
             {
                 BinaryReader reader = new BinaryReader(ms);
 
@@ -114,124 +118,119 @@ namespace Ryujinx.HLE.HOS.Ipc
 
             for (int index = 0; index < recvListCount; index++)
             {
-                RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
+                RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
             }
         }
 
-        public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
+        public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
         {
-            using (MemoryStream ms = new MemoryStream())
+            RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
+
+            int word0;
+            int word1;
+
+            word0  = (int)Type;
+            word0 |= (PtrBuff.Count      & 0xf) << 16;
+            word0 |= (SendBuff.Count     & 0xf) << 20;
+            word0 |= (ReceiveBuff.Count  & 0xf) << 24;
+            word0 |= (ExchangeBuff.Count & 0xf) << 28;
+
+            using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
+
+            int dataLength = RawData?.Length ?? 0;
+
+            dataLength = (dataLength + 3) & ~3;
+
+            int rawLength = dataLength;
+
+            int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8);
+
+            // Apparently, padding after Raw Data is 16 bytes, however when there is
+            // padding before Raw Data too, we need to subtract the size of this padding.
+            // This is the weirdest padding I've seen so far...
+            int pad1 = 0x10 - pad0;
+
+            dataLength = (dataLength + pad0 + pad1) / 4;
+
+            word1 = (dataLength & 0x3ff) | (2 << 10);
+
+            if (HandleDesc != null)
             {
-                BinaryWriter writer = new BinaryWriter(ms);
-
-                int word0;
-                int word1;
-
-                word0  = (int)Type;
-                word0 |= (PtrBuff.Count      & 0xf) << 16;
-                word0 |= (SendBuff.Count     & 0xf) << 20;
-                word0 |= (ReceiveBuff.Count  & 0xf) << 24;
-                word0 |= (ExchangeBuff.Count & 0xf) << 28;
-
-                byte[] handleData = Array.Empty<byte>();
-
-                if (HandleDesc != null)
-                {
-                    handleData = HandleDesc.GetBytes();
-                }
-
-                int dataLength = RawData?.Length ?? 0;
-
-                dataLength = (dataLength + 3) & ~3;
-
-                int rawLength = dataLength;
-
-                int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
-
-                // Apparently, padding after Raw Data is 16 bytes, however when there is
-                // padding before Raw Data too, we need to subtract the size of this padding.
-                // This is the weirdest padding I've seen so far...
-                int pad1 = 0x10 - pad0;
-
-                dataLength = (dataLength + pad0 + pad1) / 4;
-
-                word1 = (dataLength & 0x3ff) | (2 << 10);
-
-                if (HandleDesc != null)
-                {
-                    word1 |= 1 << 31;
-                }
-
-                writer.Write(word0);
-                writer.Write(word1);
-                writer.Write(handleData);
-
-                for (int index = 0; index < PtrBuff.Count; index++)
-                {
-                    writer.Write(PtrBuff[index].GetWord0());
-                    writer.Write(PtrBuff[index].GetWord1());
-                }
-
-                ms.Seek(pad0, SeekOrigin.Current);
-
-                if (RawData != null)
-                {
-                    writer.Write(RawData);
-                    ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
-                }
-
-                writer.Write(new byte[pad1]);
-                writer.Write(recvListAddr);
-
-                return ms.ToArray();
+                word1 |= 1 << 31;
             }
+
+            ms.Write(word0);
+            ms.Write(word1);
+
+            if (handleDataStream != null)
+            {
+                ms.Write(handleDataStream);
+            }
+
+            foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff)
+            {
+                ms.Write(ptrBuffDesc.GetWord0());
+                ms.Write(ptrBuffDesc.GetWord1());
+            }
+
+            ms.WriteByte(0, pad0);
+
+            if (RawData != null)
+            {
+                ms.Write(RawData);
+                ms.WriteByte(0, rawLength - RawData.Length);
+            }
+
+            ms.WriteByte(0, pad1);
+
+            ms.Write(recvListAddr);
+
+            ms.Position = 0;
+
+            return ms;
         }
 
-        public byte[] GetBytesTipc()
+        public RecyclableMemoryStream GetStreamTipc()
         {
             Debug.Assert(PtrBuff.Count == 0);
 
-            using (MemoryStream ms = new MemoryStream())
+            RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
+
+            int word0;
+            int word1;
+
+            word0 = (int)Type;
+            word0 |= (SendBuff.Count & 0xf) << 20;
+            word0 |= (ReceiveBuff.Count & 0xf) << 24;
+            word0 |= (ExchangeBuff.Count & 0xf) << 28;
+
+            using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
+
+            int dataLength = RawData?.Length ?? 0;
+
+            dataLength = ((dataLength + 3) & ~3) / 4;
+
+            word1 = (dataLength & 0x3ff);
+
+            if (HandleDesc != null)
             {
-                BinaryWriter writer = new BinaryWriter(ms);
-
-                int word0;
-                int word1;
-
-                word0 = (int)Type;
-                word0 |= (SendBuff.Count & 0xf) << 20;
-                word0 |= (ReceiveBuff.Count & 0xf) << 24;
-                word0 |= (ExchangeBuff.Count & 0xf) << 28;
-
-                byte[] handleData = Array.Empty<byte>();
-
-                if (HandleDesc != null)
-                {
-                    handleData = HandleDesc.GetBytes();
-                }
-
-                int dataLength = RawData?.Length ?? 0;
-
-                dataLength = ((dataLength + 3) & ~3) / 4;
-
-                word1 = (dataLength & 0x3ff);
-
-                if (HandleDesc != null)
-                {
-                    word1 |= 1 << 31;
-                }
-
-                writer.Write(word0);
-                writer.Write(word1);
-                writer.Write(handleData);
-
-                if (RawData != null)
-                {
-                    writer.Write(RawData);
-                }
-
-                return ms.ToArray();
+                word1 |= 1 << 31;
             }
+
+            ms.Write(word0);
+            ms.Write(word1);
+
+            if (handleDataStream != null)
+            {
+                ms.Write(handleDataStream);
+            }
+
+            if (RawData != null)
+            {
+                ms.Write(RawData);
+            }
+
+            return ms;
         }
 
         private long GetPadSize16(long position)
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
index 10406ac7d8..bcc9d8f890 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
@@ -13,13 +13,11 @@ namespace Ryujinx.HLE.HOS.Ipc
             Size = size;
         }
 
-        public IpcRecvListBuffDesc(BinaryReader reader)
+        public IpcRecvListBuffDesc(ulong packedValue)
         {
-            ulong value = reader.ReadUInt64();
+            Position = packedValue & 0xffffffffffff;
 
-            Position = value & 0xffffffffffff;
-
-            Size = (ushort)(value >> 48);
+            Size = (ushort)(packedValue >> 48);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
index 030a314f7e..1af171b926 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 
             public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
             {
-                Object    = schedulerObj;
+                Object = schedulerObj;
                 TimePoint = timePoint;
             }
         }
@@ -27,6 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
         private bool _keepRunning;
         private long _enforceWakeupFromSpinWait;
 
+        private const long NanosecondsPerSecond = 1000000000L;
+        private const long NanosecondsPerMillisecond = 1000000L;
+
         public KTimeManager(KernelContext context)
         {
             _context = context;
@@ -55,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
             {
                 _waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
 
-                if (timeout < 1000000)
+                if (timeout < NanosecondsPerMillisecond)
                 {
                     Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
                 }
@@ -142,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
         private WaitingObject GetNextWaitingObject()
         {
             WaitingObject selected = null;
-            
+
             long lowestTimePoint = long.MaxValue;
 
             for (int index = _waitingObjects.Count - 1; index >= 0; index--)
@@ -161,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 
         public static long ConvertNanosecondsToMilliseconds(long time)
         {
-            time /= 1000000;
+            time /= NanosecondsPerMillisecond;
 
             if ((ulong)time > int.MaxValue)
             {
@@ -173,18 +176,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 
         public static long ConvertMillisecondsToNanoseconds(long time)
         {
-            return time * 1000000;
+            return time * NanosecondsPerMillisecond;
         }
 
         public static long ConvertNanosecondsToHostTicks(long ns)
         {
-            long nsDiv = ns / 1000000000;
-            long nsMod = ns % 1000000000;
-            long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000;
-            long tickMod = PerformanceCounter.TicksPerSecond % 1000000000;
+            long nsDiv = ns / NanosecondsPerSecond;
+            long nsMod = ns % NanosecondsPerSecond;
+            long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond;
+            long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond;
 
-            long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000;
-            return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
+            long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond;
+            return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
         }
 
         public static long ConvertGuestTicksToNanoseconds(long ticks)
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index eef78e186f..c6467208eb 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -553,7 +553,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
             KProcess currentProcess = KernelStatic.GetCurrentProcess();
 
-            KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
+            KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length];
 
             for (int index = 0; index < handles.Length; index++)
             {
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
index 2c9d757427..14fba70456 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
@@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 {
     class KPriorityQueue
     {
-        private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
-        private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
+        private readonly LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
+        private readonly LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
 
-        private long[] _scheduledPrioritiesPerCore;
-        private long[] _suggestedPrioritiesPerCore;
+        private readonly long[] _scheduledPrioritiesPerCore;
+        private readonly long[] _suggestedPrioritiesPerCore;
 
         public KPriorityQueue()
         {
@@ -32,43 +32,134 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             _suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount];
         }
 
-        public IEnumerable<KThread> SuggestedThreads(int core)
+        public readonly ref struct KThreadEnumerable
         {
-            return Iterate(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
-        }
+            readonly LinkedList<KThread>[][] _listPerPrioPerCore;
+            readonly long[] _prios;
+            readonly int _core;
 
-        public IEnumerable<KThread> ScheduledThreads(int core)
-        {
-            return Iterate(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
-        }
-
-        private IEnumerable<KThread> Iterate(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
-        {
-            long prioMask = prios[core];
-
-            int prio = BitOperations.TrailingZeroCount(prioMask);
-
-            prioMask &= ~(1L << prio);
-
-            while (prio < KScheduler.PrioritiesCount)
+            public KThreadEnumerable(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
             {
-                LinkedList<KThread> list = listPerPrioPerCore[prio][core];
+                _listPerPrioPerCore = listPerPrioPerCore;
+                _prios = prios;
+                _core = core;
+            }
 
-                LinkedListNode<KThread> node = list.First;
+            public Enumerator GetEnumerator()
+            {
+                return new Enumerator(_listPerPrioPerCore, _prios, _core);
+            }
 
-                while (node != null)
+            public ref struct Enumerator
+            {
+                private readonly LinkedList<KThread>[][] _listPerPrioPerCore;
+                private readonly int _core;
+                private long _prioMask;
+                private int _prio;
+                private LinkedList<KThread> _list;
+                private LinkedListNode<KThread> _node;
+
+                public Enumerator(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
                 {
-                    yield return node.Value;
-
-                    node = node.Next;
+                    _listPerPrioPerCore = listPerPrioPerCore;
+                    _core = core;
+                    _prioMask = prios[core];
+                    _prio = BitOperations.TrailingZeroCount(_prioMask);
+                    _prioMask &= ~(1L << _prio);
                 }
 
-                prio = BitOperations.TrailingZeroCount(prioMask);
+                public KThread Current => _node?.Value;
 
-                prioMask &= ~(1L << prio);
+                public bool MoveNext()
+                {
+                    _node = _node?.Next;
+
+                    if (_node == null)
+                    {
+                        if (!MoveNextListAndFirstNode())
+                        {
+                            return false;
+                        }
+                    }
+
+                    return _node != null;
+                }
+
+                private bool MoveNextListAndFirstNode()
+                {
+                    if (_prio < KScheduler.PrioritiesCount)
+                    {
+                        _list = _listPerPrioPerCore[_prio][_core];
+
+                        _node = _list.First;
+
+                        _prio = BitOperations.TrailingZeroCount(_prioMask);
+
+                        _prioMask &= ~(1L << _prio);
+
+                        return true;
+                    }
+                    else
+                    {
+                        _list = null;
+                        _node = null;
+                        return false;
+                    }
+                }
             }
         }
 
+        public KThreadEnumerable ScheduledThreads(int core)
+        {
+            return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
+        }
+
+        public KThreadEnumerable SuggestedThreads(int core)
+        {
+            return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
+        }
+
+        public KThread ScheduledThreadsFirstOrDefault(int core)
+        {
+            return ScheduledThreadsElementAtOrDefault(core, 0);
+        }
+
+        public KThread ScheduledThreadsElementAtOrDefault(int core, int index)
+        {
+            int currentIndex = 0;
+            foreach (var scheduledThread in ScheduledThreads(core))
+            {
+                if (currentIndex == index)
+                {
+                    return scheduledThread;
+                }
+                else
+                {
+                    currentIndex++;
+                }
+            }
+
+            return null;
+        }
+
+        public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority)
+        {
+            foreach (var scheduledThread in ScheduledThreads(core))
+            {
+                if (scheduledThread.DynamicPriority == dynamicPriority)
+                {
+                    return scheduledThread;
+                }
+            }
+
+            return null;
+        }
+
+        public bool HasScheduledThreads(int core)
+        {
+            return ScheduledThreadsFirstOrDefault(core) != null;
+        }
+
         public void TransferToCore(int prio, int dstCore, KThread thread)
         {
             int srcCore = thread.ActiveCore;
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
index 0c51b7b9a3..b9de7d9c74 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -1,8 +1,6 @@
 using Ryujinx.Common;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Numerics;
 using System.Threading;
 
@@ -17,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
         private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
 
+        private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount];
+
         private readonly KernelContext _context;
         private readonly int _coreId;
 
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
             for (int core = 0; core < CpuCoresCount; core++)
             {
-                KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
+                KThread thread = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
 
                 if (thread != null &&
                     thread.Owner != null &&
@@ -115,12 +115,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             {
                 // If the core is not idle (there's already a thread running on it),
                 // then we don't need to attempt load balancing.
-                if (context.PriorityQueue.ScheduledThreads(core).Any())
+                if (context.PriorityQueue.HasScheduledThreads(core))
                 {
                     continue;
                 }
 
-                int[] srcCoresHighestPrioThreads = new int[CpuCoresCount];
+                Array.Fill(_srcCoresHighestPrioThreads, 0);
 
                 int srcCoresHighestPrioThreadsCount = 0;
 
@@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                         break;
                     }
 
-                    srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
+                    _srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
                 }
 
                 // Not yet selected candidate found.
@@ -158,9 +158,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                 // (the first one that doesn't make the source core idle if moved).
                 for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
                 {
-                    int srcCore = srcCoresHighestPrioThreads[index];
+                    int srcCore = _srcCoresHighestPrioThreads[index];
 
-                    KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1);
+                    KThread src = context.PriorityQueue.ScheduledThreadsElementAtOrDefault(srcCore, 1);
 
                     if (src != null)
                     {
@@ -422,9 +422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
         private static void RotateScheduledQueue(KernelContext context, int core, int prio)
         {
-            IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core);
-
-            KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
+            KThread selectedThread = context.PriorityQueue.ScheduledThreadsWithDynamicPriorityFirstOrDefault(core, prio);
             KThread nextThread = null;
 
             // Yield priority queue.
@@ -433,14 +431,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                 nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
             }
 
-            IEnumerable<KThread> SuitableCandidates()
+            static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate< KThread> predicate)
             {
                 foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
                 {
                     int suggestedCore = suggested.ActiveCore;
                     if (suggestedCore >= 0)
                     {
-                        KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
+                        KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
 
                         if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
                         {
@@ -453,14 +451,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                         nextThread == null ||
                         nextThread.LastScheduledTime >= suggested.LastScheduledTime)
                     {
-                        yield return suggested;
+                        if (predicate(suggested))
+                        {
+                            return suggested;
+                        }
                     }
                 }
+
+                return null;
             }
 
             // Select candidate threads that could run on this core.
             // Only take into account threads that are not yet selected.
-            KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
+            KThread dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority == prio);
 
             if (dst != null)
             {
@@ -469,11 +472,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
             // If the priority of the currently selected thread is lower or same as the preemption priority,
             // then try to migrate a thread with lower priority.
-            KThread bestCandidate = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
+            KThread bestCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
 
             if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
             {
-                dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority);
+                dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority < bestCandidate.DynamicPriority);
 
                 if (dst != null)
                 {
@@ -534,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             // Move current thread to the end of the queue.
             KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
 
-            IEnumerable<KThread> SuitableCandidates()
+            static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread nextThread, int lessThanOrEqualPriority)
             {
                 foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
                 {
@@ -554,12 +557,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                     if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
                         suggested.DynamicPriority < nextThread.DynamicPriority)
                     {
-                        yield return suggested;
+                        if (suggested.DynamicPriority <= lessThanOrEqualPriority)
+                        {
+                            return suggested;
+                        }
                     }
                 }
+
+                return null;
             }
 
-            KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
+            KThread dst = FirstSuitableCandidateOrDefault(context, core, nextThread, prio);
 
             if (dst != null)
             {
@@ -596,7 +604,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
             context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
 
-            if (!context.PriorityQueue.ScheduledThreads(core).Any())
+            if (!context.PriorityQueue.HasScheduledThreads(core))
             {
                 KThread selectedThread = null;
 
@@ -609,7 +617,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                         continue;
                     }
 
-                    KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
+                    KThread firstCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
 
                     if (firstCandidate == suggested)
                     {
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
index 01b65f55ea..973d5f6a41 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
@@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             }
             else
             {
-                LinkedListNode<KThread>[] syncNodes = new LinkedListNode<KThread>[syncObjs.Length];
+                LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length];
 
                 for (int index = 0; index < syncObjs.Length; index++)
                 {
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
index 227cfdae12..49e342f271 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
@@ -1,4 +1,5 @@
-using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
 using System.IO;
 
 namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
@@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
         public static byte[] MakeLaunchParams(UserProfile userProfile)
         {
             // Size needs to be at least 0x88 bytes otherwise application errors.
-            using (MemoryStream ms = new MemoryStream())
+            using (MemoryStream ms = MemoryStreamManager.Shared.GetStream())
             {
                 BinaryWriter writer = new BinaryWriter(ms);
 
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
index 66a69a8bed..fef82cbc46 100644
--- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
@@ -5,6 +5,7 @@ using LibHac.FsSystem;
 using LibHac.Ncm;
 using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.Exceptions;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS.Kernel.Memory;
@@ -160,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
             static uint KXor(uint data) => data ^ FontKey;
 
             using (BinaryReader reader    = new BinaryReader(bfttfStream))
-            using (MemoryStream ttfStream = new MemoryStream())
+            using (MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter output    = new BinaryWriter(ttfStream))
             {
                 if (KXor(reader.ReadUInt32()) != BFTTFMagic)
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index d4382a6433..619f544835 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Ipc;
@@ -5,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.Horizon.Common;
 using System;
+using System.Buffers;
 using System.Buffers.Binary;
 using System.Collections.Generic;
 using System.IO;
@@ -37,14 +40,27 @@ namespace Ryujinx.HLE.HOS.Services
         private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
         private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
 
+        private readonly MemoryStream _requestDataStream;
+        private readonly BinaryReader _requestDataReader;
+
+        private readonly MemoryStream _responseDataStream;
+        private readonly BinaryWriter _responseDataWriter;
+
         public ManualResetEvent InitDone { get; }
         public string Name { get; }
         public Func<IpcService> SmObjectFactory { get; }
 
         public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
         {
-            InitDone = new ManualResetEvent(false);
             _context = context;
+
+            _requestDataStream = MemoryStreamManager.Shared.GetStream();
+            _requestDataReader = new BinaryReader(_requestDataStream);
+
+            _responseDataStream = MemoryStreamManager.Shared.GetStream();
+            _responseDataWriter = new BinaryWriter(_responseDataStream);
+
+            InitDone = new ManualResetEvent(false);
             Name = name;
             SmObjectFactory = smObjectFactory;
 
@@ -110,15 +126,15 @@ namespace Ryujinx.HLE.HOS.Services
 
             while (true)
             {
-                int[] portHandles = _portHandles.ToArray();
-                int[] sessionHandles = _sessionHandles.ToArray();
-                int[] handles = new int[portHandles.Length + sessionHandles.Length];
+                int handleCount = _portHandles.Count + _sessionHandles.Count;
 
-                portHandles.CopyTo(handles, 0);
-                sessionHandles.CopyTo(handles, portHandles.Length);
+                int[] handles = ArrayPool<int>.Shared.Rent(handleCount);
+                
+                _portHandles.CopyTo(handles, 0);
+                _sessionHandles.CopyTo(handles, _portHandles.Count);
 
                 // We still need a timeout here to allow the service to pick up and listen new sessions...
-                var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L);
+                var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L);
 
                 thread.HandlePostSyscall();
 
@@ -129,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services
 
                 replyTargetHandle = 0;
 
-                if (rc == Result.Success && signaledIndex >= portHandles.Length)
+                if (rc == Result.Success && signaledIndex >= _portHandles.Count)
                 {
                     // We got a IPC request, process it, pass to the appropriate service if needed.
                     int signaledHandle = handles[signaledIndex];
@@ -156,6 +172,8 @@ namespace Ryujinx.HLE.HOS.Services
                     _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
                     _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
                 }
+
+                ArrayPool<int>.Shared.Return(handles);
             }
 
             Dispose();
@@ -166,13 +184,9 @@ namespace Ryujinx.HLE.HOS.Services
             KProcess process = KernelStatic.GetCurrentProcess();
             KThread thread = KernelStatic.GetCurrentThread();
             ulong messagePtr = thread.TlsAddress;
-            ulong messageSize = 0x100;
 
-            byte[] reqData = new byte[messageSize];
+            IpcMessage request = ReadRequest(process, messagePtr);
 
-            process.CpuMemory.Read(messagePtr, reqData);
-
-            IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
             IpcMessage response = new IpcMessage();
 
             ulong tempAddr = recvListAddr;
@@ -202,158 +216,157 @@ namespace Ryujinx.HLE.HOS.Services
             bool shouldReply = true;
             bool isTipcCommunication = false;
 
-            using (MemoryStream raw = new MemoryStream(request.RawData))
+            _requestDataStream.SetLength(0);
+            _requestDataStream.Write(request.RawData);
+            _requestDataStream.Position = 0;
+
+            if (request.Type == IpcMessageType.HipcRequest ||
+                request.Type == IpcMessageType.HipcRequestWithContext)
             {
-                BinaryReader reqReader = new BinaryReader(raw);
+                response.Type = IpcMessageType.HipcResponse;
 
-                if (request.Type == IpcMessageType.HipcRequest ||
-                    request.Type == IpcMessageType.HipcRequestWithContext)
-                {
-                    response.Type = IpcMessageType.HipcResponse;
+                _responseDataStream.SetLength(0);
 
-                    using (MemoryStream resMs = new MemoryStream())
-                    {
-                        BinaryWriter resWriter = new BinaryWriter(resMs);
+                ServiceCtx context = new ServiceCtx(
+                    _context.Device,
+                    process,
+                    process.CpuMemory,
+                    thread,
+                    request,
+                    response,
+                    _requestDataReader,
+                    _responseDataWriter);
 
-                        ServiceCtx context = new ServiceCtx(
-                            _context.Device,
-                            process,
-                            process.CpuMemory,
-                            thread,
-                            request,
-                            response,
-                            reqReader,
-                            resWriter);
+                _sessions[serverSessionHandle].CallHipcMethod(context);
 
-                        _sessions[serverSessionHandle].CallHipcMethod(context);
-
-                        response.RawData = resMs.ToArray();
-                    }
-                }
-                else if (request.Type == IpcMessageType.HipcControl ||
-                         request.Type == IpcMessageType.HipcControlWithContext)
-                {
-                    uint magic = (uint)reqReader.ReadUInt64();
-                    uint cmdId = (uint)reqReader.ReadUInt64();
-
-                    switch (cmdId)
-                    {
-                        case 0:
-                            request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
-                            break;
-
-                        case 3:
-                            request = FillResponse(response, 0, PointerBufferSize);
-                            break;
-
-                        // TODO: Whats the difference between IpcDuplicateSession/Ex?
-                        case 2:
-                        case 4:
-                            int unknown = reqReader.ReadInt32();
-
-                            _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
-
-                            AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
-
-                            response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
-
-                            request = FillResponse(response, 0);
-
-                            break;
-
-                        default: throw new NotImplementedException(cmdId.ToString());
-                    }
-                }
-                else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
-                {
-                    _context.Syscall.CloseHandle(serverSessionHandle);
-                    _sessionHandles.Remove(serverSessionHandle);
-                    IpcService service = _sessions[serverSessionHandle];
-                    if (service is IDisposable disposableObj)
-                    {
-                        disposableObj.Dispose();
-                    }
-                    _sessions.Remove(serverSessionHandle);
-                    shouldReply = false;
-                }
-                // If the type is past 0xF, we are using TIPC
-                else if (request.Type > IpcMessageType.TipcCloseSession)
-                {
-                    isTipcCommunication = true;
-
-                    // Response type is always the same as request on TIPC.
-                    response.Type = request.Type;
-
-                    using (MemoryStream resMs = new MemoryStream())
-                    {
-                        BinaryWriter resWriter = new BinaryWriter(resMs);
-
-                        ServiceCtx context = new ServiceCtx(
-                            _context.Device,
-                            process,
-                            process.CpuMemory,
-                            thread,
-                            request,
-                            response,
-                            reqReader,
-                            resWriter);
-
-                        _sessions[serverSessionHandle].CallTipcMethod(context);
-
-                        response.RawData = resMs.ToArray();
-                    }
-
-                    process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
-                }
-                else
-                {
-                    throw new NotImplementedException(request.Type.ToString());
-                }
-
-                if (!isTipcCommunication)
-                {
-                    process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
-                }
-
-                return shouldReply;
+                response.RawData = _responseDataStream.ToArray();
             }
+            else if (request.Type == IpcMessageType.HipcControl ||
+                        request.Type == IpcMessageType.HipcControlWithContext)
+            {
+                uint magic = (uint)_requestDataReader.ReadUInt64();
+                uint cmdId = (uint)_requestDataReader.ReadUInt64();
+
+                switch (cmdId)
+                {
+                    case 0:
+                        FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
+                        break;
+
+                    case 3:
+                        FillHipcResponse(response, 0, PointerBufferSize);
+                        break;
+
+                    // TODO: Whats the difference between IpcDuplicateSession/Ex?
+                    case 2:
+                    case 4:
+                        int unknown = _requestDataReader.ReadInt32();
+
+                        _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
+
+                        AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
+
+                        response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
+
+                        FillHipcResponse(response, 0);
+
+                        break;
+
+                    default: throw new NotImplementedException(cmdId.ToString());
+                }
+            }
+            else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
+            {
+                _context.Syscall.CloseHandle(serverSessionHandle);
+                _sessionHandles.Remove(serverSessionHandle);
+                IpcService service = _sessions[serverSessionHandle];
+                (service as IDisposable)?.Dispose();
+                _sessions.Remove(serverSessionHandle);
+                shouldReply = false;
+            }
+            // If the type is past 0xF, we are using TIPC
+            else if (request.Type > IpcMessageType.TipcCloseSession)
+            {
+                isTipcCommunication = true;
+
+                // Response type is always the same as request on TIPC.
+                response.Type = request.Type;
+
+                _responseDataStream.SetLength(0);
+
+                ServiceCtx context = new ServiceCtx(
+                    _context.Device,
+                    process,
+                    process.CpuMemory,
+                    thread,
+                    request,
+                    response,
+                    _requestDataReader,
+                    _responseDataWriter);
+
+                _sessions[serverSessionHandle].CallTipcMethod(context);
+
+                response.RawData = _responseDataStream.ToArray();
+
+                using var responseStream = response.GetStreamTipc();
+                process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
+            }
+            else
+            {
+                throw new NotImplementedException(request.Type.ToString());
+            }
+
+            if (!isTipcCommunication)
+            {
+                using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48));
+                process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
+            }
+
+            return shouldReply;
         }
 
-        private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
+        private static IpcMessage ReadRequest(KProcess process, ulong messagePtr)
         {
-            using (MemoryStream ms = new MemoryStream())
-            {
-                BinaryWriter writer = new BinaryWriter(ms);
+            const int messageSize = 0x100;
 
-                foreach (int value in values)
-                {
-                    writer.Write(value);
-                }
+            byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize);
 
-                return FillResponse(response, result, ms.ToArray());
-            }
+            Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize);
+            reqDataSpan.Clear();
+
+            process.CpuMemory.Read(messagePtr, reqDataSpan);
+
+            IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr);
+
+            ArrayPool<byte>.Shared.Return(reqData);
+
+            return request;
         }
 
-        private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
+        private void FillHipcResponse(IpcMessage response, long result)
+        {
+            FillHipcResponse(response, result, ReadOnlySpan<byte>.Empty);
+        }
+
+        private void FillHipcResponse(IpcMessage response, long result, int value)
+        {
+            Span<byte> span = stackalloc byte[sizeof(int)];
+            BinaryPrimitives.WriteInt32LittleEndian(span, value);
+            FillHipcResponse(response, result, span);
+        }
+
+        private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data)
         {
             response.Type = IpcMessageType.HipcResponse;
 
-            using (MemoryStream ms = new MemoryStream())
-            {
-                BinaryWriter writer = new BinaryWriter(ms);
+            _responseDataStream.SetLength(0);
 
-                writer.Write(IpcMagic.Sfco);
-                writer.Write(result);
+            _responseDataStream.Write(IpcMagic.Sfco);
+            _responseDataStream.Write(result);
 
-                if (data != null)
-                {
-                    writer.Write(data);
-                }
+            _responseDataStream.Write(data);
 
-                response.RawData = ms.ToArray();
-            }
-
-            return response;
+            response.RawData = _responseDataStream.ToArray();
         }
 
         protected virtual void Dispose(bool disposing)
@@ -372,6 +385,11 @@ namespace Ryujinx.HLE.HOS.Services
 
                 _sessions.Clear();
 
+                _requestDataReader.Dispose();
+                _requestDataStream.Dispose();
+                _responseDataWriter.Dispose();
+                _responseDataStream.Dispose();
+
                 InitDone.Dispose();
             }
         }
diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs
index a64d451c1e..1810b1ad76 100644
--- a/Ryujinx.HLE/Utilities/StringUtils.cs
+++ b/Ryujinx.HLE/Utilities/StringUtils.cs
@@ -1,4 +1,6 @@
 using LibHac.Common;
+using Microsoft.IO;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.HOS;
 using System;
 using System.Globalization;
@@ -77,7 +79,7 @@ namespace Ryujinx.HLE.Utilities
             ulong position = context.Request.PtrBuff[index].Position;
             ulong size     = context.Request.PtrBuff[index].Size;
 
-            using (MemoryStream ms = new MemoryStream())
+            using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
             {
                 while (size-- > 0)
                 {
@@ -91,7 +93,7 @@ namespace Ryujinx.HLE.Utilities
                     ms.WriteByte(value);
                 }
 
-                return Encoding.UTF8.GetString(ms.ToArray());
+                return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
             }
         }
 
@@ -110,7 +112,7 @@ namespace Ryujinx.HLE.Utilities
             ulong position = context.Request.SendBuff[index].Position;
             ulong size     = context.Request.SendBuff[index].Size;
 
-            using (MemoryStream ms = new MemoryStream())
+            using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
             {
                 while (size-- > 0)
                 {
@@ -124,7 +126,7 @@ namespace Ryujinx.HLE.Utilities
                     ms.WriteByte(value);
                 }
 
-                return Encoding.UTF8.GetString(ms.ToArray());
+                return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
             }
         }
 
diff --git a/Ryujinx.Input/Motion/CemuHook/Client.cs b/Ryujinx.Input/Motion/CemuHook/Client.cs
index f5f7b86431..4498b8ca67 100644
--- a/Ryujinx.Input/Motion/CemuHook/Client.cs
+++ b/Ryujinx.Input/Motion/CemuHook/Client.cs
@@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Common.Configuration.Hid.Controller;
 using Ryujinx.Common.Configuration.Hid.Controller.Motion;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using Ryujinx.Input.HLE;
 using Ryujinx.Input.Motion.CemuHook.Protocol;
 using System;
@@ -381,7 +382,7 @@ namespace Ryujinx.Input.Motion.CemuHook
 
             Header header = GenerateHeader(clientId);
 
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter writer = new BinaryWriter(stream))
             {
                 writer.WriteStruct(header);
@@ -421,7 +422,7 @@ namespace Ryujinx.Input.Motion.CemuHook
 
             Header header = GenerateHeader(clientId);
 
-            using (MemoryStream stream = new MemoryStream())
+            using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
             using (BinaryWriter writer = new BinaryWriter(stream))
             {
                 writer.WriteStruct(header);
diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs
index e1851d48b8..edbfc88557 100644
--- a/Ryujinx.Memory/IVirtualMemoryManager.cs
+++ b/Ryujinx.Memory/IVirtualMemoryManager.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Memory.Range;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 
 namespace Ryujinx.Memory
@@ -77,6 +78,21 @@ namespace Ryujinx.Memory
         /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
         void Write(ulong va, ReadOnlySpan<byte> data);
 
+        /// <summary>
+        /// Writes data to CPU mapped memory, with write tracking.
+        /// </summary>
+        /// <param name="va">Virtual address to write the data into</param>
+        /// <param name="data">Data to be written</param>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        public void Write(ulong va, ReadOnlySequence<byte> data)
+        {
+            foreach (ReadOnlyMemory<byte> segment in data)
+            {
+                Write(va, segment.Span);
+                va += (ulong)segment.Length;
+            }
+        }
+
         /// <summary>
         /// Writes data to the application process, returning false if the data was not changed.
         /// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
diff --git a/Ryujinx/Ui/Windows/AvatarWindow.cs b/Ryujinx/Ui/Windows/AvatarWindow.cs
index fc928bde29..0cda890f51 100644
--- a/Ryujinx/Ui/Windows/AvatarWindow.cs
+++ b/Ryujinx/Ui/Windows/AvatarWindow.cs
@@ -6,6 +6,7 @@ using LibHac.FsSystem;
 using LibHac.Ncm;
 using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.Ui.Common.Configuration;
 using SixLabors.ImageSharp;
@@ -136,8 +137,8 @@ namespace Ryujinx.Ui.Windows
 
                             romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
-                            using (MemoryStream stream    = new MemoryStream())
-                            using (MemoryStream streamPng = new MemoryStream())
+                            using (MemoryStream stream    = MemoryStreamManager.Shared.GetStream())
+                            using (MemoryStream streamPng = MemoryStreamManager.Shared.GetStream())
                             {
                                 file.Get.AsStream().CopyTo(stream);
 
@@ -169,7 +170,7 @@ namespace Ryujinx.Ui.Windows
 
         private byte[] ProcessImage(byte[] data)
         {
-            using (MemoryStream streamJpg = new MemoryStream())
+            using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
             {
                 Image avatarImage = Image.Load(data, new PngDecoder());
 
diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
index 495c1d1fa2..a08b5dd17b 100644
--- a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
+++ b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
@@ -1,4 +1,5 @@
 using Gtk;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS.Services.Account.Acc;
 using Ryujinx.Ui.Common.Configuration;
@@ -181,7 +182,7 @@ namespace Ryujinx.Ui.Windows
             {
                 image.Mutate(x => x.Resize(256, 256));
 
-                using (MemoryStream streamJpg = new MemoryStream())
+                using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
                 {
                     image.SaveAsJpeg(streamJpg);