繁体   English   中英

为什么我的结构数组占用了这么多内存?

[英]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个字节,但是,基于查看垃圾收集器的调试输出(特别是VALUETYPESZARRAY类型),它实际上每个占用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那样吸引任何开销:

  • 字节(1个字节)
  • Int32(4个字节)
  • Int16(2个字节)
  • Int64(8字节)
  • 双(8字节)
  • TimeSpan(12个字节 - 奇怪,因为它的内部成员是Int64)
  • DateTime(12个字节 - 奇怪)

但是, Guid确实:每个使用36个字节。

空静态成员使用72个字节(比数组中的相同结构少12个字节)分配VALUETYPE

将数组分配为static成员不会改变任何内容。

在调试或发布模式下运行没有任何区别。 我不知道如何在没有连接调试器的情况下获取GC调试信息。 但是Micro Framework被解释了,所以我不知道非附加调试器会产生什么影响。

Micro Framework不支持unsafe代码。 它也不支持StructLayout Explicit (从技术上来说,它确实支持,但没有FieldOffset属性)。 StructLayout AutoSequential没有区别。

以下是几个结构及其测量的内存分配:

// 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM