当前位置:网站首页>Fuite de mémoire Gol

Fuite de mémoire Gol

2021-11-25 18:19:14 Le vrai sens du bonheur

Résumé

  • Les variables de pointeur qui s'échappent sont elles - mêmes assignées dans l'espace de tas,Ainsi, la fonction peut renvoyer l'adresse de la variable locale.La variable locale est maintenant équivalente à la variable pointeur locale,En fuite,La variable pointeur elle - même est également assignée dans l'espace tas,Pour pouvoir retourner son adresse.
  • La mémoire de l'espace de pile est gérée par le compilateur,La distribution se libère rapidement.Espace de tas,PargcGestion,FréquentgcLes frais généraux du système seront plus élevés,stop the world
  • L'analyse d'évasion est effectuée par le compilateur au moment de la compilation statique.
  • La variable slice elle - même s'échappe,Et celui du basdataLes zones s'échappent aussi.Même si la longueur de la tranche est très petite.
  • La variable slice elle - même ne s'échappe pas,En général, c'estdataLa zone est également sur la pile,Si la longueur est trop longue,EtdataLa zone sera assignée au tas,Mais la variable slice elle - même est toujours sur la pile.

Comment déterminer l'évasion de mémoire?

go run -gcflags '-m -l' main.go

Attention!:La commande ci - dessus n'a de sortie que lorsque le compilateur est connecté pour déterminer s'il s'échappe.
Parfois, l'espace de pile est plus petit que l'adresse de l'espace de tas,Je ne sais pas pourquoi.

Exemple d'évasion de mémoire

func main()  {
    a := 1
    _ = a
}

// Pas de sortie
func main()  {
    a := 1
    fmt.Printf("%p\n", &a)
    fmt.Println(a)

    b := 2
    fmt.Printf("%p\n", &b)
    fmt.Println(b)

    c := 3
    fmt.Printf("%p\n", &c)
    fmt.Println(c)

    d := 4
    fmt.Printf("%p\n", &d)
    fmt.Println(d)
}

// Produits
./main.go:10:2: moved to heap: a
./main.go:14:2: moved to heap: b
./main.go:18:2: moved to heap: c
./main.go:22:2: moved to heap: d
./main.go:11:12: ... argument does not escape
./main.go:12:13: ... argument does not escape
./main.go:12:13: a escapes to heap
./main.go:15:12: ... argument does not escape
./main.go:16:13: ... argument does not escape
./main.go:16:13: b escapes to heap
./main.go:19:12: ... argument does not escape
./main.go:20:13: ... argument does not escape
./main.go:20:13: c escapes to heap
./main.go:23:12: ... argument does not escape
./main.go:24:13: ... argument does not escape
./main.go:24:13: d escapes to heap
0xc000012080
1
0xc000012088
2
0xc0000120a0
3
0xc0000120a8
4

fmt.PrintfFonctionsabcAssigner une valeur àinterface{}, Cause Escape ,abc L'adresse est dans l'espace de tas , Comme le montre le graphique , Ça confirme qu'au - dessus du tas .Comparer ci - dessous:

func main()  {
    a := 1
    println(&a)
    println(a)

    b := 2
    println(&b)
    println(b)

    c := 3
    println(&c)
    println(c)

    d := 4
    println(&d)
    println(d)
}

// Produits
0xc00002e768
1
0xc00002e760
2
0xc00002e758
3
0xc00002e750
4

println Ne déclenche pas l'évasion de mémoire ,abc Allocation dans l'espace de pile , La croissance des adresses de haut en bas le confirme. .

Quatre pointeurs ne s'échappent pas :

func main()  {
    a := new(int)
    println(&a)
    println(a)

    println("======")
    b := new(int)
    println(&b)
    println(b)

    println("======")
    c := new(int)
    println(&c)
    println(c)

    println("======")
    d := new(int)
    println(&d)
    println(d)
}

// Produits
./main.go:32:10: new(int) does not escape
./main.go:39:10: new(int) does not escape
./main.go:46:10: new(int) does not escape
./main.go:53:10: new(int) does not escape
0xc00002e768
0xc00002e740
======
0xc00002e760
0xc00002e738
======
0xc00002e758
0xc00002e730
======
0xc00002e750
0xc00002e748

