>  기사  >  백엔드 개발  >  golang 메모리 누수의 원인은 무엇입니까?

golang 메모리 누수의 원인은 무엇입니까?

青灯夜游
青灯夜游원래의
2023-01-10 17:45:482337검색

누출 이유는 다음과 같습니다. 1. time.After()를 사용하면 time.After(duration x)가 NewTimer()를 생성합니다. 기간 x가 만료되기 전에 새로 생성된 타이머는 만료 후 GC가 되지 않습니다. , GC, 2. time.NewTicker 리소스가 제 시간에 해제되지 않음, 3. 차단 선택, 5. 너무 많은 고루틴 적용, 고루틴 차단 등

golang 메모리 누수의 원인은 무엇입니까?

이 튜토리얼의 운영 환경: Windows 7 시스템, GO 버전 1.18, Dell G3 컴퓨터.

golang이 메모리 누수로 쉽게 이어질 수 있는 여러 상황

1. 타이머의 부적절한 사용

1.1 time.After() 사용

기본 time.After()에는 메모리가 있습니다. NewTimer()가 매번 생성되기 때문에 문제가 유출됩니다.After(duration

시간이 지날수록 특히 기간이 길어지면 차이를 직접 확인하시거나 이전 글을 읽어주세요 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)
	}
}

1.2 time.NewTicker 리소스 시간 내에 해제되지 않습니다time.NewTicker를 사용할 때 리소스를 해제하려면 Stop() 메서드를 수동으로 호출해야 합니다. 그렇지 않으면 영구적인 메모리 누수가 발생합니다

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)
	}
}

2. 선택 차단사용 시 select, 완전히 커버되지 않은 경우와 처리할 기본 브랜치가 없으면 결국 메모리 누수로 이어지게 됩니다

2.1 고루틴 차단을 유발합니다
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)
	}
}
위의 상황으로 인해 ch3의 소비가 차단됩니다. 및 메모리 누수 유발

2.2 루프 유휴 상태로 인해 CPU 급증이 발생함
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)
    }
}
위 for 루프 조건이 기본값에 도달하면 루프가 유휴 상태가 되어 결국 CPU가 급증하게 됩니다

3. 채널 차단은 주로 쓰기 차단과 읽기 차단의 두 가지 상황으로 나뉩니다.빈 채널

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)
}

쓰기 차단

버퍼되지 않은 채널 차단은 일반적으로 읽기가 없기 때문에 쓰기 작업이 차단됩니다

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)
}
버퍼가 가득 차서 버퍼링된 채널이 차단되어 쓰기 작업이 차단되었습니다

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)
}
    읽기 차단
기대됩니다. 채널에서 데이터를 읽으니 결과적으로 데이터를 쓰는 고루틴이 없습니다

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)
}

4. 고루틴으로 인한 메모리 누수

4.1 너무 많은 고루틴을 적용함

예를 들어 for 루프에서 너무 많은 고루틴이 제때 해제되지 않아 메모리 누수가 발생함

4.2 고루틴 차단

4.2.1 I/O 문제

I/O 연결이 시간 초과를 설정하지 않아 고루틴이 계속 대기하고 코드가 계속 차단됩니다.

4.2.2 뮤텍스 잠금이 해제되지 않았습니다
고루틴이 잠금 리소스를 얻을 수 없어 고루틴이 차단됩니다.
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)
}

4.2.3 교착 상태
프로그램 교착 상태가 발생하면 다른 고루틴이
//协程拿到锁未释放,其他协程获取锁会阻塞
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)
}

4.2.4 waitgroup의 부적절한 사용
waitgroup의 Add, Done 및 wait 수의 불일치로 인해 wait가 계속 대기하게 됩니다.

5.

두 개의 슬라이스 공유 주소 중 하나는 전역 변수이고 다른 하나는 GC일 수 없습니다. 슬라이스를 추가한 후 사용되었으며 정리되지 않았습니다.

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)
}

6. 배열의 값 전송

배열은 Golang의 기본 데이터 유형이므로 각 배열은 서로 다른 메모리 공간을 차지하고 수명주기가 서로 간섭하지 않습니다. 누출이 있지만 배열로 형식 매개변수를 전송할 때 시간 값의 복사본을 따르십시오. 여러 고루틴에서 함수를 호출하고 배열이 너무 크면 메모리 사용량이 급증합니다.
var a []int
 
func test(b []int) {
        a = b[:3]
        return
}
따라서 슬라이스나 포인터는 일반적으로 단기적인 메모리 사용량 급증을 피하기 위해 공식 매개변수 시나리오에서 대규모 배열을 전송하는 데 사용됩니다.

위 내용은 golang 메모리 누수의 원인은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.