Go에서 슬라이스를 어떻게 지우나요?


125

Go에서 슬라이스를 지우는 적절한 방법은 무엇입니까?

다음은 go 포럼 에서 찾은 내용입니다 .

// test.go
package main

import (
    "fmt"
)

func main() {
    letters := []string{"a", "b", "c", "d"}
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
    // clear the slice
    letters = letters[:0]
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
}

이 올바른지?

명확히하기 위해 버퍼를 지워 재사용 할 수 있습니다.

예는 bytes 패키지의 Buffer.Truncate 함수입니다.

Reset은 Truncate (0) 만 호출합니다. 따라서이 경우 70 행은 다음과 같이 평가됩니다. b.buf = b.buf [0 : 0]

http://golang.org/src/pkg/bytes/buffer.go

// Truncate discards all but the first n unread bytes from the buffer.
60  // It panics if n is negative or greater than the length of the buffer.
61  func (b *Buffer) Truncate(n int) {
62      b.lastRead = opInvalid
63      switch {
64      case n < 0 || n > b.Len():
65          panic("bytes.Buffer: truncation out of range")
66      case n == 0:
67          // Reuse buffer space.
68          b.off = 0
69      }
70      b.buf = b.buf[0 : b.off+n]
71  }
72  
73  // Reset resets the buffer so it has no content.
74  // b.Reset() is the same as b.Truncate(0).
75  func (b *Buffer) Reset() { b.Truncate(0) }

1
빠른 테스트 : play.golang.org/p/6Z-qDQtpbg 가 작동한다고 제안하는 것 같습니다 (용량을 변경하지는 않지만 길이를
줄임

답변:


120

그것은 모두 당신의 '명확한'정의에 달려 있습니다. 유효한 것 중 하나는 확실히 다음과 같습니다.

slice = slice[:0]

하지만 문제가 있습니다. 슬라이스 요소가 T 유형 인 경우 :

var slice []T

다음 시행 len(slice)은 "트릭"이상으로, 제로로, 하지 의 요소를 만들

slice[:cap(slice)]

가비지 수집 대상입니다. 이는 일부 시나리오에서 최적의 접근 방식 일 수 있습니다. 그러나 이것은 또한 "메모리 누수"의 원인이 될 수 있습니다. 메모리는 사용되지 않지만 잠재적으로 도달 할 수 있으며 ( '슬라이스'재분할 후) 가비지 "수집 가능"이 아닙니다.


1
흥미 롭군. 기본 용량을 변경하지 않고 그대로두고 슬라이스의 기본 어레이에서 모든 요소를 ​​제거하는 다른 방법이 있습니까?
Chris Weber

3
@ChrisWeber : 단지 반복 처리 기본 배열을 통해 새로운 값으로 모든 요소를 설정
newacct

2
@jnml, 슬라이스 (및 기본 배열 저장소)를 재사용하고 싶으므로 새 슬라이스 (배열 포함)를 지속적으로 할당하지 않습니다. 나는 표준 라이브러리의 일부 예제 코드를 명확히하고 보여주기 위해 내 질문을 편집했습니다.
Chris Weber

1
Go를 처음 사용합니다. 이것이 최적의 접근 방식이 될 수있는 이유에 대해 자세히 설명해 주시겠습니까? 미리 감사드립니다.
satoru

슬라이스 크기 재설정으로 인해 메모리 누수가 발생합니까? 나는 그것을 재현 할 수 아니다
마소 Barbugli

197

슬라이스를 nil로 설정하는 것이 슬라이스를 지우는 가장 좋은 방법입니다. nil이동중인 슬라이스는 완벽하게 잘 작동하며 슬라이스를로 설정 nil하면 기본 메모리가 가비지 수집기에 해제됩니다.

놀이터보기

package main

import (
    "fmt"
)

func dump(letters []string) {
    fmt.Println("letters = ", letters)
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
    for i := range letters {
        fmt.Println(i, letters[i])
    }
}

func main() {
    letters := []string{"a", "b", "c", "d"}
    dump(letters)
    // clear the slice
    letters = nil
    dump(letters)
    // add stuff back to it
    letters = append(letters, "e")
    dump(letters)
}

인쇄물

letters =  [a b c d]
4
4
0 a
1 b
2 c
3 d
letters =  []
0
0
letters =  [e]
1
1
0 e

두 슬라이스가 동일한 기본 메모리를 가리 키도록 슬라이스를 쉽게 앨리어싱 할 수 있습니다. 에 대한 설정 nil은 해당 별칭을 제거합니다.

이 방법은 용량을 0으로 변경합니다.


응답 해 주셔서 감사합니다. 내 업데이트를 참조하십시오. 재사용을 위해 슬라이스를 정리하고 있습니다. 따라서 다시 할당해야하므로 기본 메모리가 GC에 릴리스되는 것을 반드시 원하지는 않습니다.
Chris Weber

내가 검색 한 것입니다)!
티무르 Fayzrakhmanov

