C#使用stackalloc分配堆栈内存和非托管类型详解

目录
  • stackalloc 表达式
    • stackalloc 分配 System.Span<T> 或 System.ReadOnlySpan<T> 类型
    • stackalloc 分配 指针类型
    • stackalloc分配内存的注意点
  • 非托管类型 Unmanaged type

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分配堆栈内存的资料请关注我们其它相关文章!

(0)

相关推荐

  • C#托管内存与非托管内存之间的转换的实例讲解

    c#有自己的内存回收机制,所以在c#中我们可以只new,不用关心怎样delete,c#使用gc来清理内存,这部分内存就是managed memory,大部分时候我们工作于c#环境中,都是在使用托管内存,然而c#毕竟运行在c++之上,有的时候,(比如可能我们需要引入一些第三方的c++或native代码的库,在Unity3d开发中很常见)我们需要直接在c#中操纵非托管的代码,这些non-managed memory我们就需要自己去处理他们的申请和释放了, c# 中提供了一些接口,完成托管和非托管之间

  • C#实现IDisposable接口释放非托管资源

    目录 完整示例 为什么要实现Foo析构函数 Dispose方法中为什么要调用GC.SuppressFinalize Reference Why using finalizers is a bad idea 当在一个类中使用了另外一个实现了IDisposable的类作为一个成员属性时, 此时这个类就有必要也去实现IDisposable接口, 以确保在合适的实际释放非托管资源, 到底该如何正确的实现这个接口呢? 当然这只是需要实现IDisposable接口其中一种情况 完整示例 示例的Foo类中包含

  • c#关于非托管内存的释放问题及解读

    目录 关于非托管内存的释放问题 托管内存与非托管内存之间的转换 1.managed memory-> unmanaged memory 2.un-managed memory->managed memory 3.在c#直接申请一个un-managed mem传给c++ 总结 关于非托管内存的释放问题 硬件:大华sdk 软件平台:win10+vs2015 背景:近期在做大华工业相机SDK的采集的时候,用到Marshal.copy,将托管的代码转换成非托管的指针内存,由于没有及时释放内存指针,导致

  • C#使用stackalloc分配堆栈内存和非托管类型详解

    目录 stackalloc 表达式 stackalloc 分配 System.Span<T> 或 System.ReadOnlySpan<T> 类型 stackalloc 分配 指针类型 stackalloc分配内存的注意点 非托管类型 Unmanaged type stackalloc 表达式 stackalloc表达式在栈(stack)上分配内存块. 在方法执行期间创建的栈中分配的内存块会在方法返回时自动丢弃.不能显式释放使用 stackalloc 分配的内存.stackall

  • PHP内存溢出的解决方法详解

    目录 1.处理数组时出现内存溢出 2.使用sql查询数据,查出来很多,导致内存溢出 3.假定日志中存放的记录数为500000条,那么解决方案如下 4.上传excel文件时,出现内存溢出的情况 什么是内存溢出 内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存. 引起内存溢出的原因有很多种,常见的有以下几种: 1 内存中加载的数据量过于庞大,如一次从数据库取出过多数据: 2 集合类中有对对象的引用,使用完后未清空: 3 代码中存在死循环

  • Linux内存描述符mm_struct实例详解

    Linux对于内存的管理涉及到非常多的方面,这篇文章首先从对进程虚拟地址空间的管理说起.(所依据的代码是2.6.32.60) 无论是内核线程还是用户进程,对于内核来说,无非都是task_struct这个数据结构的一个实例而已,task_struct被称为进程描述符(process descriptor),因为它记录了这个进程所有的context.其中有一个被称为'内存描述符'(memory descriptor)的数据结构mm_struct,抽象并描述了Linux视角下管理进程地址空间的所有信息

  • C#通过不安全代码看内存加载的示例详解

    目录 项目文件 值类型 自定义结构体 引用类型string 自定class类型 C#中类型分为值类型和引用类型,值类型存储在堆栈中,是栈结构,先进后出,引用类型存储在托管堆中.接下来用不安全代码的地址,来看一下值类型和引用类型的存储. 项目文件 C#中使用不安全代码需要在项目文件中添加AllowUnsafeBlocks配置. <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>E

  • 使用Jedis面临的非线程安全问题详解

    目录 1. jedis类图 2. 为什么jedis不是线程安全的 2.1 共享socket引起的异常 2.2 共享数据流引起的异常 3.jedis多线程操作 网上都说jedis实例是非线程安全的,常常通过JedisPool连接池去管理实例,在多线程情况下让每个线程有自己独立的jedis实例,但都没有具体说明为啥jedis实例时非线程安全的,下面详细看一下非线程安全主要从哪个角度来看. 1. jedis类图 2. 为什么jedis不是线程安全的 由上述类图可知,Jedis类中有RedisInput

  • java 中同步、异步、阻塞和非阻塞区别详解

    java 中同步.异步.阻塞和非阻塞区别详解 简单点说: 阻塞就是干不完不准回来,一直处于等待中,直到事情处理完成才返回: 非阻塞就是你先干,我先看看有其他事没有,一发现事情被卡住,马上报告领导. 我们拿最常用的send和recv两个函数来说吧... 比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话

  • Java中内存异常StackOverflowError与OutOfMemoryError详解

     Java中内存异常StackOverflowError与OutOfMemoryError详解 使用Java开发,经常回遇到内存异常的情况,而StackOverflowError和OutOfMemoryError便是最常遇见的错误. 首先,看看这两种错误的解释: 如果当前线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常. 这里把异常分为两种情况,但是存在一些相互重

  • 逻辑表达式中与或非的用法详解

    先说逻辑与(&&),它可以从三个层次进行理解 第一个层次最简单,就是简单的布尔值之间的逻辑与,就是左值和右值都是true时,返回true,两边都是false或者两边的值其中一边是fasle,就返回false:(AND操作): 第二个层次,(false,null,indefined,0,-0,NaN和""这些都是假值,其他所有的值包括对象都是真值),对这些"真值"和"假值"进行AND操作,返回一个"真值"或者&q

  • java 线程公平锁与非公平锁详解及实例代码

    java 线程公平锁与非公平锁详解 在ReentrantLock中很明显可以看到其中同步包括两种,分别是公平的FairSync和非公平的NonfairSync.公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的:而非公平锁是允许插队的. 默认情况下ReentrantLock是通过非公平锁来进行同步的,包括synchronized关键字都是如此,因为这样性能会更好.因为从线程进入了RUNNABLE状态,可以执行开始,到实际线程执行是要比较久的时间的.而且,在一个锁释放之后,其

随机推荐