15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


About Memory

Mike Blaszczak -- mikeblas@nwlink.com
Monday, September 23, 1996

Frequently, there seems to be many questions about memory allocation
on this list.  Lots of the questions stem from not understanding the
different memory allocation schemes available to Win32 programmers
using C++.

Let's try to clear up some of those issues:


First off, a memory manager is just that: it manages memory.  A memory
manager doesn't need to expose much of an interface--it just needs to
have some way to allocate some memoyr and  release that memory.
More complicated memory managers might offer extra bells and whistles
to get the status of free memory, to do error checking, or other
stuff.

Most memory managers implement their allocation and release interfaces
in the same way: they accept a request for memory by getting a size
of the desired memory block and return a pointer to that new block.
They might accept some flags which affect the way the allocation might
work.  The release request accepts, as a parameter, a pointer to one
of the blocks that it previously handed out.

The memory manager needs to keep a bunch of lists. It keeps a list of
memory blocks that're already allocated, and a list of memory blocks
that are available for use.  Most modern memory managers use a very
simple list-oriented data structure to get this work done.  The pointer
returned by an allocation request is just that: a pointer to the requested
memory.  But, usually, just before that memory, is data private to the
memory manager. The data usually indicates the state of the block--its
size, a flag indicating wether it is allocated or freed, and a pointer
to the next block.

If you use the C runtime library, you can use the calls malloc() and
free() to get memory.  You might allocate a kilobyte of memory by
coding:

	void* pMemory = malloc(1024);

upon return, pMemory either holds NULL indicating there's no memory to
be had, or a pointer to that kilobyte of memory. If you get a pointer
to the kilobyte of memory, the bytes just before the referenced memory
is preamble information that the memory manager uses by itself.  If
the pointer you got back was 0x1000, for example, the four bytes before
your memory block would actually be at 0x0FFC thru 0x0FFF.

If you needed to release this memory, you could code:

	free(pMemory);

The first thing the implementation of free() does is to step back
from the pointer you gave it and look at that preamble block to make
sure it's valid.  If it isn't valid, the implementation of free()
might emit a trace message to tell you, the developer who's carefully
debugging their code, that something is bogus.

These are all implementation details: it is possible to implement
a memory manager without this mechanism, but most people do it this
way.  The preamble meomry might be zero bytes in length, then, or
it might be a kilobyte in size.

Since the implementation of these routines is left to the designers 
of the library, you need to make sure your program only uses the
appropriate calls on the appropriate memory blocks.  If you were using
C++, you could allocate a kilobyte of memory by doing something like
this:

	unsigned char* pMemory = new unsigned char[1024];

You'd still get a usable pointer to a block of a kilobyte of memory,
but you'd have to be careful to correctly free the memory with a call
like this;

	delete [] pMemory;

if you used, instead, this call:

	free(pMemory);

you'd be in trouble because free() might or might not understand the
preamble that the new operator put on the memory before returning it
to you. While this code might work for now on whatever compiler
you're using, it might not work on other versions of the same runtime
library--and that's a risk you shouldn't take.  You should correctly
use delete with new, and free with malloc() (and its friends).

There is no reason to worry about mixing the use of free/malloc and
new/delete within the same program, as long as you're careful to use
the right functions on the right pointers.  There's nothing at all
wrong with this code:

	void* pMem1 = malloc(1024);
	unsigned char* pMem2 = new unsigned char[1024];
	// ... use the memory for something ...
	delete [] pMem2;
	free(pMem1);

If, on the other hand, you tried to free pMem2 by calling free(), or
delete pMem1 by using delete, you'd be in trouble.

If you _do_ mistakenly mix the functions, you'll end up getting errors
from the debug heap manager.  You'll probably end up crashing the
release version of the heap manager.

Since the preamble block, by the way, is up to the implementer, different
versions of the implementation might have slightly different versions of
the preamble block. The retail version of the Microsoft CRTL, for example,
has a tiny preamble that's optimized to high heaven.  The debug version,
on the other hand, has a much bigger block that carries lots of debug
and diagnostic information.  It's okay if it is slow: it's for debugging
only.