5
"Go에서 슬라이스를 지우는 방법"이라는 제목을 기반으로합니다. 이것은 훨씬 더 안전한 대답이며 받아 들여 져야합니다. 완벽한 대답은 원래 받아 들여진 대답과이 대답을 조합하여 사람들이 스스로 결정할 수 있도록하는 것입니다.
Shadoninja

1
appendnil조각에 ing 은 항상 Go에서 작동 했습니까?
alediaferia

@alediaferia는 1.0 이후로 확실히 이동했습니다.
Nick Craig-Wood

4

나는 내 목적을 위해이 문제를 약간 조사하고 있었다. 나는 구조체 조각 (포인터 포함)을 가지고 있었고 제대로 된 것인지 확인하고 싶었다. 이 스레드에 올랐고 결과를 공유하고 싶었습니다.

연습을 위해 약간의 운동장을 사용했습니다 : https://play.golang.org/p/9i4gPx3lnY

이것에 평가되는 :

package main

import "fmt"

type Blah struct {
    babyKitten int
    kittenSays *string
}

func main() {
    meow := "meow"
    Blahs := []Blah{}
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{1, &meow})
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{2, &meow})
    fmt.Printf("Blahs: %v\n", Blahs)
    //fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
    Blahs = nil
    meow2 := "nyan"
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{1, &meow2})
    fmt.Printf("Blahs: %v\n", Blahs)
    fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
}

해당 코드를있는 그대로 실행하면 "meow"및 "meow2"변수에 대해 동일한 메모리 주소가 동일한 것으로 표시됩니다.

Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
Blahs: []
Blahs: [{1 0x1030e0f0}]
kittenSays: nyan

나는 구조체가 가비지 수집되었음을 확인한다고 생각합니다. 이상하게도 주석 처리 된 인쇄 줄의 주석을 제거하면 야옹에 대해 다른 메모리 주소가 생성됩니다.

Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
kittenSays: meow
Blahs: []
Blahs: [{1 0x1030e0f8}]
kittenSays: nyan

나는 이것이 어떤 식 으로든 (?) 인쇄가 지연 되었기 때문일 수 있지만, 일부 메모리 관리 동작에 대한 흥미로운 그림과 다음 항목에 대한 투표가 하나 더 있습니다.

[]MyStruct = nil

좋은 자세한 예. 감사!
Dolanor

2
이것은 meo1과 meow2의 메모리 주소가 같지 않다는 것을 보여주지 않습니다 : 0x1030e0c0같지 않음 0x1030e0f0(전자는으로 끝나고 c0후자는에서 f0)
carbocation

여기서 @carbocation에 동의하면 메모리 주소가 동일하지 않습니다. 나는 여기서 무슨 일이 일어나고 있는지 더 잘 설명 할 수 있다고 주장하지 않지만 이것은 나에게 증거로 작용하지 않습니다. meow2각 실행 의 주소에서 동일한 8 바이트 불일치가
보입니다
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.