abcd La variable pointeur elle - même et la zone mémoire à laquelle elle se réfère ,Pas de fuite, Tout est attribué dans l'espace de pile , Les adresses imprimées sont des zones contiguës de grande à petite taille. .
L'allocation de mémoire est comme indiqué dans la figure :
image.png

Les quatre pointeurs s'échappent.

func main() {
    a := new(int)
    fmt.Printf("%p\n", &a)
    fmt.Println(a)
    println("======")

    b := new(int)
    fmt.Printf("%p\n", &b)
    fmt.Println(b)

    println("======")
    c := new(int)
    fmt.Printf("%p\n", &c)
    fmt.Println(c)
    println("======")

    d := new(int)
    fmt.Printf("%p\n", &d)
    fmt.Println(d)

    println("======")
    f := new(int)
    println(&f)
    println(f)
}

//Produits
0xc00000e028
0xc000012080
======
0xc00000e038
0xc000012088
======
0xc00000e040
0xc0000120a0
======
0xc00000e048
0xc0000120a8
======
0xc000058f38
0xc000058f30

En cas de fuite ,abcd La variable pointeur elle - même et la zone mémoire à laquelle elle se réfère , Tout est distribué dans l'espace de tas , L'impression d'adresses de petite à grande zone continue le confirme. .f La variable pointeur et la zone mémoire à laquelle elle se réfère sont assignées dans l'espace de pile , Son adresse est plus élevée que l'adresse de la zone de tas, ce qui le confirme. .
Les variables de pointeur qui s'échappent sont elles - mêmes assignées dans l'espace de tas, Ainsi, la fonction peut renvoyer une variable de pointeur local
Le diagramme d'allocation de mémoire est le suivant :
image.png


func main()  {
    a := new(int)
    _ = a
}

// Produits
./main.go:8:10: main new(int) does not escape

Même sinewVariable de, Quand je n'ai pas fui , Est également assigné à la pile

  • println La fonction ne déclenche pas l'évasion ,Etfmt.PrintfOui., Parce que son argument de fonction est interface{}Type
  • Attention!: Exemple ci - dessus,a Est la variable pointeur assignée dans l'espace de pile ,Pointéint Unit é de stockage dans l'octet suivant ( C'est aussi dans l'espace de pile ).
  • L'espace de pile à haute adresse est , De haut en bas .

Si c'étaitC++C'est écrit, C'est une erreur typique : Renvoie l'adresse de la variable locale , Le contenu de cette adresse est automatiquement libéré après la sortie de la fonction , Parce que c'est sur la pile .

Alorsgo Les variables locales de la langue sont - elles sur la pile ou sur le tas? ?go Le compilateur de langue fera une analyse d'évasion (escape analysis), Analyser si la portée de la variable locale s'échappe de la portée de la fonction ,Si ce n'est pas le cas,, Alors mets - le sur la pile ; Si la variable est hors de portée de la fonction , Alors, mettez - le sur la pile. .

Observation des essaisgChangement d'adresse pour

func main {
    var g *int
    println(&g)
    println(g)
    g = new(int)
    println(&g)
    println(g)
    g = new(int)
    println(&g)
    println(g)
    g = new(int)
    fmt.Println(&g)
    fmt.Println(g)
}

Sans les deux dernières lignes ,Pas de fuite.,gAvecg La mémoire pointée est dans la pile , En plus, tout est dans la zone de gerbage .

Une scène d'évasion

package main

type Student struct {
 Name interface{}
}

func main()  {
 stu := new(Student)
 stu.Name = "tom"

}

interface{} Affectation,Il y aura une fuite.,L'optimisation consiste à définir le type à un type fixe,Par exemple:string

package main

type Student struct {
 Name string
}

func GetStudent() *Student {
 stu := new(Student)
 stu.Name = "tom"
 return stu
}

func main() {
 GetStudent()
}

