preface

Hello, everyone. I’m Asong the pigeon who has been dragging on longer. Because 5.1 to find a girlfriend, so there has been no time to write an article, thinking of coming back to study, helpless, still immersed in 5.1 sweet life, procrastinate, until now. Sure enough, women affect my speed, but I like it. A little bit.

Ok, so let’s get down to business. Today we’re going to talk about how make and new are used in the Go language. How are they different?

Allocate memorynew

Official document definition:

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type
Copy the code

New is a built-in function that allocates memory. The first argument is a type, not a value, and the value returned is a pointer to the newly allocated zero value of that type. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

var a *int64
*a = 10
Copy the code

We declare a pointer variable and use it directly, which will trigger panic using the program. Since there is no block address in the memory of pointer variable A, we cannot use the pointer variable directly. Therefore, the function of new appears, and there is no problem with allocating memory through new:

var a *int64 = new(int64)
	*a = 10
Copy the code

In the above example, we used new for the plain int64 type. What if we used new for a compound type? Take a look at an example:

func main(a){
	/ / array
	array := new([5]int64)
	fmt.Printf("array: %p %#v \n", &array, array)// array: 0xc0000ae018 &[5]int64{0, 0, 0, 0, 0}
	(*array)[0] = 1
	fmt.Printf("array: %p %#v \n", &array, array)// array: 0xc0000ae018 &[5]int64{1, 0, 0, 0, 0}
	
	/ / section
	slice := new([]int64)
	fmt.Printf("slice: %p %#v \n", &slice, slice) // slice: 0xc0000ae028 &[]int64(nil)
	(*slice)[0] = 1
	fmt.Printf("slice: %p %#v \n", &slice, slice) // panic: runtime error: index out of range [0] with length 0

	// map
	map1 := new(map[string]string)
	fmt.Printf("map1: %p %#v \n", &map1, map1) // map1: 0xc00000e038 &map[string]string(nil)
	(*map1)["key"] = "value"
	fmt.Printf("map1: %p %#v \n", &map1, map1) // panic: assignment to entry in nil map

	// channel
	channel := new(chan string)
	fmt.Printf("channel: %p %#v \n", &channel, channel) // channel: 0xc0000ae028 (*chan string)(0xc0000ae030) 
	channel <- "123" // Invalid operation: channel <- "123" (send to non-chan type *chan string) 
}
Copy the code

As can be seen from the running results, only the array can be used directly after initialization, but the slice, map and chan data structures cannot be used after initialization, triggering panic. This is because the basic data structure of slice, map and chan is a struct. That is, the member variables inside them are still uninitialized, so they are initialized using make, which initializes their internal structure, as we’ll see in the next section. Back to struct initialization, let’s look at an example:

type test struct {
	A *int64
}

func main(a){
	t := new(test)
	*t.A = 10  // panic: runtime error: invalid memory address or nil pointer dereference
             // [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10a89fd]
	fmt.Println(t.A)
}
Copy the code

Struct member variables are not initialized. Therefore, it is not recommended to use the new function to initialize the structure. It is better to use the key pair to initialize the structure.

In fact, the new function is relatively rare in everyday engineering code, because it can be replaced, using T{} mode is more convenient.

Initialize the built-in structuremake

In the previous section, we explained that the make function supports slice, map, and channel memory creation.

// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
//	Slice: The size specifies the length. The capacity of the slice is
//	equal to its length. A second integer argument may be provided to
//	specify a different capacity; it must be no smaller than the
//	length. For example, make([]int, 0, 10) allocates an underlying array
//	of size 10 and returns a slice of length 0 and capacity 10 that is
//	backed by this underlying array.
//	Map: An empty map is allocated with enough space to hold the
//	specified number of elements. The size may be omitted, in which case
//	a small starting size is allocated.
//	Channel: The channel's buffer is initialized with the specified
//	buffer capacity. If zero, or the size is omitted, the channel is
//	unbuffered.
func make(t Type, size ... IntegerType) Type
Copy the code

The make built-in function allocates and initializes a slice, map, or chan object. Like the new function, the first argument is a type, not a value. Unlike new, make returns the same type as its argument, not a pointer to it. The result depends on the type passed in.

The types passed in to make initialization are also different, and can be distinguished as follows:

Func             Type T     res
make(T, n)       slice      slice of type T with length n and capacity n
make(T, n, m)    slice      slice of type T with length n and capacity m

