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

问题:Micro Framework如何为结构数组分配内存?

具有要复制代码的BitBucket存储库。

上下文和细节

我正在使用固定大小的数组进行排队,以在处理USB键盘的击键过程中插入延迟。 我正在使用public struct Empty { }来表示按键向上和向下事件以及延迟。

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

我以为我的public struct Empty { }将在内存中占用4个字节,但是基于查看垃圾收集器的调试输出(特别是object[]Explicit类型),它实际上每个占用84个字节! 这让我感到吃力! (实际上,每个字节似乎是84个字节,因为如果我分配512个字节,我将得到FieldOffset。我有约20kB的可用RAM,因此我应该能够轻松分配512个字节)。

问题(再次):Micro Framework如何设法为可容纳4个的结构分配84个字节?

垃圾收集器的数字

这是public struct Empty { }的不同大小的数组的表(在我分配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    |
+--------+-----------+-----------+---------+------------+-------+

基于public struct Empty { }数字,我猜我的object[]字段与Int32边界对齐,因此占用了12个字节。 但是我不知道多余的72个字节是从哪里来的。

编辑:我通过调用public struct Empty { }并观察调试器输出中得到的转储来获取这些数字。 我还没有找到可以准确识别每个数字含义的参考。

我意识到我可以简单地分配一个public struct Empty { },但这意味着我失去了很好的封装以及该结构的任何类型安全性。 我真的很想知道微框架中结构的真正成本是多少。


我的public struct Empty { }与常规object[]非常相似,除了使用Explicit表示毫秒数,而不是使用表示100ns滴答声的Int64。

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。

编辑-更多测试和评论答案

我进行了很多测试(这次是在仿真器中,不是在真实的硬件上进行的,但是public struct Empty { }的数字是相同的,因此我假设其他测试的硬件是相同的)。

具有要复制代码的BitBucket存储库。

以下整数类型和结构不会像public struct Empty { }那样引起任何开销:

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

但是,public struct Empty { }会:每个使用36个字节。

空的静态成员确实使用72个字节(比数组中的相同结构少12个字节)分配public struct Empty { }

将阵列分配为public struct Empty { }成员不会更改任何内容。

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

Micro Framework不支持public struct Empty { }代码。 它也不支持object[] 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
{ 
}

似乎每次使用自定义结构时,都会产生某种开销。 而且无论我在结构中包含什么内容,它在public struct Empty { }中始终需要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;
}

次要编辑

发布我自己的答案后,我意识到每个项目public struct Empty { }中总会有12个字节的开销。 因此,我测试了object[]。在Micro Framework中,引用类型每个消耗12个字节。

空结构public struct Empty { }每个占用24个字节。

1个解决方案
13 votes

根据我的测试,我猜想Micro Framework中的UInt32[]不是真正的值类型,就像我们在桌面CLR上习惯的那样。 至少,它们被装箱了。 并且可能还会涉及间接的另一个层次。 这些成本是在(相当大的嵌入式平台)内存开销中产生的。

我将在FixedSizedQueue中转换为UInt32[]

实际上,我最终使用了UInt32[],并添加了一些扩展方法来环绕位冲击。

我在源代码中打了一下,但是找不到任何有用的东西(而且我真的不知道该找什么)。

ligos answered 2019-10-08T04:03:39Z
translate from https://stackoverflow.com:/questions/12445185/why-do-my-array-of-structs-take-up-so-much-memory