简体   繁体   English

使用作为泛型类型数组的类成员进行奇怪的类实例大小调整

[英]Weird class instance sizing using class members that are arrays of generic types

Below is a very simple bit of code that mimics a class structure in some code I have (the form just contains a single button attached to the click event). 下面是一个非常简单的代码,它模仿我的一些代码中的类结构(表单只包含一个附加到click事件的单个按钮)。 I am using Delphi XE and XE II and see nasty crashes when destroying objects in my production code that this class is based on. 我正在使用Delphi XE和XE II,并在销毁此类所基于的生产代码中的对象时看到令人讨厌的崩溃。 These crashes only occur if I allow the members of the array item in TMyClass to be initialised in the Clear() method. 仅当我允许在Clear()方法中初始化TMyClass中的数组项的成员时,才会发生这些崩溃。

Unfortunately the crash is not repeatable so far in this sample code, but it does mimic some very weird behaviour with instance sizing that I suspect might be the cause of the problem. 不幸的是,到目前为止,在此示例代码中崩溃是不可重复的,但它确实模仿了一些非常奇怪的行为,我怀疑这可能是导致问题的原因。

If I place a break point in the TMyClass.Clear function and look at the InstanceSize member of the class reports 1288. Which is odd, as the size of the array member alone is really 12kb. 如果我在TMyClass.Clear函数中放置一个断点并查看类报告1288的InstanceSize成员。这很奇怪,因为单独的数组成员的大小实际上是12kb。 I can change the type being provided from TRecord2 to Integer and I get the same InstanceSize result. 我可以将从TRecord2提供的类型更改为Integer,并获得相同的InstanceSize结果。 If I remove the array entirely from the class I get an Instance size of ~264 bytes, which seems over the top for a class that contains only a single 128 byte record instance. 如果我从类中完全删除数组,我得到的实例大小约为264字节,对于只包含一个128字节记录实例的类来说,这似乎超过了顶层。 This indicates that the compiler has allocated 1024 bytes to the array storage for the V (of type TT) member in TMyClass. 这表明编译器已为TMyClass中的V(TT类型)成员的数组存储分配了1024个字节。

I am suspicious that the weird instance size is causing bad things to happen when I write 12kb of data into an object that the compiler thinks is 1kb in size. 我怀疑当我将12kb数据写入编译器认为大小为1kb的对象时,奇怪的实例大小会导致不良事件发生。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TRecord = record A : Array[1..128] of byte; end;
  TRecord2 = packed record S : Single; T : TDateTime; end;

  TBase = class(TObject)
    public
      R : TRecord;
  end;

  TBase2<T> = class(TBase)
    public
      type
        TT = packed array [0..31, 0..31] of T;

      var
        V : TT;
        R2 : TRecord;
  end;

  TMyClass = class(TBase2<TRecord2>)
    public
      procedure Clear;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  O : TMyClass;
begin
  O := TMyClass.Create;
  O.Clear;
  O.Free;
end;

{ TMyClass }

procedure TMyClass.Clear;
var
  i, j : integer;
begin
  for i := 0 to 31 do
    for j := 0 to 31 do
      begin
        V[I, J].S := 0;
        V[I, J].T := 0;
      end;
end;

end.

It's a bug, as is shown by this minimal reproduction: 这是一个错误,正如这个最小的再现所示:

program InstanceSizeBug;

{$APPTYPE CONSOLE}

type
  TMyClass<T> = class
    V: array [0..255] of T;
  end;
  TMyClassSingle = class(TMyClass<Single>);

begin
  Writeln(TMyClass<Single>.InstanceSize);
  Writeln(TMyClassSingle.InstanceSize);
  Readln;
end.

Output: 输出:

1032
264

Note that the same behaviour can be observed in Delphi 2010, the only other Delphi version i have to hand. 请注意,在Delphi 2010中可以观察到相同的行为,这是我必须提供的唯一其他Delphi版本。

Trace through under the debugger and you find yourself in _GetMem with the Size parameter equal to 264 so clearly not enough memory is being allocated. 在调试器下跟踪并发现自己在_GetMemSize参数等于264因此显然没有足够的内存分配。

And just in case there is any doubt, this version fails with an AV. 如果有任何疑问,这个版本失败了AV。

program InstanceSizeBug;

{$APPTYPE CONSOLE}

type
  TMyClass<T> = class
    V: array [1..256*256] of T;
  end;
  TMyClassSingle = class(TMyClass<Single>);

begin
  TMyClassSingle.Create.V[256*256] := 0;
end.

I have submitted this to Quality Central, issue #100561 . 我已将此提交给Quality Central,问题#100561

As a workaround I suggest you use a dynamic array which you allocate using SetLength in the constructor. 作为一种解决方法,我建议您使用在构造函数中使用SetLength分配的动态数组。 I rather imagine that it is the presence of a fixed sized generic array that is making the compiler misbehave. 我宁愿想象它是一个固定大小的通用数组的存在,使编译器行为不端。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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