Maison > développement back-end > Golang > Quelles sont les causes des fuites de mémoire Golang ?

Quelles sont les causes des fuites de mémoire Golang ?

青灯夜游
Libérer: 2023-01-10 17:45:48
original
2396 Les gens l'ont consulté

Les raisons de la fuite sont : 1. L'utilisation de time.After(). Chaque fois.After(duration x) générera NewTimer() Avant l'expiration de la durée x, la minuterie nouvellement créée ne sera pas GC. , GC; 2. Les ressources time.NewTicker ne sont pas libérées à temps ; 3. blocage de sélection ; 4. blocage de canal ; 5. application d'un trop grand nombre de goroutines, blocage de goroutines ;

Quelles sont les causes des fuites de mémoire Golang ?

L'environnement d'exploitation de ce tutoriel : système Windows 7, GO version 1.18, ordinateur Dell G3.

Plusieurs situations où Golang peut facilement entraîner des fuites de mémoire

1. Utilisation inappropriée des minuteries

1.1 L'utilisation de time.After()

L'heure par défaut.After() a de la mémoire Le problème est divulgué car NewTimer() sera généré à chaque fois.After(duration

Au fil du temps, surtout si la durée Veuillez vérifier la différence par vous-même ou lire mes articles précédents https://blog.csdn.net/weixin_38299404/article/details/119352884

for true {
	select {
	case <-time.After(time.Minute * 3):
    // do something
  default:
		time.Sleep(time.Duration(1) * time.Second)
	}
}
Copier après la connexion

1.2 time.NewTicker ressources ne sont pas libérés à tempslors de l'utilisation de time.NewTicker Vous devez appeler manuellement la méthode Stop() pour libérer les ressources, sinon cela provoquera une fuite de mémoire permanente

timer := time.NewTicker(time.Duration(2) * time.Second)
defer timer.Stop()
for true {
	select {
	case <-timer.C:
		// do something
	default:
		time.Sleep(time.Duration(1) * time.Second)
	}
}
Copier après la connexion

2. select, s'il y a un cas qui n'est pas entièrement couvert et qu'il n'y a pas de branche par défaut pour le traitement, cela finira par entraîner des fuites de mémoire

2.1 Causer le blocage de goroutine

timer := time.NewTicker(time.Duration(2) * time.Second)
// defer timer.Stop()
for true {
	select {
	case <-timer.C:
		// do something
	default:
		time.Sleep(time.Duration(1) * time.Second)
	}
}
Copier après la connexion
La situation ci-dessus bloquera la consommation de ch3 et provoquer des fuites de mémoire

2.2 L'inactivité de la boucle provoque une surtension du processeur

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    ch3 := make(chan int)
    go Getdata("https://www.baidu.com",ch1)
    go Getdata("https://www.baidu.com",ch2)
    go Getdata("https://www.baidu.com",ch3)
    select{
        case v:=<- ch1:
            fmt.Println(v)
        case v:=<- ch2:
            fmt.Println(v)
    }
}
Copier après la connexion
Ci-dessus Une fois que la condition de la boucle for atteint la valeur par défaut, la boucle deviendra inactive et finira par faire monter en flèche le processeur

3.

Le blocage des canaux est principalement divisé en deux situations : le blocage en écriture et le blocage en lecture

canal vide
func main() {
	fmt.Println("main start")
	msgList := make(chan int, 100)
	go func() {
		for {
			select {
			case <-msgList:
			default:
 
			}
		}
	}()
	
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	s := <-c
	
	fmt.Println("main exit.get signal:", s)
}
Copier après la connexion
Blocage en écriture

Le blocage du canal sans tampon est généralement l'opération d'écriture est bloquée car il n'y a pas de lecture

func channelTest() {
  	//声明未初始化的channel读写都会阻塞
    var c chan int
  	//向channel中写数据
    go func() {
        c <- 1
        fmt.Println("g1 send succeed")
        time.Sleep(1 * time.Second)
    }()
  	//从channel中读数据
    go func() {
        <-c
        fmt.Println("g2 receive succeed")
        time.Sleep(1 * time.Second)
    }()
    time.Sleep(10 * time.Second)
}
Copier après la connexion

    Le canal tamponné est bloqué car le tampon est plein, l'opération d'écriture est bloquée
  • func channelTest() {
        var c = make(chan int)
      	//10个协程向channel中写数据
        for i := 0; i < 10; i++ {
            go func() {
                <- c
                fmt.Println("g1 receive succeed")
                time.Sleep(1 * time.Second)
            }()
        }
      	//1个协程丛channel读数据
        go func() {
            c <- 1
            fmt.Println("g2 send succeed")
            time.Sleep(1 * time.Second)
        }()
      	//会有写的9个协程阻塞得不到释放
        time.Sleep(10 * time.Second)
    }
    Copier après la connexion
