C#使用stackalloc分配堆棧內存和非托管類型詳解

stackalloc 表達式

stackalloc表達式在棧(stack)上分配內存塊。

在方法執行期間創建的棧中分配的內存塊會在方法返回時自動丟棄。不能顯式釋放使用 stackalloc 分配的內存。stackalloc分配的內存塊不受垃圾回收的影響,也不必通過 fixed 語句固定。

棧內存,棧內存開辟快速高效但資源有限,通常限制1M。

可以將 stackalloc 表達式的結果分配給以下任一類型的變量:

stackalloc 分配 System.Span<T> 或 System.ReadOnlySpan<T> 類型

int length = 3;
Span<int> numbers = stackalloc int[length];
for (var i = 0; i < length; i++)
{
    numbers[i] = i;
}

stack分配內存塊賦值給 System.Span<T>System.ReadOnlySpan<T> 類型的變量不必使用不安全上下文(unsafe context)。

可以在表達式允許的任何地方使用stackalloc,並且在需要分配內存時,推薦盡可能的使用 Span<T>ReadOnlySpan<T> 類型。

int length = 1000;
Span<byte> buffer = length <= 1024 ? stackalloc byte[length] : new byte[length];
Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6, 8 });
Console.WriteLine(ind);  // output: 1

stackalloc 分配 指針類型

如下示例,對於指針類型,stackalloc表達式隻能用於本地變量聲明的初始化中。

unsafe
{
    int length = 3;
    int* numbers = stackalloc int[length];
    for (var i = 0; i < length; i++)
    {
        numbers[i] = i;
    }
}

使用指針類型,必須使用不安全上下文(unsafe context)。

stackalloc分配內存的註意點

堆棧可用的內存數量是有限的,如果分配太多內存,則可能發生StackOverflowException異常。因此需要註意以下幾點:

  • 限制使用stackalloc分配的內存數量。

例如,如果預期的緩沖區大小低於某個限制,則可以在堆棧上分配內存;否則,請使用所需長度的數組。如下代碼所示:

const int MaxStackLimit = 1024;
Span<byte> buffer = inputLength <= MaxStackLimit ? stackalloc byte[MaxStackLimit] : new byte[inputLength];

stack 上可用內存數量取決於代碼的執行環境。

  • 避免在循環內部使用stackalloc。在循環外部allocate分配內存塊,並在循環內部重用。

新分配內存的內容是未定義的。必須在使用之前初始化。 比如,可以使用 Span<T>.Clear 方法設置所有的元素項為類型T的默認值。

也可以使用數組初始化器定義新分配內存的內容。

Span<int> first = stackalloc int[3] { 1, 2, 3 };
Span<int> second = stackalloc int[] { 1, 2, 3 };
ReadOnlySpan<int> third = stackalloc[] { 1, 2, 3 };

非托管類型 Unmanaged type

在定義指針、stackalloc T[n]時,其類型隻能是非托管類型。(雖然在使用和形式上,非托管類型與C#的原始類型幾乎沒有區別,但,還是可以瞭解下)。

以下類型的屬於或也屬於非托管類型:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool
  • 任何 enum 類型
  • 任何 pointer 類型
  • 任何 隻包含非托管類型字段的用戶定義(user-defined)的 struct 類型

使用非托管泛型約束unmanaged,指定類型參數為非指針、不可為空的非托管類型。

僅包含非托管類型字段的構造結構類型(constructed struct type)也是非托管的。如下示例所示,DisplaySize<T>()方法的泛型約束為unmanaged,在調用時Coords<int>Coords<double>作為非托管類型使用:

using System;
public struct Coords<T>
{
    public T X;
    public T Y;
}
public class UnmanagedTypes
{
    public static void Main()
    {
        DisplaySize<Coords<int>>();
        DisplaySize<Coords<double>>();
    }
    private unsafe static void DisplaySize<T>() where T : unmanaged
    {
        Console.WriteLine($"{typeof(T)} is unmanaged and its size is {sizeof(T)} bytes");
    }
}
// Output:
// Coords`1[System.Int32] is unmanaged and its size is 8 bytes
// Coords`1[System.Double] is unmanaged and its size is 16 bytes

泛型結構Coords<T>可以是非托管和托管構造類型。當然也可以限制為非托管類型,如下:

public struct Coords<T> where T : unmanaged
{
    public T X;
    public T Y;
}

參考

stackalloc expression

Unmanaged types

以上就是C#使用stackalloc分配堆棧內存和非托管類型詳解的詳細內容,更多關於C# stackalloc分配堆棧內存的資料請關註WalkonNet其它相關文章!

推薦閱讀: