preface

Make and new are used when initializing a type in GO, which can be confusing for beginners because they have similar functionality; This article will introduce its functions and differences from shallow to deep

conclusion

To cut a long story short, here’s the conclusion:

methods role Function object The return value
new Allocate memory Value type and user-defined type Initialize to zero,Returns a pointer
make Allocate memory Built-in reference types (Map, Slice, channel) Initialize to zero,Returns the reference type itself

The above is the difference between make and new. If someone asks, can you give more details?

Oh dear, this is embarrassing

The body of the

Short story short, what do make and New do

new

New allocates memory, initializes zero, and returns a pointer to zero

During compilation, using new generally results in two situations:

  1. If the object claims zero space, the zerobase variable is returned representing a null pointer to such objects as Slice, Map, channel, and some structures.

    // path: src/runtime/malloc.go
    
    // base address for all 0-byte allocations
    var zerobase uintptr
    Copy the code
  2. In other cases, runtime. newObject is used:

    // path: src/runtime/malloc.go
    func newobject(typ *_type) unsafe.Pointer {
    	return mallocgc(typ.size, typ, true)}Copy the code

Runtime. newoject calls Runtime. mallocGC to create an area of memory, and returns the address of that area

Here’s a simple example to prove it:

func main(a) {
   a := new(map[int]int)
    fmt.Println(*a)  // nil, see case 1

   b := new(int)
   fmt.Println(*b)    // 0, refer to case 2
}
Copy the code

After all, what does new do

// a := new(int); Other types use this analogy
var a int
return &a
Copy the code

make

Make is used to initialize specific types of map, slice, and channel

During compilation, using make to initialize different types calls different underlying functions:

  1. Initialize themap, the callruntime.makemap
  2. Initialize theslice, the callruntime.makeslice
  3. Initialize thechannel, the callruntime.makechan

Let’s take a look at the source code for these functions and see how they differ from New. If you know the source code for these types, it’s easy to understand the code below. If you don’t know this section, you can just follow the notes and understand the process

runtime.makemap:

// path: src/runtime/map.go
func makemap(t *maptype, hint int, h *hmap) *hmap{...// Initialize Hmap
   if h == nil {
      h = new(hmap)
   }
    
   // Generate hash seeds
   h.hash0 = fastrand()
    
   // Count buckets
   B := uint8(0)
   for overLoadFactor(hint, B) {
      B++
   }
   h.B = B
   ifh.B ! =0 {
      var nextOverflow *bmap
       
      / / create a bucket
      h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)... }return h
}
Copy the code

Part of the code is omitted for easy viewing. We can see that there are many steps here, and h = new(hmap) is just one of them

runtime.makeslice:

// path: src/runtime/slice.go
func makeslice(et *_type, len.cap int) unsafe.Pointer {
    // Calculate occupied space and overflow
	mem, overflow := math.MulUintptr(et.size, uintptr(cap))
    
   // Some boundary condition processing
	if overflow || mem > maxAlloc || len < 0 || len > cap {
		mem, overflow := math.MulUintptr(et.size, uintptr(len))
		if overflow || mem > maxAlloc || len < 0 {
           // Panic: len is out of range
			panicmakeslicelen()
		}
        // Panic: Cap is out of scope
		panicmakeslicecap()
	}

	return mallocgc(mem, et, true)}Copy the code

This is similar to the runtime. newObject in new, but with some exception handling

runtime.makechan:

// path: src/runtime/chan.go
func makechan(t *chantype, size int) *hchan{...var c *hchan
    
   // Implement different memory allocation strategies for channels in different cases
   switch {
   case mem == 0:
      // There is no buffer, only one memory is allocated to hchan
      c = (*hchan)(mallocgc(hchanSize, nil.true))
      c.buf = c.raceaddr()
   case elem.ptrdata == 0:
      // channel does not contain Pointers and allocates contiguous memory to hchan and buffer
      c = (*hchan)(mallocgc(hchanSize+mem, nil.true))
      c.buf = add(unsafe.Pointer(c), hchanSize)
   default:
      // Allocate memory for hchan and buffer separately
      c = new(hchan)
      c.buf = mallocgc(mem, elem, true)}// Initialize the internal field of hchan
   c.elemsize = uint16(elem.size)
   c.elemtype = elem
   c.dataqsiz = uint(size)
   ...
}
Copy the code

Part of the code is omitted here, including some exception handling

In short, make does more than new, which just opens up memory, and make opens up memory for more complex data structures and initializes some fields

Note: Makechan (makeslice); makechan (makeslice); makechan (makeslice); makechan (makeslice); makechan (makeslice); makechan (makeslice); makechan (makeslice); makechan Not all source code

To learn more about make, check out the source code for Map, Slice, and Channel

The soul of torture

Here are two questions to help you better understand:

  1. Can new be used to initialize map, slice and channel?

    First of all, let’s recall the functions of new. Simple understanding is as follows:

    var i int
    return &int
    Copy the code

    If we want to initialize the above types, how do we do it:

    	m := *new(map[int]int)  // Take the value first, because new returns a pointer
    	s := *new([]int)
    	ch := *new(chan int)
    Copy the code

    In the above code, using new to initialize these types does not panic

    In the case of the above code, we can discuss it in categories:

    1. Map, new does not perform initial operations such as creating buckets on the map, so when we add key-value pairs and return to panic, the query and deletion of non-existent keys will not cause panic, because both the query and deletion need to search for buckets and keys. If there is no corresponding bucket and key, the query returns zero value. No operation is performed on the deletion
    2. Channel, there is no memory space for the buffer of the channel and more internal initial call operations. The created channel is always nil, and sending or receiving data from it causes panic
    3. **slice, which uses new to create nil slices, works fine ** because mallocGC is called during slice append to acquire a chunk of memory, return a new slice, and then assign to nil slices
  2. Can I use make to initialize other types, such as int, string?

    No, because make does not provide underlying methods for other types

The last

Above, due to the limited capacity, negligence and inadequacy is difficult to avoid, welcome readers to correct, in order to timely modification.

If this article is helpful to you, please like 👍 and forward. Thank you for your support!

The resources

  • The make and new

    Draveness. Me/golang/docs…

  • Map source code analysis

    Mp.weixin.qq.com/s/2CDpE5wfo…

  • Slice source code analysis

    Mp.weixin.qq.com/s/MTZ0C9zYs…

  • Channel source code analysis

    Mp.weixin.qq.com/s/MTZ0C9zYs…