Now: Tutorial for Web and Software Design > Programming > delphi > Programming Content
> Memory [Bookmark it]
Memory

Memory

32-bit pointer
About objects and memory usage
TList allocating memory
Heaping objects
Finding total global variable size
DLL's and Memory Management
TotalSystemMemory
Freeing Pointers to Constants
Destroy dynamically-created components
Arrays bigger than 64k


32-bit pointer

Question


Does anyone know how to define a 32-bit pointer? If I want to use

LPBitMapInfo, defined in the WinApi as a 32bit pointer to the TBitMapInfo

class, but not reconized by delphi (why?), I will have to create my own

type:



My_LPBitMapInfo =3D ^TBitMapInfo



It should be a 16bit pointer, right?

Answer


A:

All pointers in Delphi are 32-bits.  Here is a simple test.

Place the following code into a buttons onclick event handler and you

will get all the verification you need.

ShowMessage( Format('Pointers are %d bytes long or %d-bits', [sizeof(pointer),8*sizeof(pointer)]) );



A:

I think you're confusing the size of the data element with how it's interpreted into a physical address. "Pointers" even on the old 8086/8088 processors were 32 bits in size; including a 16-bit segment and a 16-bit offset, which were finally combined to create a 20-bit physical address; some different combinations of segment and offset could therefore resolve to the same physical address, which led to the need to "normalize" pointers for some purposes.



For 16-bit Enhanced mode Windows, pointers in all of Borland's Pascal products have been intra-segment, that is, the "segment" portion of a given pointer should always resolve to the same extended memory selector; then, given a 16-bit "offset", this seg/ofs model gives a maximum of 64kb for a data structure.  Windows provides methods whereby

to overcome this limitation (using the _AHIncr procedure for instance), but it's not automatic like using C's "Huge" data directive.  16-bit Delphi doesn't have "Huge" pointers in that sense.  32-bit Delphi is supposed to fix this.



As far as I can tell, the VCL code that allows reading of greater than 64k bitmaps (for instance) uses (and well conceals) these Windows kludges; this would be a good resource for seeing how this can be done in Delphi.



A:

Yes, but they're 16:16 pointers, as opposed to the 16:32 pointers he probably

wanted.



A:

Gentlemen, we are talking apples and oranges here. If the C DLL is compiled by a 16 bit compiler, the pointers in Delphi are ABSOLUTELY compatible with the 16:16 bit far and huge pointers in C (or C++). There is not problem whatsoever. Just pass them.



On the other hand, if the C (or C++)application is a Win32 application, it uses 32 bit offsets only. The segments (more appropiately selectors) are invisible and pressumed of

zero linear base address.

Note that i said application. Ring 0 components also use 32 bit pointers but might rarely use 48 bit pointers. VXD's are the ring 0 components.



To call a 16 bit application you just do it. Nothing special. To call or be called by a Win32 application you need a thunk. Not just for the pointers but also to change from

a 16 bit stack to a 32 bit stack. And much more. If this is what you need to do, check the

file CALL32NT.ZIP, available in many places in the web.




About objects and memory usage

Question


It's well known that memory allocation under Windows shouldn't

be made at a very low granularity because of the limited system

handles available.



 If I have to create several instances of objects at runtime

  - Does Delphi allocate them in global memory?

  - Am I restricted ... to the local heap size?

  - Should I apply any special programming technique in order to

    avoid problems with allocation, or just freeing the objects?

Answer


A:

