쿼카러버의 기술 블로그
[golang] time.After이란? (memory leak 가능성) 본문
context를 사용해서 웹서버가 2초 안에 꺼지게 되면 request가 cancel되는 코드를 짜는 예제를 보면 time.After가 많이 쓰인다.
다른 branch가 더 빨리 run하게 되면, timer는 release되지 않는 식으로 select 문에 많이 쓰인다.
그리고 어떻게 작동하는지 확인 하기 위해 아래 예제를 보면,
time.After(2*time.Second)는 select문이 도달한 이후 시점 부터 계산되고, function이 끝나는 것을 확인할 수 있다.
Memory Leak이 발생하지 않는 케이스
select {
case <-time.After(time.Second):
// do something after 1 second.
}
위 코드의 경우에는 select statement이 끝날 한 가지 경우밖에 없기 때문에 memory leak이 발생하지 않는다.
timer.After()함수에 의해 생성된 timer는 select statement가 끝나면서 같이 멈추고, resource도 free된다.
]
Memory Leak이 발생할 수 있는 time.After() function
select {
case <-time.After(time.Second):
// do something after 1 second.
case <-ctx.Done():
// do something when context is finished.
// resources created by the time.After() will not be garbage collected
}
위와 같이 코드를 작성하는 경우가 매우 많다.
만약 타이머가 먼저 끝나게되면 GC가 정상적으로 작동하지만, ctx.Done()이 먼저 발생할 경우, time.After()에 의해 생성된 timer는 멈추지 않고, 리소스는 release되지 않는다. = memory leak
Improved usage of the time.After() function
따라서, timer.After()을 사용해서 타이머를 만드는 것 보다는 아래처럼 하는 것이 가장 바람직 하다.
delay := time.NewTimer(time.Second)
select {
case <-delay.C:
// do something after one second.
case <-ctx.Done():
// do something when context is finished and stop the timer.
if !delay.Stop() {
// if the timer has been stopped then read from the channel.
<-delay.C
}
}
위와 같은 경우는 어떤 case가 됐든, 타이머가 GC에 의해 수거된다.
ctx.Done()이 먼저 발생해도, delay.Stop()에 의해서 resource가 release될 것이기 때문이다.
또한 ctx.Done()이 발생하자마자 timer가 expire되는 경우도 있을 수 있기 때문에, 한번더 확인하는 코드도 추가된다.
https://www.arangodb.com/2020/09/a-story-of-a-memory-leak-in-go-how-to-properly-use-time-after/
'[Golang]' 카테고리의 다른 글
[golang] Unit Test 마스터하기 (step1. 테스트 전에 미리 설정이 필요할때) (testify suite패키지) (0) | 2021.12.08 |
---|---|
[golang] Type assertion과 Type conversion 차이 (0) | 2021.09.24 |
[golang] Marshal, Unmarshal 차이 (0) | 2021.08.09 |
[golang] string pointer는 언제쓸까? (0) | 2021.08.09 |
[golang] Closure(클로저)란? (0) | 2021.08.02 |