Original link: Which of the new and make do you use to allocate memory in Go?
Preface
Hello, everyone, I’m the pigeonasong
. Because5.1
went to find a girlfriend, I have never had time to write an article, thinking about it, I will hurry up and study, but I am still immersed in the sweet life of 5.1. Sure enough, the woman affected my speed of drawing the knife, but I like it very much, slightly.
Go
sprinkling dog food. Let’s start to get into the topic. Today, let’s discuss how to usemake
andnew
in the 060a49b6197125 language. How are they different?
Allocate memory new
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
The translation is: new
is a built-in function that allocates memory. The first parameter is a type, not a value. The returned value is a pointer to the newly allocated zero value of the type.
We usually need to allocate memory space when we use pointers. Direct use of pointers with unallocated memory space will cause the program to crash, such as this:
var a *int64
*a = 10
We declare a pointer variable and use it directly, and the program will trigger panic
. Because now the pointer variable a
does not have a block address in the memory belonging to it, the pointer variable cannot be used directly, so the function of new
Now, new
, there is no problem:
var a *int64 = new(int64)
*a = 10
The above example, we are against a common type int64
be new
treatment, if it is a composite type, use new
look like it? Let's look at an example:
func main(){
// 数组
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}
// 切片
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)
}
It can be seen from the running results that after we use the new
function to allocate memory, only the array can be used directly after initialization. slice
, map
, chan
can not be used after initialization, which will trigger panic
, this is because of the basic data structure of slice
, map
, chan
It is a struct
, which means that the member variables in it have not been initialized yet, so their initialization should be done with make
. 060a49b6197265 will initialize their internal structure. We will make
Let's go back to the struct
's look at an example first:
type test struct {
A *int64
}
func main(){
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)
}
From the results of the operation new()
function to initialize the structure, we only initialized the struct
type, and its member variables are not initialized. Therefore, it is not recommended to use the new
function for the initialization structure. The use of key-value pairs for initialization is more effective. good.
In fact, the new
function is relatively rare in the daily engineering code, because it can be replaced, and it is more convenient and convenient to T{}
Initialize the built-in structure make
As we mentioned in the previous section, the make
function specifically supports the memory creation of three data types: slice
, map
, and channel
// 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
Probably translate the top paragraph: make
built-in function allocates and initializes an object of type slice
, map
or chan
Like the new
function, the first parameter is the type, not the value. Unlike new
, make
the same as the type of its parameter, not a pointer to it. The result depends on the type of incoming.
Using make
initialize the incoming type is also different, which 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
Different types of initialization can use different postures. The main difference is the specification of length (len) and capacity (cap). Some types have no capacity, so naturally they cannot be specified. If you determine the length and capacity, you can save memory space.
Write a simple example:
func main(){
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
}
There is a point to note here, that is slice
initialized, it will give a value of zero by default. Pay attention to this problem in development. I made this mistake, which resulted in inconsistent data.
Summary of the difference between new
and make
new
function is mainly to apply for a piece of memory space for the type, and return a pointer to the execution memorymake
function can allocate and initialize the memory space and structure required by the type, and return the composite type itself.make
function only supportschannel
,map
, andslice
. Other types cannot usemake
.new
function is rarely used in daily development and can be replaced.make
function initializationslice
will initialize the zero value. Pay attention to this problem in daily development.
make
function bottom layer implementation
I was more curious make
underlying implementation is what, so execute assembly instructions: go tool compile -N -l -S file.go
, we can see make
function initializes slice
, map
, chan
each call is runtime.makeslice
, runtime.makemap_small
, runtime.makechan
these three methods, because of the different types of underlying data structures Different, so the initialization method is also different. Let's just look at slice
. Let everyone see the others. In fact, they are all similar.
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)
}
The function of this function is actually relatively simple:
- Check whether the memory space occupied by the slice overflows.
- Call
mallocgc
to apply for a piece of contiguous memory on the heap.
Checking the memory space here is calculated based on the slice capacity. The current memory space size is obtained according to the product of the current slice element size and the slice capacity. There are four conditions for checking overflow:
- The size of the memory space has overflowed
- The requested memory space is greater than the maximum allocatable memory
- The incoming
len
smaller than0
, andcap
is only smaller thanlen
mallocgc
implementation of the 060a49b619762c function is more complicated. I haven't understood it yet, but it is not very important. If you are interested, you can learn by yourself.
new
function bottom layer implementation
new
bottom layer of the 060a49b619765f function is mainly to call 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)
}
The internal implementation is to directly call the mallocgc
function to apply for memory on the heap, and the return value is a pointer type.
to sum up
In today's article, we mainly introduce the make
and new
and their differences. In fact, they are all used to allocate memory, but the make
function is slice
, map
, chan
these three types of services. Pay attention to the zero value problem when make
initialize slice
in daily development p0
accident.
, this article is over now. The triple quality (sharing, liking, and watching) is the motivation for the author to continue to create more high-quality content! My name is asong
. See you in the next issue.
created a Golang learning exchange group, welcome you all to join the group, let’s learn and communicate together. Ways to join the group: follow the official account to obtain. For more learning materials, please go to the public account to receive.
Recommend previous articles:
- Go see the source code and know the unsafe package
- source code analysis panic and recover, if you don’t understand, call me!
- Large-scale face fight scene triggered by empty structure
- Leaf—Segment distributed ID generation system (Golang implementation version)
- Interviewer: What is the result of comparing two nil?
- Interviewer: Can you use Go to write a piece of code to judge the storage mode of the current system?
- How to smoothly switch online Elasticsearch index
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。