Retour( Adresse de la variable locale )Type de pointeur,Il y aura une fuite.,Options d'optimisation selon le cas.
La fonction passe - t - elle efficacement le pointeur et la valeur??Nous savons que le passage de pointeurs réduit la copie des valeurs sous - jacentes,Peut améliorer l'efficacité,Mais si la quantité de données copiées est faible,Parce que le pointeur s'échappe,Le tas peut être utilisé,Peut également augmenter GC Le fardeau,Donc le pointeur de passage n'est pas nécessairement efficace.
N'utilisez pas aveuglément le pointeur de variable comme paramètre,Bien que la réplication ait été réduite,Mais l'évasion variable peut coûter plus cher.

func main()  {
    nums := make([]int, 5, 5)
    nums2 := make([]int, 5, 5)
    println(&nums)
    println(nums)
    println(&nums2)
    println(nums2)
    for i := range nums {
        nums[i] = i
        println(&nums[i])
    }
    println("======2")
    for i := range nums2 {
        nums2[i] = i
        println(&nums2[i])
    }

    println("======3")
    nums3 := make([]int, 10000, 10000)
    println(&nums3)
    println(nums3)
    for i := range nums3 {
        if i == 5 {
            break
        }
        nums3[i] = i
        println(&nums3[i])
    }
    //fmt.Println(&nums3)

}

// Produits
./main.go:8:14: make([]int, 5, 5) does not escape
./main.go:9:15: make([]int, 5, 5) does not escape
./main.go:25:15: make([]int, 10000, 10000) escapes to heap
0xc00002e758
[5/5]0xc00002e6f8
0xc00002e740
[5/5]0xc00002e6d0
0xc00002e6f8
0xc00002e700
0xc00002e708
0xc00002e710
0xc00002e718
======2
0xc00002e6d0
0xc00002e6d8
0xc00002e6e0
0xc00002e6e8
0xc00002e6f0
======3
0xc00002e728
[10000/10000]0xc000054000
0xc000054000
0xc000054008
0xc000054010
0xc000054018
0xc000054020

Comme ci - dessus:No1、2- Oui.slice Variable et son Sous - sol data Zone du pointeur , Tout est assigné sur la pile .
No3 Une très grande tranche ,slice La variable elle - même est assignée sur la pile , Les données sous - jacentes sont en tas .
Si vous Commentez le dernier fmt.PrintfPar.3- Oui.slice Les variables elles - mêmes s'échappent et sont assignées au tas .

Attention!:Tranchedata Régions 1À lan L'espace d'adresse des éléments est à partir de 0 Plus haut , Que ce soit sur la pile ou sur le tas .

func main()  {
    var slice1 []int
    println(&slice1)
    println(slice1)
    fmt.Printf("%p\n", &slice1)
    println(&slice1)
    println(slice1)
    slice1 = make([]int,5,5)
    println(&slice1)
    println(slice1)
}

// Produits
./main.go:11:6: moved to heap: slice1
./main.go:14:12: ... argument does not escape
./main.go:17:15: make([]int, 5, 5) escapes to heap
0xc0000a4018
[0/0]0x0
0xc0000a4018
0xc0000a4018
[0/0]0x0
0xc0000a4018
[5/5]0xc0000aa030

La variable slice elle - même s'échappe,Et celui du basdataLes zones s'échappent aussi.Même si la longueur de la tranche est très petite.
La variable slice elle - même ne s'échappe pas,En général, c'estdataLa zone est également sur la pile,Si la longueur est trop longue,EtdataLa zone sera assignée au tas,Mais la variable slice elle - même est toujours sur la pile.

package main

func main() {
 nums := make([]int, 10000, 10000)

 for i := range nums {
  nums[i] = i
 }
}

La tranche est trop grande ,Il y aura une fuite.,Optimiser le schéma pour maximiser la capacité,Si la capacité est trop grande, c'est impossible..

map L'élément ne peut pas prendre d'adresse .

map Quand je n'ai pas fui , Est également assigné sur la pile , Variable elle - même et zone de données sous - jacente .

版权声明
本文为[Le vrai sens du bonheur]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/11/20211125181257766w.html

随机推荐