如果stackalloc与引用类型一起使用,如下所示
var arr = stackalloc string[100];有一个错误
无法获取托管类型的地址、大小或声明指向托管类型(“字符串”)的指针
为甚麽这样呢?为什么CLR不能声明指向托管类型的指针?
发布于 2016-02-24 09:29:47
发布于 2016-02-24 10:37:46
.NET中的实时编译器在将C#编译器生成的C#转换为可执行机器代码时执行两项重要任务。显而易见的,可见的是生成机器代码。不明显和完全不可见的任务是生成一个表,该表告诉垃圾收集器在执行方法时查找对象引用的位置。
这是必要的,因为对象根不能仅作为类的字段存储在GC堆中,还可以存储在局部变量或CPU寄存器中。为了正确地完成这项工作,抖动需要知道堆栈帧的确切结构以及存储在其中的变量的类型,这样它才能正确地创建该表。这样,垃圾收集器就可以知道如何读取适当的堆栈帧偏移量或CPU寄存器来获取对象根值。指向GC堆的指针。
当您使用stackalloc时,这是一个问题。该语法利用了允许程序声明自定义值类型的CLR功能。普通托管类型声明的后门,但限制此值类型不能包含任何字段。仅仅是一小块内存,这取决于程序生成适当的偏移到该blob。C#编译器帮助您根据类型声明和索引表达式生成这些偏移量。
在C++/CLI程序中也很常见,相同的自定义值类型特性可以为本机C++对象提供存储。只需要存储该对象的空间,如何正确初始化该对象并访问该C++对象的成员是C++编译器确定的任务。没什么GC需要知道的。
因此,核心限制是无法为这个内存块提供类型信息。就CLR而言,这些只是没有结构的普通字节,GC使用的表没有描述其内部结构的选项。
不可避免的是,您可以使用的唯一类型是不需要GC需要知道的对象引用的类型。闪烁值类型或指针。所以System.String是不允许的,它是一个引用类型.你能得到的最接近的是“严格”:
char** mem = stackalloc char*[100];有了进一步的限制,即完全由您来确保char*元素指向固定或非托管字符串。而且你不会索引“数组”超出界限。这不是很实际。
发布于 2016-02-24 09:31:26
因为C#致力于内存安全的垃圾收集,而不是C++,所以您应该了解内存管理的中性值。
例如,看看下面的代码:
public static void doAsync(){
var arr = stackalloc string[100];
arr[0] = "hi";
System.Threading.ThreadPool.QueueUserWorkItem(()=>{
Thread.Sleep(10000);
Console.Write(arr[0]);
});
}这个程序很容易崩溃。因为arr是堆栈分配的,所以一旦doAsync结束,对象+它的内存就会消失。lamda函数仍然指向这个无效的内存地址,这是无效的状态。
如果通过引用传递本地原语,也会出现同样的问题。
方案是:
静态对象->存在于整个应用程序定位时间。
只要创建本地对象的作用域是有效的,本地对象->就会存在。
堆分配对象(用new创建)只要有人持有对它们的引用,->就存在。
另一个问题是垃圾收集在时间段内工作。当一个对象是本地的,它应该在函数结束后就完成,因为之后--内存将被其他变量覆盖。GC不能强制完成对象,或者不应该,无论如何。
然而,好的是,C# JIT有时(并不总是)能够确定一个对象是否可以安全地分配到堆栈上,并且如果可能的话也会诉诸堆栈分配(同样,有时)。
另一方面,在C++中,您可以在任何地方声明所有内容,但是这比C#或Java更不安全,但是您可以微调应用程序,实现高性能低资源应用程序。
https://stackoverflow.com/questions/35598078
复制相似问题