1.  Objects are definitely allocated on (from?) the global heap,

    although I can't find a clear statement to this effect in

    either manuals or on-line help.  (I'm 99% sure it's there;

    I just can't find it right now).



2.  Delphi has a memory sub-allocator which deals with the

    granularity issue.  Do  a search on "Memory" in the on-line

    help, and select the topic "Heap Manager" for a description

    of this.  I have assumed that the description given there

    applies to object instantiation as well as memory allocated

    with New or GetMem.  (I don't have the source for TObject's

    Create method, and without it I can't be 100% sure).



3.  My general philosophy for freeing in Delphi is to Free

    objects which I Create, and leave Delphi components to Free

    anything which they Create.   Unusual situations may require

    a different approach.


TList allocating memory

Question


My question is do I need to allocate a new record for each Add() to the TList

...   and   ...    does deleting an item from the TList also free the copy of

the record?

Answer


A:

If you are going to create your own list class you must call NEW &

DISPOSE EVERY TIME or horrible things will happen on your machine.

The way I would handle that is in the add, delete and free methods.



A:

I don't get this. In this example he allocates spaces for a

record, then adds the pointer to that record to the list. How in the

world would TList i.e. Delphi then know how to deallocate the space

again? I still recommend using a TClass descendant instead of the

record. Then each element is responsible for deallocating its own

space. And TList knows how to call a TClass descendants Free method,

thus there is no need override TLists constructor/destructor.


Heaping objects

Question


I have allocated objects on the heap, created pointer to them, but I cannot

call any of their methods.

My Object is TAnimal, here is my syntax:

ptrAnimal^.Run;

This does not seem to work, although the same syntax works for setting

properties. What am I doing wrong?

Answer


A:

If you created this class your self and inherited it from someone else  you

should try the following:

If you want to access the method, say Click, you'll have to assign the

procedure to yours object's event click, that is:

ptrAnimal^.OnClick:= OnMyClick;

This is the procedure wich gets the event, looking something like this:

procedure [FILE].OnMyClick(SENDER: TObject);

BEGIN

  (SENDER as TAnimal).YourProperty:= Value;

END;


Finding total global variable size

Question


Is there a way to determine how much of the data segment all the global

variables/constants in my program are occupying?

Answer


You can create a map file in the linker options, recompile and then take a

look at it. Look for the globals and the data segment size. Select a detailed

map file.


DLL's and Memory Management

Question


Does anyone know how Delphi manages it's memory?

I have an app written in Delphi, which calls a DLL, also written in Delphi. I

have done a lot of work on both, and now whenever I try to run the two, I get

a GPF (I now suspect it is a memory problem).

First, how can I guestimate how much memory is used by my DLL and how much is

used by the EXE?

Where does the DLL get its memory from?

Answer


I doubt if the GPF is being caused by a shortage of memory. If it was stack

shortage you would get Error 202. In the case of heap memory, Windows supports

virtual global memory so the normal symptom of a shortage of real memory is a

drastic slow-down rather than a GPF.



    From the "Writing DLLs" on-line help:



Global Variables: A DLL has its own data segment and any variables declared

in a DLL are private to that DLL. A DLL cannot access variables declared by

modules that call the DLL, and it is not possible for a DLL to export its

variables for use by other modules. Such access must take place through a

procedural interface.



DLLs and Stack Segments: A DLL does not have its own stack segment, so it uses

the stack segment of the application that called the DLL.



Also note the following advice from "Run-time errors in DLLs"



If a run-time error does occur in a DLL, the safest thing to do is to exit

Windows entirely. If you simply try to modify and rebuild the faulty DLL code,

when you run your program again, Windows will not load the new version of the

DLL if the faulty one is still in memory. Exiting Windows and then restarting

Windows and Delphi ensures that your corrected version of the DLL is loaded.



I would try and re-create the problem with the DLL code in a normal unit so

that the debugger can help you.



A:

If you've ever developed a serious DLL, you'd know that exiting Windows every

time you get a GPF is totally impractical.  You can generally just kill it

from memory and recompile and everything is fine.  I've done this many, many

times.  Also, once you have your DLL working, it is often impractical to move

the offending code to a 'normal' unit within an EXE to debug it.  So, the

answer to this problem is Turbo Debugger for Windows, which comes in the RAD

pack.  To debug a DLL with it, you can simply include a 'hard' breakpoint by

adding 'inline($cc);' to your code near the offending area, or immediately

after the entry point to the DLL.  This will wake up the debugger and you can

step through your code.


TotalSystemMemory

Question




Answer


Unit MemInfo;



Interface



Procedure FreeMemory(Var lTotalMemory: LongInt; Var lFreeMemory: LongInt);





Implementation



Uses WinTypes, WinProcs, ToolHelp;



Function Min(Number1, Number2 : LongInt) : LongInt;

{Returns the minimum of Number1 & Number2}

Begin

     If (Number1 <= Number2) Then

        Min := Number1

     Else

        Min := Number2;

End; {end Function, Min()}



Procedure FreeMemory(Var lTotalMemory: LongInt; Var lFreeMemory: LongInt);

{Calculates and returns the amount of Total & Free Memory in bytes

 (ie. divide each by 1024 of Kilobytes)

 NB: Total Memory will be 0 if windows is running in Standard Mode since

     actual Total Memory is not able to be determined.}

Var

   lWinFlags : LongInt;

   mmiMemManInfo : TMemManInfo;

Begin

     {Initialise Variables}

     lTotalMemory := 0;

     lFreeMemory  := 0;

     lWinFlags    := GetWinFlags;

     If (0 <> (lWinFlags And WF_ENHANCED)) Then

     Begin

          {Initialise MemManInfo structure}

          mmiMemManInfo.dwSize               := SizeOf(TMemManInfo);

          mmiMemManInfo.dwLargestFreeBlock   := 0;

          mmiMemManInfo.dwMaxPagesAvailable  := 0;

          mmiMemManInfo.dwMaxPagesLockable   := 0;

          mmiMemManInfo.dwTotalLinearSpace   := 0;

          mmiMemManInfo.dwTotalUnlockedPages := 0;

          mmiMemManInfo.dwFreePages          := 0;

          mmiMemManInfo.dwTotalPages         := 0;

          mmiMemManInfo.dwFreeLinearSpace    := 0;

          mmiMemManInfo.dwSwapFilePages      := 0;

          mmiMemManInfo.wPageSize            := 0;

          MemManInfo(@mmiMemManInfo);  {Get Memory Manager Information}

          {Calculate Total Memory}

          lTotalMemory := (Min(mmiMemManInfo.dwTotalLinearSpace,

                               mmiMemManInfo.dwTotalPages +

mmiMemManInfo.dwSwapFilePages)

                           * mmiMemManInfo.wPageSize);



          {Calculate Free Memory}

          lFreeMemory := GetFreeSpace(0);

     End

     Else

     Begin

          {Total Memory = 0}

          lTotalMemory := 0;

          {Calculate Free Memory}

          lFreeMemory := GetFreeSpace(0);

     End; {end If-Then-Else}

End; {End Procedure, TotalMemory()}



End. {end of Unit, MemInfo}


Freeing Pointers to Constants

Question


I have been using using a listbox to store a database string and long

 integer.  The longint is store in the Object field:

    MyList.AddObject(sDescription, POINTER(lKey) );



Now, how do I reclaim the memory used by the lKeys stored in the Listbox?

Answer


You don't have to.  The Data property of TList is simply a space to put an

address.  Since your just storing a number there and the number doesn't point to

any memory that you have allocated there is nothing to free/reclaim.  When the

TList items are deleted the space allocated to hold your pointer will be freed.


Destroy dynamically-created components

Question


I use a few dynamically-created buttons:

I destroy them using this code since I don't have any names assigned

to them at runtime.



for i := 0 to ComponentCount-1 do

  if (Components[i] is TButton) then TButton(Components[i]).Free;



However, it gives an "Index out of bounds" error.  this is really

surprising since I thought by using "if (Components[i] is....",

I destroy the dynamic button ONLY when the specific component

is confirmed to be a "TButton" type.

Also, this error seems to appear only when I call the "Free" method

for this dynamic button.

If I use something such as showing its name:



for i := 0 to ComponentCount-1 do

  if (Components[i] is TButton) then

      ShowMessage( TButton(Components[i]).NAME );



The above will work fine!  so I thought there may be some caveats

when it comes to freeing dynamically-created buttons.

Answer


A:

You have to think about the Components as a List, so when you cancel out

an item at a certain position, the position left vacant WON'T REMAIN EMPTY,

but it will be replace with the item immediately after and the ComponentCount

property, to which you index base the upper bound of your loop, should be

decreased as well!. A safer (and working way) is to walk thru lists beginning

from the bottom as in:



for i := Pred(ComponentCount) downto 0 do

  if (Components[i] is TButton) then TButton(Components[i]).Free;



A:

I don't have an answer to the question "Why?" but I have had the same

problem when destroying all MDI child windows in a program I've been

working on.  The solution that I came up with, which may also

work for you is:



{ Remove current MDI child windows }

;	WHILE MDIChildCount > 0

DO	BEGIN

	;	MDIChildren[0].Close

	;	Application.ProcessMessages

        END



In the MDI child window OnClose event it sets the action to "caFree" which

will free the window.



The problem I had was that the windows weren't being freed. 



*FLASH* I just thought of this: Is the freeing of components tied

to Application.ProcessMessages?  I know that there are events triggered

when they get free'd, etc. My guess is that the messages need to be 

processed before the free is completed.  I think that this may be the

case because the following code produced an infinite loop:



{ Remove current MDI child windows }

;	WHILE MDIChildCount > 0

DO	MDIChildren[0].Close



When I added Application.ProcessMessages it worked and I moved on to

finish the section of code around it. I didn't question why it worked,

but was glad that it did.



A:

There's a very subtle mistake here...



for i := 0 to ComponentCount-1 do

  if (Components[i] is TButton) then TButton(Components[i]).Free;



While you are freeing your components, the component list is updated, but the

upperbound of your for loop isn't. This means your freeing components that

don't exist any longer. Use:



for i := 0 to ComponentCount-1 do

  if (Components[0] is TButton) then TButton(Components[0]).Free;

***  Index i changed to 0 ! ***



To delete them all.


Arrays bigger than 64k

Question


I can't manage arrays bigger than 64k.

There is a problem to cross this barrier I really cannot solve.

Look at the code that follows, I fill a big array of bytes with

random(100) and calculate the sum of the values added (ie Res1).

Then I read the array and recalculate the sum of the values it

contains (ie res2).

If the size of the array is <65535 then

  it's ok,

else

  it's not ok...

Answer


There is no way to directly access an array with over 65520 worth of data.

Either you use GlobalAlloc or a TMemoryStream to allocate the memory and

build a class to access it or you do it directly by hand.

You can get to subsequent segments on a GlobalAlloc'ed object by using

SelectorInc to build the pointer. The easiest way is to use TMemoryStream.



Type

  Tmyarr = Class

    buffer  : TMemoryStream;

    elsize  : LongInt;



    Constructor Create(esize, number : Word);

    Destructor Free;

    Procedure SetElement(index : Word; p : Pointer);

    Procedure GetElement(index : Word; p : Pointer);

  End;



Implementation



Constructor Tmyarr.Create(esize, number : Word);

Var

  size : LongInt;

Begin

  Inherited Create;

  buffer := TMemoryStream.Create;

  elsize := esize;

  size := esize * number;

  buffer.SetSize(size);

End;



Procedure Tmyarr.Free;

Begin

  If Self <> Nil Then

  Begin

    buffer.Free;

    Destroy;

  End;

End;



Procedure GetElement(index : Word; p : Pointer);

Begin

  buffer.Seek(elsize * index, 0);

  buffer.Read(p^, elsize);

End;



Procedure SetElement(index : Word; ptr : Pointer);

Begin

  buffer.Seek(elsize * index, 0);

  buffer.Write(p^, elsize);

End;


[Bookmark][Print] [Close][To Top]
  • Prev Article-Programming:

  • Next Article-Programming:
  • Related Materias
    Using the For匛ach Stateme
    Executing a VB Program wit
    Visual Basic Explorer - OL
    Visual Basic Explorer- Gam
    Visual Basic Explorer- Sou
    Visual Basic Explorer- Gam
    Visual Basic Explorer- Gam
    Visual Basic Explorer- Gam
    ATL Tutorial - Events meth
    Active Directory Programmi
    Topics
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Graphic Design Tutorial
     

    Coreldraw Tutorial

      Illustrator Tutorial
      3D Graphics Articles
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial&Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial&Articles
     

    XML Style Tutorial

      AJAX Tutorial
      XML Mobile
    Flash Tutorial&Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial&Articles
     

    Linux Tutorial

      Symbian Tutorial
      MacOS Tutorial