638
edits
(3 top-level headings) |
m (→GCHeap: fmt, sp) |
||
Line 586: | Line 586: | ||
# A large allocator for items over 2K | # A large allocator for items over 2K | ||
When you want to allocate something we figure out what size class it's in and then ask that allocator for the memory. Each fixed size allocator maintains a doubly linked list of 4K blocks that it obtains from the <code>GCHeap</code>. These 4K blocks are aligned on 4K boundaries so we can easily allocate everything on 8-byte boundaries (a necessary consequence of the 32-bit atom design-- 3 type bits and 29 pointer bits). Also we store the <code>GCAlloc::GCBlock</code> structure at the beginning of the 4K block so each allocation doesn't need a pointer to its block (just zero the lower 12 bits of any GC-allocated thing to get the <code>GCBlock</code> pointer). The <code>GCBlock</code> contains bitmaps for marking and indicating if an item has a destructor that needs to be called (a <code>GCFinalizedObject</code> base class exists defining a virtual destructor for GC items that need it). Deleted items are stored in a per-block free list which is used if there are any otherwise we get the next free item at the end. If we don't have anything free and we reach the end, we get another block from the | When you want to allocate something we figure out what size class it's in and then ask that allocator for the memory. Each fixed size allocator maintains a doubly linked list of 4K blocks that it obtains from the <code>GCHeap</code>. These 4K blocks are aligned on 4K boundaries so we can easily allocate everything on 8-byte boundaries (a necessary consequence of the 32-bit atom design-- 3 type bits and 29 pointer bits). Also we store the <code>GCAlloc::GCBlock</code> structure at the beginning of the 4K block so each allocation doesn't need a pointer to its block (just zero the lower 12 bits of any GC-allocated thing to get the <code>GCBlock</code> pointer). The <code>GCBlock</code> contains bitmaps for marking and indicating if an item has a destructor that needs to be called (a <code>GCFinalizedObject</code> base class exists defining a virtual destructor for GC items that need it). Deleted items are stored in a per-block free list which is used if there are any otherwise we get the next free item at the end. If we don't have anything free and we reach the end, we get another block from the <code>GCHeap</code>. | ||
=== GCHeap's reserve/commit strategy === | === <code>GCHeap</code>'s reserve/commit strategy === | ||
GCHeap reserves | <code>GCHeap</code> reserves 16MB of address space per heap region. The goal of reserving so much address space is so that subsequent expansions of the heap are able to obtain contiguous memory blocks. If we can keep the heap contiguous, that reduces fragmentation and the possibility of many small "Balkanized" heap regions. | ||
Reserving 16MB of space per heap region should not be a big deal in a 2GB address space... it would take a lot of Player instances running simultaneously to exhaust the 2GB address space of the browser process. By allocating contiguous blocks of address space and managing them ourselves, fragmentation of the IE heap may actually be decreased. | Reserving 16MB of space per heap region should not be a big deal in a 2GB address space... it would take a lot of Player instances running simultaneously to exhaust the 2GB address space of the browser process. By allocating contiguous blocks of address space and managing them ourselves, fragmentation of the IE heap may actually be decreased. | ||
On Windows, this uses the VirtualAlloc API to obtain memory. On Mac OS X and Unix, we use mmap. VirtualAlloc and mmap can reserve memory and/or commit memory. Reserved memory is just virtual address space. It consumes the address space of the process but isn't really allocated yet; there are no pages committed to it yet. Memory allocation really occurs when reserved pages are committed. Our strategy in GCHeap is to reserve a fairly large chunk of address space, and then commit pages from it as needed. By doing this, we're more likely to get contiguous regions in memory for our heap. | On Windows, this uses the <code>VirtualAlloc</code> API to obtain memory. On Mac OS X and Unix, we use <code>mmap</code>. <code>VirtualAlloc</code> and <code>mmap</code> can reserve memory and/or commit memory. Reserved memory is just virtual address space. It consumes the address space of the process but isn't really allocated yet; there are no pages committed to it yet. Memory allocation really occurs when reserved pages are committed. Our strategy in <code>GCHeap</code> is to reserve a fairly large chunk of address space, and then commit pages from it as needed. By doing this, we're more likely to get contiguous regions in memory for our heap. | ||
GCHeap serves up 4K blocks to the size class allocators or groups of | <code>GCHeap</code> serves up 4K blocks to the size class allocators or groups of contiguous 4K blocks for requests from the large allocator. It maintains a free list and blocks are coalesced with their neighbors when freed. If we use up the 16MB reserved chunk, we reserve another one, contiguously with the previous if possible. | ||
=== When memory mapping is not available === | === When memory mapping is not available === | ||
GCHeap can fall back on a malloc/free approach for obtaining memory if a memory mapping API like VirtualAlloc or mmap is not available. In this case, GCHeap will allocate exactly as much memory as is requested when the heap is expanded and not try to reserve additional memory pages to expand into. GCHeap won't attempt to allocate contiguous regions in this case. | <code>GCHeap</code> can fall back on a malloc/free approach for obtaining memory if a memory mapping API like <code>VirtualAlloc</code> or <code>mmap</code> is not available. In this case, <code>GCHeap</code> will allocate exactly as much memory as is requested when the heap is expanded and not try to reserve additional memory pages to expand into. <code>GCHeap</code> won't attempt to allocate contiguous regions in this case. | ||
We currently use VirtualAlloc for Windows (supported on all flavors of Windows back to 95), mmap on Mach-O and Linux. On Classic and Carbon, we do not currently use a memory mapping strategy... these implementations are calling MPAllocAligned, which can allocate 4096-byte aligned memory. We could potentially bind to the Mach-O Framework dynamically from Carbon, if the user's system is Mac OS X, and call mmap/munmap. | We currently use <code>VirtualAlloc</code> for Windows (supported on all flavors of Windows back to 95), <code>mmap</code> on Mach-O and Linux. On Classic and Carbon, we do not currently use a memory mapping strategy... these implementations are calling <code>MPAllocAligned</code>, which can allocate 4096-byte aligned memory. We could potentially bind to the Mach-O Framework dynamically from Carbon, if the user's system is Mac OS X, and call <code>mmap/munmap</code>. |
edits