首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GetMem、指针和访问冲突

GetMem、指针和访问冲突
EN

Stack Overflow用户
提问于 2013-11-13 21:47:56
回答 2查看 1.6K关注 0票数 0

在使用XE2时,我遇到了一些内存分配问题,而且很难找到和修复异常。

我有一个工厂类,它创建并存储动态创建的对象,以便在窗体上显示,但是工厂创建在某些情况下失败。如果我在程序启动后作为第一个动作执行以下代码,那么一切都很好--内存被很好地分配,所有其他函数都正常工作。但是,如果我说打开另一个表单,或者打开一个文件对话框,然后执行工厂创建,就会有一个例外:

代码语言:javascript
复制
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。

虽然工厂将在启动时立即创建,但我不想忽略这个错误,因为这只是糟糕的形式!

以下是工厂创建代码:

代码语言:javascript
复制
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中实例化的。

代码语言:javascript
复制
  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. 在"if Capacity<19然后Capacity:=20“行上,如果我更改了20到60,我可以将异常推迟更长的时间,也就是说,在创建工厂之前,我可以执行更多的与非相关的过程。异常总是发生在一定数量的使用之后。

工厂在这里创建一次:

代码语言:javascript
复制
  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的类定义是:

代码语言:javascript
复制
  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修复了这个问题。因此,工厂构造函数的最终代码是:

代码语言:javascript
复制
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;
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-11-13 21:50:32

我是不是做错了什么疯狂的事?

是。疯狂的错误。要实例化一个实例,只需调用构造函数即可。你想立刻创造一堆吗?运行一个循环并调用该循环中的构造函数。

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

票数 3
EN

Stack Overflow用户

发布于 2013-11-13 22:12:00

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

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19965072

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档