make(T)          map        map of type T
make(T, n)       map        map of type T with initial space for approximately n elements

make(T)          channel    unbuffered channel of type T
make(T, n)       channel    buffered channel of type T, buffer size n
Copy the code

Different types of initializations can use different poses. The main difference is the length (len) and capacity (cap). Some types have no capacity and therefore cannot be specified. If you determine the size of the length and capacity, you can save memory space very well.

Here’s a simple example:

func main(a){
	slice := make([]int64.3.5)
	fmt.Println(slice) / / [0 0 0]
	map1 := make(map[int64]bool.5)
	fmt.Println(map1) // map[]
	channel := make(chan int.1)
	fmt.Println(channel) // 0xc000066070
}
Copy the code

One caveat here is that slice is initialized with a default value of zero. Be aware of this in development. I have made this mistake, resulting in inconsistent data.

newandmakeThe difference between summary

  • newThe function allocates a memory space for the type and returns a pointer to the execution memory
  • makeThe function allocates and initializes the memory space and structure required by the type, returning the compound type itself.
  • makeFunction only supportedchannel,map,sliceThere are three types. The other types cannot be usedmake.
  • newFunctions are used less frequently in daily development and can be replaced.
  • makeFunction initializationsliceInitializes zero. Be aware of this in your daily development.

makeFunction low-level implementation

I’m still curious about the low-level implementation of Make, so execute the assembly instruction: Go tool compile -n -l -s file.go, Makeslice (makeslice, runtime.makemap_small); makechan (makeslice, runtime.makechan); makechan (makeslice, runtime.makemap_small); makechan (makeslice, runtime.makechan); makechan (makeslice, runtime.makechan); So the initialization is different, so let’s just look at the internal implementation of Slice and leave the rest to the public, which is pretty much the same.

func makeslice(et *_type, len.cap int) unsafe.Pointer {
	mem, overflow := math.MulUintptr(et.size, uintptr(cap))
	if overflow || mem > maxAlloc || len < 0 || len > cap {
		// NOTE: Produce a 'len out of range' error instead of a
		// 'cap out of range' error when someone does make([]T, bignumber).
		// 'cap out of range' is true too, but since the cap is only being
		// supplied implicitly, saying len is clearer.
		// See golang.org/issue/4085.
		mem, overflow := math.MulUintptr(et.size, uintptr(len))
		if overflow || mem > maxAlloc || len < 0 {
			panicmakeslicelen()
		}
		panicmakeslicecap()
	}

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

This function is also relatively simple:

  • Check whether the memory space occupied by slices is used up.
  • callmallocgcApply for a contiguous block of memory on the heap.

The calculation here is based on the slice capacity. The size of the current memory space can be obtained by multiplying the size of the current slice element and the slice capacity. There are four conditions for checking overflow:

  • The memory space is out of size
  • The requested memory space is greater than the maximum available memory. Procedure
  • The incominglenLess than0.capThe size is only less thanlen

The mallocGC function is a bit complicated to implement, and I haven’t understood it yet, but it’s not very important, so you can learn by yourself if you are interested.

newFunction low-level implementation

The underlying new function calls runtime. newObject:

// implementation of new builtin
// compiler (both frontend and SSA backend) knows the signature
// of this function
func newobject(typ *_type) unsafe.Pointer {
	return mallocgc(typ.size, typ, true)}Copy the code

The internal implementation is to call mallocGC directly to the heap and return a pointer.

conclusion

In this article we will focus on the use of make and new, and the differences between them. They are both used to allocate memory, but the make function serves the three types of slice, map, and chan. Note the zero value problem when initializing slice with make in daily development, otherwise another P0 failure will occur.

Well, that’s the end of this article, the quality three (share, like, look) are the author to continue to create more quality content motivation! I am aasongAnd we’ll see you next time.

We have created a Golang learning and communication group. Welcome to join the group and we will learn and communicate together. Way to join the group: pay attention to the public account. For more learning materials, please go to the official number.

Recommended previous articles:

  • Unsafe package
  • Source analysis panic and recover, do not understand you hit me!
  • The scene of large face blows caused by empty structures
  • Leaf-segment Distributed ID Generation System (Golang implementation version)
  • Interviewer: What is the result of two nil comparisons?
  • Interviewer: Can you use Go to write some code to determine how the current system is stored?
  • How to smoothly toggle online Elasticsearch index