[Go memory allocation]

The running of programs requires memory, such as variable creation, function call, data calculation and so on. So when you need memory, you need to apply for memory and allocate memory. In languages such as C/C + +, the memory is managed by the developers themselves and needs to be actively applied and released, while in Go language, it is managed by the language itself. Developers don't need to care too much. They just need to declare variables, and Go language will automatically allocate the corresponding memory according to the type of variables.

The virtual memory space managed by Go language program is divided into two parts: heap memory and stack memory. Stack memory is mainly managed by Go language. Developers cannot interfere too much. Heap memory is the stage for our developers, because most of the data of a program is allocated to heap memory, and most of the memory of a program is also occupied by heap memory.

Tip: we often say that the memory garbage collection of Go language is for heap memory garbage collection.

The declaration and initialization of variables involve memory allocation. For example, the var keyword will be used to declare variables. If variables are to be initialized, the = assignment operator will be used. In addition, the built-in functions make and new can be used. The functions of these two functions are very similar, but there may be some confusion. Next, let's based on memory allocation, Introduce the built-in functions make and new, and talk about their differences and usage scenarios.

variable

A data type, after declaration initialization, will be assigned to a variable, which stores the data required for program operation.

Declaration of variables

// Let's review the declaration of variables
var s string 
// In this example, we just declared a variable s of type string, and did not initialize it, so its value is the zero value of string, that is "" (empty string)

var sp *string
// We declared a pointer type variable and did not initialize it, so its value is the zero value of * string, that is, nil

Assignment of variables

The value of a variable can be modified by the = assignment operator. If a variable is assigned a value when it is declared, this operation is called variable initialization. There are three ways to initialize a variable

  1. Directly initialize when declaring, var s string = "running snail"
  2. After declaration, during initialization, s = "running snail" / / (assuming variable s has been declared)
  3. Use a short statement, such as s: = "running snail"

Tip: variable initialization is also a kind of assignment, but it occurs when the variable is declared, and the timing is the most advanced. That is, when you get this variable, it has been assigned

So, can pointer type variables be assigned directly?

func main() {
    var sp *string
    *sp = "Running snail "
    fmt.Println(*sp)
}

// The above code will report an error, and the error information is as follows:
painc: runtime error: invalid memory address or nil pointer dereference


// If the pointer type variable does not allocate memory, the default value is nil. It has no memory to point to, so it is useless. Forced use will get nil pointer error. For value types, even if only a variable is declared and not initialized, the variable will have allocated memory.

func main() {
    var s string
    fmt.Println(&s)
}
// We can get the memory address of variable s, which is done by Go language and can be used directly.

var wg sync. The variable WG declared by waitgroup can be used without initializing it, because sync Waitgroup is a struct structure and a value type. Go language automatically allocates memory, so it can be used directly without nil exceptions.

Summary: if you want to assign a value to a variable, the variable must have the corresponding allocated memory, so that you can operate on this memory and complete the assignment operation. For pointer variables, if no memory is allocated, the value operation will also report nil exception because there is no memory available for operation. Therefore, a variable must be declared and memory allocated before it can be assigned, and then it can be initialized at the time of declaration. When a pointer type is declared, the Go language does not automatically allocate memory, so it cannot be assigned, which is different from the value type.

Tip: map and chan are the same. They are essentially pointer types.

new function

We know that the declared pointer variable does not allocate memory, so how to allocate a block to it? That is through the built-in new function.

func main() {
    var sp *string
    sp = new(string) // Key points, a * string is generated through the built-in new function and assigned to the variable sp.
    *sp = "Running snail "
    fmt.Println(*sp)
}

// What is the function of built-in new? Let's analyze the source code
func new(Type) *Type

The new function applies for a piece of memory according to the incoming type, and then returns a pointer to the memory. The data pointed to by the pointer is the zero value of the type. For example, if the incoming type is string, the returned string pointer is string pointer, and the data pointed to by this string pointer is empty string.

spl = new(string)
fmt.Println(*spl) // Print an empty string, that is, the zero value of string.

After allocating memory through the new function and returning a pointer to the memory, you can assign and value this memory through the pointer.

Variable initialization

When some types of variables are declared, the zero value of these variables cannot meet our needs. At this time, we need to assign values (modify the value of variables) while declaring variables. This process is called variable initialization.

Not only the basic type can be initialized by literal, but also the composite type, such as structure.

type person struct{
    name string
    age int
}

func main() {
    // Literal initialization
    p := person{name:"zhangsan", age:18}
}

Pointer variable initialization

We know that the new function can apply for memory and return a pointer to the memory, but the default value of the data in this memory is the zero value of this type, which does not meet the needs in some cases. What if we want to get a pointer of type * person and initialize it, but the new function has only one type parameter and no parameter with initialization value? You can customize a function to initialize pointer variables.

func NewPerson() *person {
    p := new(person)
    p.name = "zhangsan"
    p.age = 18
    return p
}

p := NewPerson()
fmt.Println("name:", p.name, "age:", p.age)

// This is the factory function. The NewPerson function is the factory function. In addition to using the new function to create a pointer, it also performs assignment, that is, initialization. A layer of packaging is made through the NewPerson function to complete the memory allocation (new function) and initialization (assignment).

make function

Next, let's talk about the make function. As we know, when you use the make function to create a map, you actually call the makehmap function.

func makemap(t *maptype, hint int ,h *hmap) *hmap {}

// The makemap function returns the * hmap type, and hmap is a structure. The source code is as follows

type hmap struct {
    count 		int 
    flags 		uint8
    B 			uint8
    noverflow	uint16
    hash0		uint16
    buckets     unsafe.Pointer
    oldbuckets  unsafe.Pointer
    nevacuate   uintptr
    extra  		*mapextra
}


m := make(map[string]int, 10)
// The make function is very similar to our custom NewPerson function. In fact, the make function is a factory function of map type. It can create different types of maps according to the type of K-V key value pair passed, and initialize the size of the map.

You can see that the map keyword we usually use is very complex, including the size of the map, count, buckets, and so on. To use hmap in this way, you can not simply return a * hmap through the new function, but also initialize it. This is what the make function does.

Tip: the make function is not only a map type factory function, but also a chan and slice factory function. It can be used for slice, chan and map initialization at the same time.

Summary: the differences between new and make functions are as follows:

1. new The function is only used to allocate memory and clear the memory, that is, it returns a pointer to the corresponding type zero value. new Function is generally used to display the return pointer, which is not very commonly used.**new The pointer of the object is returned. Changes to the object where the pointer is located will affect the value of the original object pointed to by the pointer**. 
2. make Function only for slice,chan and map The three built-in types are created and initialized because their structures are complex, such as slice The type of internal element to initialize in advance slice In order to make better use of them.**make Object returned**
 *   Modifications to value type objects do not affect the value of the original object
 *   Changes to the reference type affect the value of the original object

Keywords: Go Back-end

Added by barney0o0 on Mon, 20 Dec 2021 17:08:59 +0200