지도에서 선택한 키를 어떻게 제거 할 수 있습니까? delete()
아래 코드와 같이 범위와 결합 하는 것이 안전 합니까?
package main
import "fmt"
type Info struct {
value string
}
func main() {
table := make(map[string]*Info)
for i := 0; i < 10; i++ {
str := fmt.Sprintf("%v", i)
table[str] = &Info{str}
}
for key, value := range table {
fmt.Printf("deleting %v=>%v\n", key, value.value)
delete(table, key)
}
}
답변
이것은 안전합니다! Effective Go 에서도 비슷한 샘플을 찾을 수 있습니다 .
for key := range m {
if key.expired() {
delete(m, key)
}
}
그리고 언어 사양 :
맵에 대한 반복 순서는 지정되지 않았으며 한 반복에서 다음 반복으로 동일하지는 않습니다. 반복 중에 아직 도달하지 않은 맵 항목이 제거 되면 해당 반복 값이 생성되지 않습니다. 반복 중에 맵 항목이 작성 되면 해당 항목이 반복 중에 생성되거나 건너 뛸 수 있습니다. 선택은 생성 된 각 항목과 반복마다 다를 수 있습니다. 맵이 nil 인 경우 반복 횟수는 0입니다.
답변
Sebastian의 답변은 정확하지만 왜 안전한지 알고 싶었 으므로 Map 소스 코드를 파헤 쳤습니다 . 에 대한 호출 에서처럼 보이지만 delete(k, v)
기본적으로 실제로 값을 삭제하는 대신 플래그를 설정하고 카운트 값을 변경합니다.
b->tophash[i] = Empty;
(빈 값은 상수입니다 0
)
지도는 실제로 당신의 속도로 삽입을 수행으로 성장하는지도의 크기에 따라 버킷 세트 번호 할당되어 일을 할 나타납니다 무엇 2^B
(에서 이 소스 코드 ) :
byte *buckets; // array of 2^B Buckets. may be nil if count==0.
따라서 사용하는 것보다 거의 항상 할당 된 버킷이 많으며 range
맵을 오버 오버 할 때 tophash
각 버킷의 값을 확인하여 2^B
건너 뛸 수 있는지 확인합니다.
요약 하면 데이터는 기술적으로 여전히 존재하기 때문에 delete
내부 range
는 안전하지만 데이터를 확인하면 데이터를 tophash
건너 뛰고 range
수행중인 작업에 포함시킬 수 없음을 알 수 있습니다 . 소스 코드에는 TODO
다음이 포함됩니다 .
// TODO: consolidate buckets if they are mostly empty
// can only consolidate if there are no live iterators at this size.
이것은 delete(k,v)
함수를 사용하여 실제로 메모리를 비우는 것이 아니라 액세스 권한이있는 버킷 목록에서 메모리를 제거하는 이유를 설명합니다 . 실제 메모리를 확보하려면 가비지 콜렉션이 시작되도록 전체 맵에 도달 할 수 없도록해야합니다. 다음과 같은 행을 사용하여이를 수행 할 수 있습니다.
map = nil
답변
메모리 누수가 발생할 수 있는지 궁금합니다. 그래서 테스트 프로그램을 작성했습니다.
package main
import (
log "github.com/Sirupsen/logrus"
"os/signal"
"os"
"math/rand"
"time"
)
func main() {
log.Info("=== START ===")
defer func() { log.Info("=== DONE ===") }()
go func() {
m := make(map[string]string)
for {
k := GenerateRandStr(1024)
m[k] = GenerateRandStr(1024*1024)
for k2, _ := range m {
delete(m, k2)
break
}
}
}()
osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals, os.Interrupt)
for {
select {
case <-osSignals:
log.Info("Recieved ^C command. Exit")
return
}
}
}
func GenerateRandStr(n int) string {
rand.Seed(time.Now().UnixNano())
const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
}
return string(b)
}
GC가 메모리를 비우는 것처럼 보입니다. 괜찮습니다.
답변
한마디로 그렇습니다. 이전 답변을 참조하십시오.
또한 여기에서 :
ianlancetaylor는 2015 년 2 월 18 일에 댓글을 달았습니다.
이를 이해하는 열쇠는 for / range 문의 본문을 실행하는 동안 현재 반복이 없다는 것을 인식하는 것입니다. 보인 값 세트와 보지 않은 값 세트가 있습니다. 본문을 실행하는 동안 가장 최근에 본 키 / 값 쌍 중 하나가 range 문의 변수에 할당되었습니다. 해당 키 / 값 쌍에는 특별한 것이 없으며 반복 중에 이미 본 것 중 하나 일뿐입니다.
그가 대답하는 질문은 range
작업 중에 적절한 맵 요소를 수정 하는 것입니다. 이것이 그가 “현재 반복”을 언급하는 이유입니다. 그러나 그것은 또한 관련이 있습니다 : 당신은 범위 동안 키를 삭제할 수 있으며, 그것은 당신이 그 범위에서 나중에 볼 수 없다는 것을 의미합니다 (그리고 이미 보았 으면 괜찮습니다).