@@ -184,6 +184,50 @@ func (b gcBlock) unmark() {
184184// any packages the runtime depends upon may not allocate memory during package
185185// initialization.
186186func initHeap () {
187+ calculateHeapAddresses ()
188+
189+ // Set all block states to 'free'.
190+ metadataSize := heapEnd - uintptr (metadataStart )
191+ memzero (unsafe .Pointer (metadataStart ), metadataSize )
192+ }
193+
194+ // setHeapEnd is called to expand the heap. The heap can only grow, not shrink.
195+ // Also, the heap should grow substantially each time otherwise growing the heap
196+ // will be expensive.
197+ func setHeapEnd (newHeapEnd uintptr ) {
198+ if gcAsserts && newHeapEnd <= heapEnd {
199+ panic ("gc: setHeapEnd didn't grow the heap" )
200+ }
201+
202+ // Save some old variables we need later.
203+ oldMetadataStart := metadataStart
204+ oldMetadataSize := heapEnd - uintptr (metadataStart )
205+
206+ // Increase the heap. After setting the new heapEnd, calculateHeapAddresses
207+ // will update metadataStart and the memcpy will copy the metadata to the
208+ // new location.
209+ // The new metadata will be bigger than the old metadata, but a simple
210+ // memcpy is fine as it only copies the old metadata and the new memory will
211+ // have been zero initialized.
212+ heapEnd = newHeapEnd
213+ calculateHeapAddresses ()
214+ memcpy (metadataStart , oldMetadataStart , oldMetadataSize )
215+
216+ // Note: the memcpy above assumes the heap grows enough so that the new
217+ // metadata does not overlap the old metadata. If that isn't true, memmove
218+ // should be used to avoid corruption.
219+ // This assert checks whether that's true.
220+ if gcAsserts && uintptr (metadataStart ) < uintptr (oldMetadataStart )+ oldMetadataSize {
221+ panic ("gc: heap did not grow enough at once" )
222+ }
223+ }
224+
225+ // calculateHeapAddresses initializes variables such as metadataStart and
226+ // numBlock based on heapStart and heapEnd.
227+ //
228+ // This function can be called again when the heap size increases. The caller is
229+ // responsible for copying the metadata to the new location.
230+ func calculateHeapAddresses () {
187231 totalSize := heapEnd - heapStart
188232
189233 // Allocate some memory to keep 2 bits of information about every block.
@@ -206,9 +250,6 @@ func initHeap() {
206250 // sanity check
207251 runtimePanic ("gc: metadata array is too small" )
208252 }
209-
210- // Set all block states to 'free'.
211- memzero (metadataStart , metadataSize )
212253}
213254
214255// alloc tries to find some free space on the heap, possibly doing a garbage
@@ -238,7 +279,16 @@ func alloc(size uintptr) unsafe.Pointer {
238279 GC ()
239280 } else {
240281 // Even after garbage collection, no free memory could be found.
241- runtimePanic ("out of memory" )
282+ // Try to increase heap size.
283+ if growHeap () {
284+ // Success, the heap was increased in size. Try again with a
285+ // larger heap.
286+ } else {
287+ // Unfortunately the heap could not be increased. This
288+ // happens on baremetal systems for example (where all
289+ // available RAM has already been dedicated to the heap).
290+ runtimePanic ("out of memory" )
291+ }
242292 }
243293 }
244294
0 commit comments