0

在使用 XE2 时,我正在为一些内存分配和异常难以定位和修复异常而苦苦挣扎。

我有一个工厂类,它创建和存储动态创建的对象以在表单上显示,但在某些情况下工厂创建失败。如果我在程序启动后执行以下代码作为第一个操作,那么一切都很好 - 内存分配得很好,所有其他功能都可以工作。但是,如果我说打开另一个表单或打开文件对话框,然后执行工厂创建,则会出现异常:

Exception EAccessViolation: Access violation at address 0040AA1F in module
'UMTester.exe'. Write of address 00000069 (OS Exception)  Exception occurred at
$0040AA1F (Module "System", Procedure "@DynArrayAsg", Unit "", Line 0)

异常总是在地址 0040AA1F 并且 proc 总是@DynArrayAsg。

尽管工厂将在启动时立即创建,但我不想忽略该错误,因为那将是错误的形式!

这是工厂创建代码:

constructor TFactory.Create(FactoryObjectClass : tclass;Capacity : integer);
var nn : integer;
fptr : pointer;
fObj : TFactoryObject;
begin
  fClass := FactoryObjectClass;
  fsize := fclass.InstanceSize;
  fdata:=nil;

  //Capacity represents the number of objects; trying to allocate small amounts 
  //of memory results in exceptions so i artificially increase it anything less 
  //than 20 throws an exception
  if Capacity<19 then Capacity:=20;

  //This seems to reduce exceptions as the requested memory is in whole blocks 
  while frac((Capacity*SizeOf(Pointer))/4)<>0.0 do Inc(Capacity);

  fcapacity := Capacity;

  //Allocate memory
  getmem(fdata,fsize*fcapacity);
  getmem(fDatalist,sizeof(Pointer)*fcapacity);
  getmem(ffreelist,sizeof(Pointer)*fcapacity);

  fdatacount :=0;
  ffreecount :=0;
  fptr       := fdata;

  //create a pointer to the factory object at each memory address
  for nn := 0 to Capacity-1 do begin
   fdatalist[fdatacount]:= fptr;         <------- Exception is always here
   fobj := Fclass.InitInstance(fptr) as TFactoryObject;
   fobj.factory := self;
   fObj.Create;
   fptr := pointer(integer(fptr)+fsize);
   inc(fDataCount);
  end;
end;

工厂指针列表是从带有标签、备忘录和列表框等基本表单元素的 TDictionary 实例化的。

  procedure TObjs.Initialize(AObjs : TDict);
  var
   nn :integer;
   C : TRttiContext;
   T : TRttiInstanceType;
   V : TValue;
  begin
    //Process all objects requested
    for nn := 0 to AObjs.Count-1 do begin
      //Locate the class from the supplied reference in AObjs
      T := (C.GetType(TClass(FindClass(AObjs[nn]['Type']))) as TRttiInstanceType);

      //Invoke the creation of the object by calling its native constructor
      V := T.GetMethod('Create').Invoke(T.metaClassType,[Application]);

      //Adds each requested object to the repository and displays the object
      Objs[nn].SetValue(V.AsObject,AObjs[nn]);
    end;
  end;

观察和思考:

  1. 也许堆碎片意味着 fDatalist 请求的内存意味着我无法在一个连续的块中获得全部数量。
  2. 当创建工厂作为 pgm 启动后的第一个操作时,fDataList 始终位于地址 $1A9E2B0。如果我先显示另一个表单,则创建工厂 fDataList 的价格为 $1A9DF98。某些操作不会影响地址位置,并且当 fDataList 位于地址 $1A9E2B0 时不会引发异常。我没有将 fDataList 分配给特定地址,所以天知道为什么它在这个地址而不是其他地址工作。
  3. 在“如果容量<19 则容量:=20;”这一行上 如果我将 20 更改为 60,我可以将异常推迟更长时间,即我可以在创建工厂之前执行更多数量的不相关程序。尽管在一定数量的使用后总是会发生异常。

工厂在这里创建一次:

  SetLength(Objs,AObjs.Count);
  AFactory := TFactory.Create(TchObj,AObjs.Count);

  //Initialize the full array of objects
  for Index := 0 to AObjs.Count -1 do
    Objs[Index] := Afactory.Request_obj as TchObj;

TFactory 的类定义是:

  TchFactory = class
    private
      fdata      : pointer;
      fsize      : integer;
      fDataList  : PPointerList;  // from classes unit
      fdatacount : integer;
      fFreeList  : PPointerList;
      fFreeCount : integer;
      fCapacity  : integer;
      fcLass     : TClass;
    public
      constructor Create(FactoryObjectClass : Tclass;Capacity : integer);
      destructor  Destroy;   override;
      function    Request_Obj : TchFactoryObject;
      procedure   Recycle(FactoryObject : TchFactoryObject);
    property Capacity  : integer read fCapacity;
    property CountUsed : integer read fdataCount;
    property CountFree : integer read fFreeCount;
  end;

我可以看什么?
关于我如何调试的任何想法?
还是我只是在做一些非常错误的事情?

编辑: 删除 David 所指的“试验和错误”行并将 GetMem 更改为 AllocMem 可以解决问题。所以工厂构造函数的最终代码是:

constructor TchFactory.Create(FactoryObjectClass : tclass;Capacity : integer);
  var Index : integer;
  fptr : pointer;
  fObj : TchFactoryObject;
  begin
    fClass := FactoryObjectClass;
    fsize := fclass.InstanceSize;

    fcapacity := Capacity;
    fdata := AllocMem(fsize*fcapacity);
    fdatalist:= AllocMem(sizeof(Pointer)*fcapacity);
    fFreelist:= AllocMem(sizeof(Pointer)*fcapacity);
    fdatacount :=0;
    ffreecount :=0;
    fptr       := fdata;

    for index := 0 to Capacity-1 do begin

      fdatalist[fdatacount]:= fptr;
      fobj := Fclass.InitInstance(fptr) as TchFactoryObject;
      fobj.factory := self;
      fObj.Create;
      fptr := pointer(integer(fptr)+fsize);
      inc(fDataCount);

     end;      
  end;
4

2 回答 2

3

我只是在做一些非常错误的事情吗?

是的。大错特错。要实例化一个实例,您应该简单地调用构造函数。你想一次创建一堆?运行一个循环并调用该循环内的构造函数。


可以实例化内存中连续的对象,但这样做需要技巧和理解。它涉及依赖实现细节。只有在有合理的性能原因时,您才应该尝试这样做。据我所知,情况并非如此。因此,您需要一个简单的 for 循环以通常的方式实例化实例。

于 2013-11-13T21:50:32.277 回答
0

Afaik 这是允许的,如果没有完整的声明,很难看出哪里出了问题。但是 IIRC 你必须确保你的内存是零初始化的。

如果该类具有自动类型,那么这是必需的。

于 2013-11-13T22:12:00.930 回答