@@ -110,6 +110,18 @@ func (b gcBlock) address() uintptr {
110110 return poolStart + uintptr (b )* bytesPerBlock
111111}
112112
113+ // setAlloc sets states of a series of gc blocks to list it as allocated.
114+ func (b gcBlock ) setAlloc (len uintptr ) {
115+ if len == 0 {
116+ return
117+ }
118+
119+ b .setState (blockStateHead )
120+ for i := uintptr (1 ); i < len ; i ++ {
121+ (b + gcBlock (i )).setState (blockStateTail )
122+ }
123+ }
124+
113125// findHead returns the head (first block) of an object, assuming the block
114126// points to an allocated object. It returns the same block if this block
115127// already points to the head.
@@ -177,10 +189,133 @@ func (b gcBlock) unmark() {
177189 }
178190}
179191
192+ // gcFreeSpan stores information about a span of free blocks.
193+ // It is stored in the first free block of the span.
194+ type gcFreeSpan struct {
195+ // next is a pointer to the next free span of equal size.
196+ next * gcFreeSpan
197+ }
198+
199+ // gcBlock returns the first block in the free span.
200+ func (span * gcFreeSpan ) gcBlock () gcBlock {
201+ return blockFromAddr (uintptr (unsafe .Pointer (span )))
202+ }
203+
204+ // gcFreeRootSpan stores information about available free block spans.
205+ type gcFreeRootSpan struct {
206+ gcFreeSpan
207+
208+ // len is the length of the free span (measured in blocks).
209+ len uintptr
210+
211+ // nextSize is a pointer to the root span of the next smallest span size.
212+ nextSize * gcFreeRootSpan
213+ }
214+
215+ // gcFreeSpans is a tree which is used to track spans of free blocks in the heap.
216+ // This tree is asymmetric, and can also be viewed as a linked list of linked lists.
217+ // The outer list tracks available node sizes, and is sorted in ascending size order.
218+ // The inner lists are of individual free spans of a given size, sorted in no particular order.
219+ // This ensures that the worst case lookup time for the smallest span fitting an allocation is proportional to the size of the allocation.
220+ // However, the minimum heap size for n different sized spans is ½n² + (3/2)n - 2.
221+ // Therefore the worst case lookup complexity with respect to heap size is O(√(8n+17)) ≈ O(√n).
222+ var gcFreeSpans * gcFreeRootSpan
223+
224+ // insertFreeSpan inserts a new span of free blocks into the span tree.
225+ func insertFreeSpan (block gcBlock , len uintptr ) {
226+ if len == 0 {
227+ return
228+ }
229+
230+ // Search for the appropriate root insertion point.
231+ rins := & gcFreeSpans
232+ for * rins != nil && (* rins ).len < len {
233+ rins = & (* rins ).nextSize
234+ }
235+
236+ if root := * rins ; root != nil && root .len == len {
237+ // There is already a root for this size.
238+ // Add a span to this root.
239+ span := (* gcFreeSpan )(block .pointer ())
240+ * span = gcFreeSpan {
241+ next : root .next ,
242+ }
243+ root .next = span
244+ return
245+ }
246+
247+ // Create a new root for this size.
248+ root := (* gcFreeRootSpan )(block .pointer ())
249+ * root = gcFreeRootSpan {
250+ len : len ,
251+ nextSize : * rins ,
252+ }
253+ * rins = root
254+ }
255+
256+ // rebuildSpanTree rebuilds the free span tree.
257+ // This is used after the GC sweep.
258+ func rebuildSpanTree () {
259+ // Clear the existing span tree.
260+ gcFreeSpans = nil
261+
262+ // Search for all free blocks.
263+ for block := gcBlock (0 ); block < endBlock ; block ++ {
264+ if block .state () != blockStateFree {
265+ // This block is in use.
266+ continue
267+ }
268+
269+ // This is the first block in a free span.
270+ spanStart := block
271+
272+ // Find the end of the free span.
273+ for block < endBlock && block .state () == blockStateFree {
274+ block ++
275+ }
276+
277+ // Add the span to the tree.
278+ insertFreeSpan (spanStart , uintptr (block - spanStart ))
279+ }
280+ }
281+
282+ // allocBlocks allocates a series of gc blocks.
283+ // It attempts to use the smallest possible free span.
284+ func allocBlocks (len uintptr ) (gcBlock , bool ) {
285+ // Search for a root with a span length of at least len.
286+ rins := & gcFreeSpans
287+ for * rins != nil && (* rins ).len < len {
288+ rins = & (* rins ).nextSize
289+ }
290+ if * rins == nil {
291+ // There are no sufficiently-large free spans.
292+ return 0 , false
293+ }
294+
295+ // Get a free span.
296+ root := * rins
297+ var span * gcFreeSpan
298+ if span = root .next ; span != nil {
299+ // Pop a leaf span off of the tree.
300+ root .next = span .next
301+ } else {
302+ // There is only one free span of this size.
303+ // Extract the root.
304+ * rins = root .nextSize
305+ span = & root .gcFreeSpan
306+ }
307+
308+ if root .len > len {
309+ // The span is longer than requested.
310+ // Insert the remainder of the span.
311+ insertFreeSpan (span .gcBlock ()+ gcBlock (len ), root .len - len )
312+ }
313+
314+ return span .gcBlock (), true
315+ }
316+
180317// Initialize the memory allocator.
181- // No memory may be allocated before this is called. That means the runtime and
182- // any packages the runtime depends upon may not allocate memory during package
183- // initialization.
318+ // No memory may be allocated before this is called.
184319func initHeap () {
185320 totalSize := heapEnd - heapStart
186321
@@ -208,6 +343,9 @@ func initHeap() {
208343
209344 // Set all block states to 'free'.
210345 memzero (unsafe .Pointer (heapStart ), metadataSize )
346+
347+ // Insert an initial free span.
348+ insertFreeSpan (0 , numBlocks )
211349}
212350
213351// alloc tries to find some free space on the heap, possibly doing a garbage
@@ -219,66 +357,36 @@ func alloc(size uintptr) unsafe.Pointer {
219357 }
220358
221359 neededBlocks := (size + (bytesPerBlock - 1 )) / bytesPerBlock
360+ if neededBlocks > uintptr (endBlock ) {
361+ runtimePanic ("oversized allocation" )
362+ }
222363
223- // Continue looping until a run of free blocks has been found that fits the
224- // requested size.
225- index := nextAlloc
226- numFreeBlocks := uintptr (0 )
227- heapScanCount := uint8 (0 )
228- for {
229- if index == nextAlloc {
230- if heapScanCount == 0 {
231- heapScanCount = 1
232- } else if heapScanCount == 1 {
233- // The entire heap has been searched for free memory, but none
234- // could be found. Run a garbage collection cycle to reclaim
235- // free memory and try again.
236- heapScanCount = 2
237- GC ()
238- } else {
239- // Even after garbage collection, no free memory could be found.
240- runtimePanic ("out of memory" )
241- }
364+ var gcRun bool
365+ tryAlloc:
366+ // Find a span of free blocks.
367+ block , ok := allocBlocks (neededBlocks )
368+ if ! ok {
369+ // There is no sufficiently large span of free blocks available.
370+
371+ if ! gcRun {
372+ // Run the garbage collector and try again.
373+ GC ()
374+ gcRun = true
375+ goto tryAlloc
242376 }
243377
244- // Wrap around the end of the heap.
245- if index == endBlock {
246- index = 0
247- // Reset numFreeBlocks as allocations cannot wrap.
248- numFreeBlocks = 0
249- }
378+ // There is not enough space for the allocation.
379+ runtimePanic ("out of memory" )
380+ }
250381
251- // Is the block we're looking at free?
252- if index .state () != blockStateFree {
253- // This block is in use. Try again from this point.
254- numFreeBlocks = 0
255- index ++
256- continue
257- }
258- numFreeBlocks ++
259- index ++
260-
261- // Are we finished?
262- if numFreeBlocks == neededBlocks {
263- // Found a big enough range of free blocks!
264- nextAlloc = index
265- thisAlloc := index - gcBlock (neededBlocks )
266- if gcDebug {
267- println ("found memory:" , thisAlloc .pointer (), int (size ))
268- }
382+ // Set the states of the blocks as allocated.
383+ block .setAlloc (neededBlocks )
269384
270- // Set the following blocks as being allocated.
271- thisAlloc .setState (blockStateHead )
272- for i := thisAlloc + 1 ; i != nextAlloc ; i ++ {
273- i .setState (blockStateTail )
274- }
385+ // Clear the allocation.
386+ pointer := block .pointer ()
387+ memzero (pointer , size )
275388
276- // Return a pointer to this allocation.
277- pointer := thisAlloc .pointer ()
278- memzero (pointer , size )
279- return pointer
280- }
281- }
389+ return pointer
282390}
283391
284392func free (ptr unsafe.Pointer ) {
@@ -300,6 +408,9 @@ func GC() {
300408 // the next collection cycle.
301409 sweep ()
302410
411+ // Build a tree of free memory spans.
412+ rebuildSpanTree ()
413+
303414 // Show how much has been sweeped, for debugging.
304415 if gcDebug {
305416 dumpHeap ()
0 commit comments