GetMem,指针和访问冲突

时间:2013-11-13 21:47:57

标签: delphi pointers memory-management factory

我正在努力处理一些内存分配,并且在使用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. 在“如果容量&lt; 19然后容量:= 20;”行上如果我将20更改为60,我可以将异常推迟更长时间,即我可以在创建工厂之前执行更多数量的不相关程序。尽管经过一定的使用后仍会出现异常。
  4. 工厂在这里创建一次:

      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;
    

2 个答案:

答案 0 :(得分:3)

  

我只是做了一些疯狂的错误?

是。疯狂错了。要实例化一个实例,您应该只需调用构造函数。你想一次创建一堆吗?运行一个循环并在该循环中调用构造函数。


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

答案 1 :(得分:0)

Afaik这是允许的,如果没有完整声明,很难看出出了什么问题。 但是你必须确保你的记忆是零初始化的。

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