(In the libraries that ship with Visual C++ 4.2, the preamble size
is about 26 bytes.  So, our request for 1024 bytes really turns into
a request for 1060 bytes, and we get a pointer to the 27th byte back.
You, as the developer, never get to see those first 26 bytes--they're
reserved for use by the implementation of the library.  But since
you've read this far, you know that there's 26 bytes of someone
else's memory starting at pMemory-26.)

But this difference means that the debug version of free() can't be used
on memory gained with the release version of malloc(), and vice-versa,
and the same is true for debug and release versions of new and delete.

If you can be perfectly sure that you're not mixing these pointers, you
can use the different versions in different modules of your program.
But it's not that easy to control which you're using.  And, if you use
MFC, you simply can't solve the problem: MFC wants to pass pointers to
dynamically allocated memory throughout the program without prejudice
or restriction.


That covers everything you can do in C and C++ themselves.  But Windows
offers yet another variant: Windows, being an operating system,
provides its own memory management services. It turns out that the
Microsoft versions of the C and C++ runtime libraries end up calling
the operating system to get their memory.

You, as a developer, can directly call those same APIs if you want to.
Sometimes, it's very necessary to do so--if you're playing with OLE,
for example, or using the clipboard to pass a block of data to another
program.

The same rules apply here: you can't get some memory with a call to
the Windows API and then release it with a call to free() or an invocation
of the delete operator.

The implementation of any algorithm lends subtle characteristics to
the functionality intended by the algorithm. You might notice, for
example, that certain memory managers are bad at allocations of a small
size but very good at handling large allocations.  Most library vendors
do their best to make the library handle "normal" memory allocations
very well.  That makes the characteristics of the library very
complicated: maybe you only notice a performance degredation after 
allocating lots and lots of small blocks, one more big one, and then
freeing all but two of the small ones and then expanding the big one by
a tiny amount.  Since "big" and "small" are inspecific and relative
terms, you really can't be sure that you'll find any problems at all.

It is sometimes appropriate to mix different memory managers in the
same program. You must be careful not to use pointers to memory gained
from one memory manager with another memory manager, however.  And
that's all there is to it.



.B ekiM
http://www.nwlink.com/~mikeblas/
Don't look at my hands: look at my _shoulders_!
These words are my own. I do not speak on behalf of Microsoft.




Niels Ull Jacobsen -- nuj@kruger.dk
Thursday, September 26, 1996

[Mini-digest: 2 responses]

At 23:34 23-09-96 -0700, Mike Blaszcak wrote:

>Frequently, there seems to be many questions about memory allocation
>on this list.  Lots of the questions stem from not understanding the
>different memory allocation schemes available to Win32 programmers
>using C++.
[Most of a large, informative article left out - any hope of it showing u=
p
on MSDN  or in the MSVC docs or KB ?]=20

> It is sometimes appropriate to mix different memory managers in the
> same program. You must be careful not to use pointers to memory gained
> from one memory manager with another memory manager, however.  And
> that's all there is to it.

One more case, where you might run into problems: DLL's.

If a DLL is statically linked to the C RTL, memory blocks allocated by=20
the DLL must be freed by the DLL. Even if the application and the DLL
are using identical memory managers, and they are operating in the same
address space, they are using two different copies of the memory manager.
So you must keep track of who allocated what to use the correct free().

The simplest solution is to use the shared DLL version of the RTL, but if
your DLL is small and it will be called by non-VC programs, a statically
linked version may prove handier.

Niels Ull Jacobsen [MVP], Kr=FCger A/S (nuj@kruger.dk)
Everything stated herein is THE OFFICIAL POLICY of the entire Kruger=20
group and should be taken as legally binding in every respect.=20
Pigs will grow wings and fly.





-----From: "Dave Ryan" 

Thanks for a well informed article on memory allocation. However I believe
that the main reason for using delete with new is so the C++ compiler can
call the destructor of the object in order for the destructer to do
addition clean up for example deleting any other memory blocks allocated
with new if I'm correct. I was allways curious about the preamble block of
memory after an allocation while viewing the memory block in VC++. Thanks
and have a good day.




Mike Blaszczak -- mikeblas@nwlink.com
Saturday, September 28, 1996

At 09:27 9/26/96 +0200, Niels Ull Jacobsen wrote:

>[Most of a large, informative article left out - any hope of it showing up
>on MSDN  or in the MSVC docs or KB ?] 

I don't know. It seems like most of the information is already on the CD,
if you look carefully.

>If a DLL is statically linked to the C RTL, memory blocks allocated by 
>the DLL must be freed by the DLL. Even if the application and the DLL
>are using identical memory managers, and they are operating in the same
>address space, they are using two different copies of the memory manager.

Strictly, this isn't true: if the DLL uses GlobalAlloc() and passes the
resulting handle or pointer back to the application, the applicaiton can
call GlobalFree() (or GlobalAnythingElse() on it), without hesitation.

This limitation _does_ exist if you're using different memory managers:
the DLL might be getting memory out of a heap allocated and managed by it's
copy of the runtime.  If it passes it to another module that's using the
same runtime code but expects to see memory in a different heap, then
there's problems.

If you'd said "... memory blocks allocated by the 4.x versions of
the Visual C++ runtime library in the DLL ...", you'd've hit the bull
on the head.

>Thanks for a well informed article on memory allocation. However I believe
>that the main reason for using delete with new is so the C++ compiler can
>call the destructor of the object in order for the destructer to do
>addition clean up for example deleting any other memory blocks allocated
>with new if I'm correct.


If you're think you're a tough guy, you can always do this:
       
        CVerbose* pVerbose;
        pVerbose = malloc(sizeof(CVerbose));
        pVerbose->CVerbose::CVerbose();

and then, later,

        pVerbose->CVerbose::~CVerbose();
        free(pVerbose);

and you could expect it to work.  Your friends might think you're a kook,
though.
.B ekiM
http://www.nwlink.com/~mikeblas/
Don't look at my hands: look at my _shoulders_!
These words are my own. I do not speak on behalf of Microsoft.





| Вернуться в корень Архива |