Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Where are the Go language variables assigned?

2025-01-20 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Next, please follow the editor to study!

problem

Type User struct {ID int64 Name string Avatar string} func GetUserInfo () * User {return & User {ID: 13746731, Name: "EDDYCJY", Avatar: "https://avatars0.githubusercontent.com/u/13746731"}} func main () {_ = GetUserInfo ()}

The beginning is a question mark, learning with questions. Please ask the & User {...} returned by main after calling GetUserInfo. Is this variable assigned to the stack or to the heap?

What is a heap / stack

I'm not going to cover the stack in detail here, just a brief introduction to the basics required for this article. As follows:

Heap: generally speaking, it is manually managed, manually applied, allocated, and released. Generally, the amount of memory involved is not fixed, and larger objects are generally stored. In addition, its allocation is relatively slow and involves a relatively large number of instruction actions.

Stack (Stack): managed by the compiler, automatically applied, allocated, and released. Generally not too large, our common function parameters (the number of allowed storage varies from platform to platform), local variables, and so on will be stored on the stack.

Today we introduce the Go language, its stack allocation is analyzed by Compiler and managed by GC, and its analysis and selection action is the focus of today's discussion.

What is escape analysis?

In compiler optimization theory, escape analysis is a method to determine the dynamic range of the pointer, which is simply to analyze where the pointer can be accessed in the program.

Generally speaking, escape analysis is to determine whether a variable should be placed on the heap or on the stack. The rules are as follows:

Whether it is referenced elsewhere (not locally). Whenever it is possible to be referenced, it must be assigned to the heap. Otherwise, it is assigned to the stack.

Even if it is not externally referenced, the object is too large to be stored on the stack. It is still possible to assign to the heap.

You can understand that escape analysis is a behavior used by the compiler to determine whether variables are assigned to the heap or the stack.

At what stage does escape be established?

Establish an escape at compile time, not at run time.

Why do you need to escape?

We can think about this question the other way around, what happens if the variables are assigned to the heap? For example:

The pressure of garbage collection (GC) is increasing.

The system overhead of requesting, allocating, and reclaiming memory increases (relative to the stack).

Dynamic allocation produces a certain amount of memory fragmentation.

To put it simply, there is a "price" for frequently requesting and allocating heap memory. It will affect the efficiency of the application, and indirectly affect the overall system.

Therefore, "distribution according to demand" to maximize the flexible use of resources is the correct way of governance. That's why escape analysis is needed, don't you think?

How to determine whether to escape or not?

-m will print out the optimization strategy of escape analysis. in fact, a total of 4-m can be used at most, but there is a large amount of information, generally using 1.

-l disables function inlining. Disabling inline here can better observe the escape and reduce interference.

$go build-gcflags'- m-l' main.go

Second, use the decompilation command to see

$go tool compile-S main.go

Note: you can view all the identity parameters allowed to be passed to the compiler through go tool compile-help.

Escape case

Case 1: pointer

The first case is the question that was thrown at the beginning, and now you look at it again, think about it, as follows:

Type User struct {ID int64 Name string Avatar string} func GetUserInfo () * User {return & User {ID: 13746731, Name: "EDDYCJY", Avatar: "https://avatars0.githubusercontent.com/u/13746731"}} func main () {_ = GetUserInfo ()}

Execute the command to observe, as follows:

$go build-gcflags'- m-l' main.go # command-line-arguments. / main.go:10:54: & User literal escapes to heap

By looking at the results of the analysis, we can see that User escaped to the heap, that is, it was assigned to the heap. Is there a problem? Take a look at the assembly code to make sure, as follows:

We focused on the CALL instruction and found that it executed the runtime.newobject method, which is indeed assigned to the heap. Why is that?

Analysis result

This is because GetUserInfo () returns a pointer object and the reference is returned outside the method. So the compiler allocates the object to the heap, not to the stack.

Otherwise, after the end of the method, the local variable will be recycled, which is not a rollover. So it is only natural that it will eventually be allocated to the heap.

Think again.

So you might think, that is, all pointer objects should be on the heap? Not really. As follows:

Func main () {str: = new (string) * str = "EDDYCJY"}

Where do you think this object will be assigned? As follows:

$go build-gcflags'- m-l' main.go # command-line-arguments. / main.go:4:12: main new (string) does not escape

Obviously, the object is assigned to the stack. The core point is whether it is referenced outside the scope, where the scope is still in the main, so it does not escape.

Case 2: undetermined type

Func main () {str: = new (string) * str = "EDDYCJY" fmt.Println (str)}

Execute the command to observe, as follows:

$go build-gcflags'- m-l' main.go # command-line-arguments. / main.go:9:13: str escapes to heap. / main.go:6:12: new (string) escapes to heap. / main.go:9:13: main. Argument does not escape

By looking at the analysis results, you can see that the str variable escaped to the heap, that is, the object is allocated on the heap. But in the last case, it was still on the stack, so we just output it from fmt. This is... What on earth happened?

Analysis result

Compared to case 1, case 2 adds only one line of code fmt.Println (str), which must be the problem. Its prototype:

Func Println (a... interface {}) (n int, err error)

Through the analysis of it, we can know that when the formal parameter is an interface type, the compiler can not determine its specific type in the compilation phase. As a result, there is an escape, which is eventually assigned to the heap.

If you are interested in chasing the source code, you can take a look at the internal reflect.TypeOf (arg). Kind () statement, which causes the heap to escape, and the representation is that the interface type causes the object to be assigned to the heap.

Case 3. Divulging parameters

Type User struct {ID int64 Name string Avatar string} func GetUserInfo (u * User) * User {return u} func main () {_ = GetUserInfo (& User {ID: 13746731, Name: "EDDYCJY", Avatar: "https://avatars0.githubusercontent.com/u/13746731"})}"

Execute the command to observe, as follows:

$go build-gcflags'- m-l' main.go # command-line-arguments. / main.go:9:18: leaking param: u to result ~ R1 level=0. / main.go:14:63: main & User literal does not escape

We note the statement of leaking param, which shows that the variable u is a leaky parameter. Combined with the code, you can know that after it is passed to the GetUserInfo method, it does not do any actions involving variables, such as references, and returns the variable directly.

So this variable doesn't actually escape, and its scope is still in main (), so it's assigned on the stack.

Think again.

So how do you think about how to allocate it to the heap? Combined with case one, there are three examples. The modifications are as follows:

Type User struct {ID int64 Name string Avatar string} func GetUserInfo (u User) * User {return & u} func main () {_ = GetUserInfo (User {ID: 13746731, Name: "EDDYCJY", Avatar: "https://avatars0.githubusercontent.com/u/13746731"})}"

Execute the command to observe, as follows:

$go build-gcflags'- m-l' main.go # command-line-arguments. / main.go:10:9: & u escapes to heap. / main.go:9:18: moved to heap: U

As long as it is modified, it is considered to be referenced externally, so it is properly allocated to the heap

At this point, the study of "where are the Go language variables assigned" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report