Blocage de la lecture

    J'attends avec impatience la lecture des données du canal, le résultat est qu'il n'y a pas de goroutine pour écrire des données
  • func channelTest() {
        var c = make(chan int, 8)
      	//10个协程向channel中写数据
        for i := 0; i < 10; i++ {
            go func() {
                <- c
                fmt.Println("g1 receive succeed")
                time.Sleep(1 * time.Second)
            }()
        }
      	//1个协程丛channel读数据
        go func() {
            c <- 1
            fmt.Println("g2 send succeed")
            time.Sleep(1 * time.Second)
        }()
      	//会有写的几个协程阻塞写不进去
        time.Sleep(10 * time.Second)
    }
    Copier après la connexion

4. Fuite de mémoire causée par goroutine

4.1 Demander trop de goroutines

Par exemple, dans une boucle for Trop de goroutines ne sont pas libérées à temps, provoquant des fuites de mémoire

4.2 Blocage de Goroutine

4.2.1 Problème d'E/SLa connexion d'E/S ne définit pas de délai d'attente, ce qui oblige la goroutine à attendre et le code continuera à se bloquer.

4.2.2 Le verrou mutex n'est pas libéré

goroutine ne peut pas obtenir la ressource de verrouillage, ce qui provoque le blocage de la goroutine
func channelTest() {
   var c = make(chan int)
  //1个协程向channel中写数据
  go func() {
    <- c
    fmt.Println("g1 receive succeed")
    time.Sleep(1 * time.Second)
  }()
  //10个协程丛channel读数据
  for i := 0; i < 10; i++ {
    go func() {
        c <- 1
        fmt.Println("g2 send succeed")
        time.Sleep(1 * time.Second)
    }()
  }
  //会有读的9个协程阻塞得不到释放
  time.Sleep(10 * time.Second)
}
Copier après la connexion
4.2.3 Deadlock

Lorsque le programme se bloque, d'autres goroutines le feront bloque également
//协程拿到锁未释放,其他协程获取锁会阻塞
func mutexTest() {
    mutex := sync.Mutex{}
    for i := 0; i < 10; i++ {
        go func() {
            mutex.Lock()
            fmt.Printf("%d goroutine get mutex", i)
      			//模拟实际开发中的操作耗时
            time.Sleep(100 * time.Millisecond)
        }()
    }
    time.Sleep(10 * time.Second)
}
Copier après la connexion
4.2.4 Utilisation inappropriée du groupe d'attente

La différence entre le nombre d'ajout, terminé et d'attente du groupe d'attente entraînera une attente constante
5. par slice

Lorsque deux tranches sont partagées, dont l'une est une variable globale, l'autre ne peut pas être GC

Elle a été utilisée après l'ajout de la tranche et n'a pas été nettoyée.
func mutexTest() {
    m1, m2 := sync.Mutex{}, sync.RWMutex{}
  	//g1得到锁1去获取锁2
    go func() {
        m1.Lock()
        fmt.Println("g1 get m1")
        time.Sleep(1 * time.Second)
        m2.Lock()
        fmt.Println("g1 get m2")
    }()
    //g2得到锁2去获取锁1
    go func() {
        m2.Lock()
        fmt.Println("g2 get m2")
        time.Sleep(1 * time.Second)
        m1.Lock()
        fmt.Println("g2 get m1")
    }()
  	//其余协程获取锁都会失败
    go func() {
        m1.Lock()
        fmt.Println("g3 get m1")
    }()
    time.Sleep(10 * time.Second)
}
Copier après la connexion

6. Transfert de valeur des tableaux

Étant donné que les tableaux sont le type de données de base de Golang, chaque tableau occupe un espace mémoire différent et le cycle de vie n'interfère pas les uns avec les autres. fuite, mais en tant que tableau Lors du transfert des paramètres formels, suivez la copie de la valeur temporelle. Si la fonction est appelée par plusieurs goroutines et que le tableau est trop grand, cela entraînera une augmentation de l'utilisation de la mémoire.

var a []int
 
func test(b []int) {
        a = b[:3]
        return
}
Copier après la connexion
Par conséquent, les tranches ou les pointeurs sont généralement utilisés pour transférer de grands tableaux dans des scénarios de paramètres formels afin d'éviter une augmentation à court terme de l'utilisation de la mémoire.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal