Memory
Mysteries of Memory Management Revealed
Windows is a demand-paged memory system
- Demand: Process aren't born with all the needed resources. They demand/request them
- When a process allocates Virtual Memory, it is just a concept until the process actually needs for something physical it can us
- Paged: The granularity of the memory to be handled. Depends on the hardware, but usually is 4KB
- There are Large pages depending on architecture. (x86: 4MB | x64 and x86 PAE: 2MB)
- There is no memory swapping in windows
- Allocations must happen in 64 kb boundaries. If you allocate just one page, 60KB are wasted
- VirtualMemory and PhysicalMemory have (almost) no connection
Address Space
- What is an Address space
Range of virtual addresses that the operating system assigns to a process. This is the area of contiguous virtual addresses available for executing instructions and storing data.
The range of virtual addresses in an address space starts at zero and can extend to the highest address permitted by the operating system architecture. Source
32bit x86 Address Space
32bit -> 2^32 -> 4 GB
- Memory is splitted in
- 2 GB Process Space
- 2 GB System Space
- Setting 3GT mode (Needs to be Large address space aware)
- 3 GB Process Space
- 1 GB System Space
64bit x86 Address Space
64bit -> 2^64 -> 17,179,869,184 GB
Processors can't handle that yet.
-
x64 today (2016) supports 48 bits
-
IA-64 today (2016) supports 50 bits
-
64 bit Windows today (2016) supports 44 bits = 16 TB
-
Memory is splitted in
- 8 TB Process Space
- 8 TB System Space
-
For 32bit processes on x64
- 4 GB Process Space
- 8 TB System Space
Every address space can be:
- Committed: In use. It is actually doing something
- Reserved: Reserved for future use. Can be committed later. Cannot be allocated again.
Every address space is divided in 3
- Private: Used by the process (ie. the heap)
- Shared: Shared memory, DLLs
- Free: Undefined
Links
#todo2/2 (Physical Memory)
Structure packing
Storage of basic C data type are not stored in arbitary locations in memory. There are alignment constraints. Except char.
- chars can start on any byte address
- 2-byte shorts must start on an even address
- 4-byte int or floats must start on an address divisible by 4
- 8-byte long or doubles must start on an address divisible by 8. Signed or unsigned makes no difference.
These, in vanilla ISAs (Intel, ARM, Risc-V), are referred to as self-aligned.
// on an x64
char *p; // 64 data bits
char c; // 8 data bits
// 24 padding bits -> "slop" not guaranteed to be zeroed
int x; // 32 data bits
On platforms with self-aligned data types, arrays of basic datatypes do not have padding between elements.
Structs
In general, a struct will be aligned to its widest member. This is the easiest way to make sure all members are self-aligned.
The address of the first member is the address of the struct (Might not be the case in C++)
Stride address. Its the first address following the data structure that has the same alignment as the structure. this becomes trailing padding
If there are nested structs, the inner data will follow the same alignment rules:
struct foo5 {
char c; // 1 byte data
// 7 bytes padding
struct foo5_inner {
char *p; // 8 bytes data
short x; // 2 bytes data
// 6 bytes padding
} inner;
};
Structure packing
One strategy to make sure your struct is packed, is to order its members by size. It can be descending or ascending order, and the last/first should be the pointer-aligned elements.
Notes:
- Linux might align 8 byte data into 4 byte alignment.
- Cache line size could be 64 bytes or 32 bytes.