@@ -49,7 +49,6 @@ const (
4949
5050var (
5151 poolStart uintptr // the first heap pointer
52- nextAlloc gcBlock // the next block that should be tried by the allocator
5352 endBlock gcBlock // the block just past the end of the available space
5453)
5554
@@ -207,6 +206,105 @@ func initHeap() {
207206
208207 // Set all block states to 'free'.
209208 memzero (unsafe .Pointer (heapStart ), metadataSize )
209+
210+ // Initialize the freeList with a single node encompassing the entire heap.
211+ freeList = endBlock
212+ freeListInsert (0 , numBlocks )
213+ }
214+
215+ var freeList gcBlock
216+
217+ type freeListNode struct {
218+ prev , next * freeListNode
219+ size uintptr
220+ }
221+
222+ // freeListHead returns a pointer to the first entry in the free list.
223+ // If the free list is empty, this returns nil.
224+ func freeListHead () * freeListNode {
225+ if freeList == endBlock {
226+ return nil
227+ }
228+
229+ return (* freeListNode )(unsafe .Pointer (freeList .pointer ()))
230+ }
231+
232+ // freeListFind searches the free list for the smallest gap that is at least the specified size (in blocks).
233+ func freeListFind (size uintptr ) gcBlock {
234+ for node := freeListHead (); node != nil ; node = node .next {
235+ if node .size >= size {
236+ return blockFromAddr (uintptr (unsafe .Pointer (node )))
237+ }
238+ }
239+
240+ // The entire free list was scanned, and no blocks were sufficiently large.
241+ return endBlock
242+ }
243+
244+ // freeListInsert adds a free region starting at block, with the specified size (in blocks).
245+ func freeListInsert (block gcBlock , size uintptr ) {
246+ // Search for an insertion point.
247+ var prev * freeListNode
248+ next := freeListHead ()
249+ for ; next != nil && next .size < size ; next = next .next {
250+ prev = next
251+ }
252+
253+ // Build the new free list entry.
254+ node := (* freeListNode )(block .pointer ())
255+ * node = freeListNode {
256+ prev : prev ,
257+ next : next ,
258+ size : size ,
259+ }
260+
261+ if prev != nil {
262+ // Update previous entry to reference this.
263+ prev .next = node
264+ } else {
265+ // Update list head.
266+ freeList = block
267+ }
268+
269+ if next != nil {
270+ // Update next entry to reference this.
271+ next .prev = node
272+ }
273+ }
274+
275+ // freeListRemove removes the gap starting at the specified block from the free list.
276+ // It returns the size of the gap that was removed.
277+ func freeListRemove (block gcBlock ) uintptr {
278+ node := (* freeListNode )(block .pointer ())
279+ prev , next := node .prev , node .next
280+
281+ if prev != nil {
282+ // Update previous node.
283+ prev .next = next
284+ } else {
285+ // Update list head.
286+ if next == nil {
287+ // List is now empty.
288+ freeList = endBlock
289+ } else {
290+ // The former next node becomes the head.
291+ freeList = blockFromAddr (uintptr (unsafe .Pointer (next )))
292+ }
293+ }
294+
295+ if next != nil {
296+ // Update next node.
297+ next .prev = prev
298+ }
299+
300+ return node .size
301+ }
302+
303+ func freeListDump () {
304+ println ("free list:" )
305+ for node := freeListHead (); node != nil ; node = node .next {
306+ println ("" , node , "size:" , node .size )
307+ }
210308}
211309
212310// alloc tries to find some free space on the heap, possibly doing a garbage
@@ -219,65 +317,50 @@ func alloc(size uintptr) unsafe.Pointer {
219317
220318 neededBlocks := (size + (bytesPerBlock - 1 )) / bytesPerBlock
221319
222- // Continue looping until a run of free blocks has been found that fits the
223- // requested size.
224- index := nextAlloc
225- numFreeBlocks := uintptr (0 )
226- heapScanCount := uint8 (0 )
227- for {
228- if index == nextAlloc {
229- if heapScanCount == 0 {
230- heapScanCount = 1
231- } else if heapScanCount == 1 {
232- // The entire heap has been searched for free memory, but none
233- // could be found. Run a garbage collection cycle to reclaim
234- // free memory and try again.
235- heapScanCount = 2
236- GC ()
237- } else {
238- // Even after garbage collection, no free memory could be found.
239- runtimePanic ("out of memory" )
320+ // Find an unused gap in the heap to fill with the allocation.
321+ var ranGC bool
322+ findMem:
323+ block := freeListFind (neededBlocks )
324+ if block == endBlock {
325+ // There are no available gaps big enough for the allocation.
326+ if ranGC {
327+ // Even after garbage collection, no free memory could be found.
328+ if gcDebug {
329+ freeListDump ()
330+ dumpHeap ()
240331 }
332+ runtimePanic ("out of memory" )
241333 }
242334
243- // Wrap around the end of the heap.
244- if index == endBlock {
245- index = 0
246- // Reset numFreeBlocks as allocations cannot wrap.
247- numFreeBlocks = 0
248- }
335+ // Run the garbage collector and try again.
336+ GC ()
337+ ranGC = true
338+ goto findMem
339+ }
249340
250- // Is the block we're looking at free?
251- if index .state () != blockStateFree {
252- // This block is in use. Try again from this point.
253- numFreeBlocks = 0
254- index ++
255- continue
341+ // Update the free list.
342+ gapSize := freeListRemove (block )
343+ if gcDebug {
344+ println ("using gap" , block , "of size" , gapSize )
345+ }
346+ if gapSize > neededBlocks {
347+ // Create a new free list entry with the leftover space.
348+ if gcDebug {
349+ println ("creating shrunk gap" , block + gcBlock (neededBlocks ), "of size" , gapSize - neededBlocks )
256350 }
257- numFreeBlocks ++
258- index ++
259-
260- // Are we finished?
261- if numFreeBlocks == neededBlocks {
262- // Found a big enough range of free blocks!
263- nextAlloc = index
264- thisAlloc := index - gcBlock (neededBlocks )
265- if gcDebug {
266- println ("found memory:" , thisAlloc .pointer (), int (size ))
267- }
268-
269- // Set the following blocks as being allocated.
270- thisAlloc .setState (blockStateHead )
271- for i := thisAlloc + 1 ; i != nextAlloc ; i ++ {
272- i .setState (blockStateTail )
273- }
351+ freeListInsert (block + gcBlock (neededBlocks ), gapSize - neededBlocks )
352+ }
274353
275- // Return a pointer to this allocation.
276- pointer := thisAlloc .pointer ()
277- memzero (pointer , size )
278- return pointer
279- }
354+ // Update block metadata.
355+ block .setState (blockStateHead )
356+ for offset := uintptr (1 ); offset < neededBlocks ; offset ++ {
357+ (block + gcBlock (offset )).setState (blockStateTail )
280358 }
359+
360+ // Return a pointer to this allocation.
361+ pointer := block .pointer ()
362+ memzero (pointer , neededBlocks * bytesPerBlock )
363+ return pointer
281364}
282365
283366func free (ptr unsafe.Pointer ) {
@@ -348,27 +431,56 @@ func markRoot(addr, root uintptr) {
348431
349432// Sweep goes through all memory and frees unmarked memory.
350433func sweep () {
351- freeCurrentObject := false
434+ var freeStart gcBlock
435+ freeStarted := false
436+ freeBlockStart := true
352437 for block := gcBlock (0 ); block < endBlock ; block ++ {
353- switch block .state () {
438+ state := block .state ()
439+ switch state {
440+ case blockStateFree :
441+ if freeBlockStart {
442+ freeListRemove (block )
443+ if ! freeStarted {
444+ freeStart = block
445+ freeStarted = true
446+ }
447+ freeBlockStart = false
448+ }
354449 case blockStateHead :
355450 // Unmarked head. Free it, including all tail blocks following it.
356451 block .markFree ()
357- freeCurrentObject = true
452+ if ! freeStarted {
453+ freeStart = block
454+ freeStarted = true
455+ }
456+
457+ freeBlockStart = true
358458 case blockStateTail :
359- if freeCurrentObject {
459+ if freeStarted {
360460 // This is a tail object following an unmarked head.
361461 // Free it now.
362462 block .markFree ()
363463 }
464+
465+ freeBlockStart = true
364466 case blockStateMark :
365467 // This is a marked object. The next tail blocks must not be freed,
366468 // but the mark bit must be removed so the next GC cycle will
367469 // collect this object if it is unreferenced then.
368470 block .unmark ()
369- freeCurrentObject = false
471+
472+ if freeStarted {
473+ freeListInsert (freeStart , uintptr (block - freeStart ))
474+ freeStarted = false
475+ }
476+
477+ freeBlockStart = true
370478 }
371479 }
480+
481+ if freeStarted {
482+ freeListInsert (freeStart , uintptr (endBlock - freeStart ))
483+ }
372484}
373485
374486// looksLikePointer returns whether this could be a pointer. Currently, it
0 commit comments