There are two visual differences between Memory<T> and Span<T>. The first one is that Memory<T> type doesn’t contain ref modifier in the header of the type. In other words, the Memory<T> type can be allocated both on the stack while being either a local variable, or a method parameter, or its returned value and on the heap, referencing some data in memory from there. However, this small difference creates a huge distinction in the behavior and capabilities of Memory<T> compared to Span<T>. Unlike Span<T> that is an instrument for some methods to use some data buffer, the Memory<T> type is designed to store information about the buffer, but not to handle it. Thus, there is the difference in API. However, I suggest we get familiar with the whole set of classes. First, let’s look at the Memory<T> structure itself (here I show only those type members that I found most important): As we see the structure contains the constructor based on arrays, but stores data in the object. This is to additionally reference strings that don’t have a constructor designed for them, but can be used with the AsMemory() string method, it returns ReadOnlyMemory. However, as both types should be binary similar, Object is the type of the _object field. Next, we see two constructors based on MemoryManager. We will talk about them later. The properties of obtaining Length (size) and IsEmpty check for an empty set. Also, there is the Slice method for getting a subset as well as CopyTo and TryCopyTo methods of copying. Talking about Memory I want to describe two methods of this type in detail: the Span property and the Pin method. Namely, the lines that handle strings management. They say that if we convert ReadOnlyMemory<T> to Memory<T> (these things are the same in binary representation and there is even a comment these types must coincide in a binary way as one is produced from another by calling Unsafe.As) we will get an ~access to a secret chamber~ with an opportunity to change strings. This is an extremely dangerous mechanism: This mechanism combined with string interning can produce dire consequences. The second method that draws strong attention is Pin: It is also an important instrument for unification because if we want to pass a buffer to unmanaged code, we just need to call the Pin() method and pass a pointer to this code no matter what type of data Memory<T> refers to. This pointer will be stored in the property of a resulting structure. It doesn’t matter what Pin() was called for in this code: it can be Memory that represents either T[], or a string or a buffer of unmanaged memory. Merely arrays and string will get a real GCHandle.Alloc(array, GCHandleType.Pinned) and in case of unmanaged memory nothing will happen. Besides indicating structure fields, I want to note that there are two other internal type constructors based on an other entity – MemoryManager. This is not a classic memory manager that you might have thought of and we are going to talk about it later. classic memory manager that you might have thought of and we are going to talk about it later. Like Span, Memory has a reference to a navigated object, an offset, and a size of an internal buffer. Note that you can use the new operator to create Memory from an array only. Or, you can use extension methods to create Memory from a string, an array or ArraySegment. I mean it is not designed to be created from unmanaged memory manually. However, we can see that there is an internal method to create this structure using MemoryManager. File MemoryManager.cs This structure indicates the owner of a memory range. In other words, Span is an instrument to work with memory, Memory is a tool to store the information about a particular memory range and MemoryManager is a tool to control the lifetime of this range, i.e. its owner. For example, we can look at NativeMemoryManager<T> type. Although it is used for tests, this type clearly represents the concept of “ownership”. File NativeMemoryManager.cs That means the class allows for nested calls of the Pin() method, thus counting generated references from the unsafe world. Another entity closely tied with Memory is MemoryPool that pools MemoryManager instances (IMemoryOwner in fact): File MemoryPool.cs It is used to lease buffers of a necessary size for temporary use. The leased instances with implemented IMemoryOwner<T> interface have the Dispose() method to return the leased array back to the pool of arrays. By default, you can use the shareable pool of buffers built on ArrayMemoryPool: File ArrayMemoryPool.cs Based on this architecture, we have the following picture: To measure the performance of new data types I decided to use a library that has already become standard BenchmarkDotNet: Now, let’s see the results.  Looking at them we can get the following information: Thus, we can draw simple conclusions regarding the use of these data types:  Семинары по платформе .NET CLRium