![](/img/trans.png)
[英]Why AxShockwaveFlash take so much memory or process while transitions?
[英]Why do my array of structs take up so much memory?
问题: Micro Framework如何为结构数组分配内存?
BitBucket存储库 ,包含要复制的代码。
我正在使用固定大小的数组来排队,以便在处理来自USB键盘的击键时插入延迟。 我正在使用struct
来表示关键的上下事件和延迟。
public struct QueuedEvent
{
public readonly EventType Type; // Byte
public readonly byte KeyPressed;
public readonly TinyTimeSpan Delay; // Int16
public readonly static QueuedEvent Empty = new QueuedEvent();
}
public enum EventType : byte
{
None = 0,
Delay = 1,
KeyDown = 2,
KeyUp = 3,
KeyPress = 4,
}
public class FixedSizeQueue
{
private readonly QueuedEvent[] _Array;
private int _Head = 0;
private int _Tail = 0;
public FixedSizeQueue(int size)
{
_Array = new QueuedEvent[size];
}
// Enqueue and Dequeue methods follow.
}
我原以为我的QueuedEvent
会在内存中占用4个字节,但是,基于查看垃圾收集器的调试输出(特别是VALUETYPE
和SZARRAY
类型),它实际上每个占用84个字节! 这让我觉得有点矫枉过正! (它实际上看起来每个都是84个字节,因为如果我分配了512个字节,我会得到一个OutOfMemoryException
。我有~20kB的RAM可用,所以我应该能够轻松地分配512个)。
问题(再次): Micro Framework如何为一个可以容纳4的结构分配84个字节?
这是一个不同大小的QueuedEvent
数组表(在我分配0时减去数量后):
+--------+-----------+-----------+---------+------------+-------+
| Number | VALUETYPE | B/Q'dEvnt | SZARRAY | B/Q'edEvnt | Total |
| 16 | 1152 | 72 | 192 | 12 | 84 |
| 32 | 2304 | 72 | 384 | 12 | 84 |
| 64 | 4608 | 72 | 768 | 12 | 84 |
| 128 | 9216 | 72 | 1536 | 12 | 84 |
+--------+-----------+-----------+---------+------------+-------+
根据SZARRAY
数字,我猜我的QueuedEvent
字段与Int32边界对齐,因此占用12个字节。 但我不知道额外的72字节来自哪里。
编辑:我通过调用Debug.GC(true)
获取这些数字,并观察我在调试器输出中得到的转储。 我还没有找到一个参考,它确切地标出了每个数字的含义。
我意识到我可以简单地分配一个int[]
,但这意味着我失去了很好的封装和结构的任何类型安全性。 而且我真的想知道结构体在微框架中的真正成本是什么。
我的TinyTimeSpan
很像常规TimeSpan
除了使用Int16
表示毫秒而不是Int64表示100ns刻度。
public struct TinyTimeSpan
{
public static readonly TinyTimeSpan Zero = new TinyTimeSpan(0);
private short _Milliseconds;
public TinyTimeSpan(short milliseconds)
{
_Milliseconds = milliseconds;
}
public TinyTimeSpan(TimeSpan ts)
{
_Milliseconds = (short)(ts.Ticks / TimeSpan.TicksPerMillisecond);
}
public int Milliseconds { get { return _Milliseconds; } }
public int Seconds { get { return _Milliseconds * 1000; } }
}
我正在使用FEZ Domino作为硬件。 这完全有可能是硬件特定的。 另外,Micro Framework 4.1。
我运行了大量的测试(这次是在模拟器中,而不是在真正的硬件上,但QueuedEvent
的数字是相同的,所以我假设我的硬件与其他测试相同)。
BitBucket存储库 ,包含要复制的代码。
以下整数类型和结构不会像VALUETYPE
那样吸引任何开销:
但是, Guid
确实:每个使用36个字节。
空静态成员使用72个字节(比数组中的相同结构少12个字节)分配VALUETYPE
。
将数组分配为static
成员不会改变任何内容。
在调试或发布模式下运行没有任何区别。 我不知道如何在没有连接调试器的情况下获取GC调试信息。 但是Micro Framework被解释了,所以我不知道非附加调试器会产生什么影响。
Micro Framework不支持unsafe
代码。 它也不支持StructLayout
Explicit
(从技术上来说,它确实支持,但没有FieldOffset
属性)。 StructLayout
Auto
和Sequential
没有区别。
以下是几个结构及其测量的内存分配:
// Uses 12 bytes in SZARRAY and 24 in VALUETYPE, total = 36 each
public struct JustAnInt32
{
public readonly Int32 Value;
}
// Uses 12 bytes in SZARRAY and 48 in VALUETYPE, total = 60 each
// Same as original QueuedEvent but only uses integral types.
public struct QueuedEventSimple
{
public readonly byte Type;
public readonly byte KeyPressed;
public readonly short DelayMilliseconds;
// Replacing the short with TimeSpan does not change memory usage.
}
// Uses 12 bytes in SZARRAY and 12 in VALUETYPE, total = 24 each
// I have to admit 24 bytes is a bit much for an empty struct!!
public struct Empty
{
}
似乎每次我使用自定义结构时,都会产生某种开销。 无论我在结构中包含什么,它在SZARRAY
总是需要12个字节。 所以我尝试了这个:
// Uses 12 bytes in SZARRAY and 36 in VALUETYPE, total = 48 each
public struct DifferentEntity
{
public readonly Double D;
public readonly TimeSpan T;
}
// Uses 12 bytes in SZARRAY and 108 in VALUETYPE, total = 120 each
public struct MultipleEntities
{
public readonly DifferentEntity E1;
public readonly DifferentEntity E2;
}
// Uses 12 bytes in SZARRAY and 60 in VALUETYPE, total = 72 each
// This is equivalent to MultipleEntities, but has quite different memory usage.
public struct TwoDoublesAndTimeSpans
{
public readonly double D1;
public readonly TimeSpan T1;
public readonly double D2;
public readonly TimeSpan T2;
}
在发布我自己的答案之后,我意识到每个项目的SZARRAY
总是有12字节的开销。 所以我测试了一个object[]
。 Micro Framework中的引用类型各占12个字节。
空结构public struct Empty { }
每个消耗24个字节。
基于我的测试,我猜测Micro Framework中的ValueTypes
不是真正的值类型,就像我们习惯在桌面CLR上一样。 至少,他们正在被装箱。 并且可能还涉及另一层次的间接。 这些成本是在(对嵌入式平台非常重要)内存开销中产生的。
我将在我的
。 FixedSizedQueue
转换为
int[]
实际上,我最终使用了UInt32[]
并添加了一些扩展方法来绕过比特抨击。
我在源代码中捅了一下,但找不到任何有用的东西(我真的不知道该找什么)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.