4

Im trying to allocate preciesly 1 KiB of memory by the usage of pointers

GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
        std::cout << pmc.WorkingSetSize << " Current physical memory used by the process" << std::endl;
        int a = pmc.WorkingSetSize;
        char *test= new char[1024];

GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
        int b = pmc.WorkingSetSize;
            std::cout << "Actual allocated  " << (b - a) / 1024 << std::endl;

problem is everytime i run this code it seems to allocate anywhere between 100 KiB to 400 KiB i used char since it's 1 byte in size

Asesh
  • 3,186
  • 2
  • 21
  • 31
  • 1
    It is operating system specific: `GetCurrentProcess` is *not* in the C++ standard. – Basile Starynkevitch Apr 06 '18 at 08:11
  • I guess you're using Windows? – user202729 Apr 06 '18 at 08:11
  • 4
    "... it seems to allocate anywhere between 100 KiB to 400 KiB" where do you get this numbers from? `new char[1024]` allocates 1024 bytes for you, what the os actually allocates might be something different – 463035818_is_not_an_ai Apr 06 '18 at 08:11
  • btw "where do you get this numbers from" = facepalm, didnt read the code carefully ;) – 463035818_is_not_an_ai Apr 06 '18 at 08:12
  • 1
    The immediate issue is, that you do not understand what the [Working Set](https://msdn.microsoft.com/en-us/library/windows/desktop/cc441804.aspx) is. *"The working set of a process is the set of pages in the virtual address space of the process that are **currently resident in physical memory**."* – IInspectable Apr 06 '18 at 09:44

3 Answers3

8

The implementation is free to allocate more than you asked for behind your back in order to be able to satisfy future (expected) allocations faster (without having to involve the OS). This is usually a good optimization and nothing to worry about - especially since operating systems may not even back that allcation with actual physical pages before you actually write to them, so it often costs nothing at all.

In any case, from a C++ language perspective; as long as you got the memory you requested, whether or not the implementation actually allocated more is not usually something you can know nor something you should care about.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
4

The code behind new is aware of the Operating System. It often knows which allocation sizes are efficient. And as it turns out, most programs make many more allocations of smaller sizes. Hence, the code behind new generally asks for larger chunks of memory and subdivides each chunk as demanded.

It looks like new in your case asks for approximately 100kB, hands out 1KB, and has 99kB so satisfy future requests without bothering the OS.

MSalters
  • 173,980
  • 10
  • 155
  • 350
2

The ::operator new is allocating memory (as documented) and is provided by your C++ standard library (which is some specification written in English and part of the C++ standard, e.g. drafted in n3337). On your system and computer, its implementation is using operating system services (in principle, you might have some C++ standard library implementation which does not use any OS and runs on some bare hardware, but I know none). You should expect new & delete to need some internal overhead and to consume resources (but you should not really care and hope it stays reasonable).

Read Operating Systems: Three Easy Pieces (freely downloadable textbook on OSes).

The terminology is not entirely consensual, since Microsoft has its own. However, the concepts and explanations in that book apply to most current OSes, including Microsoft Windows. You could also find various lectures and slides explaining similar concepts with slightly different terminology. But I found that textbook well written and I recommend reading it. Once you have understood most of that book, you'll be able to answer by yourself to your question, and spot differences in terminology.


Your C++ standard library implementation is probably requesting heap memory from your operating system (perhaps using some OS specific system calls -on Linux these includes mmap(2), I don't know what system calls are available on Windows; that information might not be public). Of course the virtual address space of your process grows by multiple of pages.

The virtual address space is defined by Wikipedia as

"the set of ranges of virtual addresses that an operating system makes available to a process"

and (if using that definition) it does change (probably with VirtualAlloc -likely to be called by new and malloc- & LoadLibrary and other functions). BTW, on x86-64 the page size (determined by the hardware, e.g. the MMU) is often 4kbytes, so it would be impossible that your virtual address space would grow by only 1024 bytes.

(see the note in the PS below; for Iinspectable and Microsoft, "virtual address space" has a different definition so means something different; I am using it in the wikipedia definition, and then it may vary with time)

There is no guaranteed relation between both (memory allocated by new, and virtual address space supplied by the OS). In many systems, new calls malloc (from the C standard library) which asks memory from the OS but prefer reusing previously free-d memory zones when possible. Some C++ standard library implementations might directly call OS services for memory management. Details are implementation specific.

Asking heap memory, and releasing it back, from/to your operating system kernel, is generally an expensive operation. So your implementation usually prefers to ask more memory than needed, and keep previously delete-d (or free-d) memory zones for later reuse.

BTW, if you used Linux, all of the C++ standard library, the C++ compiler, the C standard library, and the kernel are free software, and you could study their source code to get all the gory details.

Notice that the virtual address space does change (thru mmap(2) and related system calls on Linux, with VirtualAlloc and other functions like its LoadLibrary, and probably several other system functions on Windows). BTW, on Linux, you can use proc(5) (with /proc/self/maps) to query programmatically that virtual address space. I guess there is some way on Windows to query it also. The VAS is changing when some "set of ranges of virtual addresses that an operating system made available to a process" changes, e.g. when a region changes (like after successful VirtualAlloc on Windows or mmap on Linux).

Iinspectable claims in his comment that the virtual address space is never changing, but he defines virtual address space in different and unusual way. I'm sticking to wikipedia's definition which is much more common, and was even used in the 1970s on IBM mainframes

it seems to allocate anywhere between 100 KiB to 400 KiB

You probably need to study the source code of your C++ standard library implementation (which could be difficult or costly to get) to explain that. My guess is that such a variability could be related to ASLR (but it is just a blind guess and I could be wrong).

If you are concerned about the actual memory consumption, you are allowed to redefine your own implementation of ::operator new (and related, notably delete). The following one is probably conforming to the letter of the standard:

void* ::operator new  ( std::size_t count ) { throw std::bad_alloc; }

but is is practically useless. See also my proposed implementation of malloc. Feel free to work on improvements!

(both are jokes; just to show that in practice you expect more than the letter of the standard)


PS. It seems that Microsoft documentation (and comments from Iinspectable below) uses "the virtual address space" with a definition different of wikipedia. I am following wikipedia's definition, which is also the one used in Operating Systems: Three Easy Pieces (see its chapter 13). Of course, the possible span of virtual address space is defined by the ISA and the processor (IIRC 48 bits on Ryzen, see figure of x86-64 wikipage about canonical form addresses). Some people call "segment" each of those [individual] "range of virtual addresses that an operating system makes available to a process" but that terminology is not universal, and segmented memory could mean something different. Other people speak of virtual memory regions for the same thing (a single contiguous interval of valid addresses in the virtual address space of a process). AMD documentation speaks of virtual memory space (256TB) in §2.1.5.1 AMD Ryzen, used to implement virtual address space (by the OS). Some other documentation speaks of virtual address range as the amount of significant bits in virtual addresses (it is limited by the hardware, e.g. the MMU). The point is that the OS is managing the virtual address space of each process and can allocate "regions" - or "segments" - that is consecutive address ranges in it. Remember that processes are artifacts managed by the OS kernel (they don't exist in the hardware; processes are an abstraction provided by the OS) and its scheduler. The virtual address space is also an artifact (part of, or property of, some process) managed by the OS. On Linux, mmap(2) is defined to "create a new mapping in the virtual address space" so is changing that VAS.

PPS working set and resident set size and thrashing and virtual memory are related concepts (but different of virtual address space). The virtual address space is an essential -but changing- property of each process (and every process has its own one). The implementation of operator new or of malloc may change the VAS (but don't change it every time) of your process (e.g. by calling VirtualAlloc on Windows, or mmap or sbrk on Linux). If you stick to Microsoft's definition, the virtual address space means something different which stays eternally immutable (but that is not very interesting, and is not a feature of the process but of the hardware, which AMD calls "virtual memory space"; see also this).


NB. I don't know Windows and never used it (and hope to never code on it in my professional life). But I have been programming since 1974 and using Unix-like systems since 1987. Today I am a Linux user, and I do know a bit about the GCC compiler internals.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • *"Notice that the virtual address space does change"* - That's wrong. [Virtual address space](https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/virtual-address-spaces) never changes throughout the lifetime of a process. That's not relevant to the question either. The OP is asking about committed memory. The commit charge does change over time. – IInspectable Apr 06 '18 at 13:05
  • It does change, if using the wikipedia's definition. But Microsoft (and you) are using a different (and incompatible) definition and terminology. – Basile Starynkevitch Apr 06 '18 at 13:06
  • Even if there were different definitions for *"virtual address space"* (there aren't; they all agree), why choose the definition that doesn't apply to Windows when answering a Windows question? But again, virtual address space has nothing to do with the question, so why expand on an unrelated topic? – IInspectable Apr 06 '18 at 15:48
  • Using the definition of wikipedia and of [chapter 13](http://pages.cs.wisc.edu/~remzi/OSTEP/vm-intro.pdf) of the cited textbook – Basile Starynkevitch Apr 06 '18 at 20:31