24

我已经尝试 Go 一段时间了,这个问题一直困扰着我。假设我在一个切片中构建了一个有点大的数据集(例如,1000 万个 int64)。

package main

import (
    "math"
    "fmt"
)

func main() {
    var a []int64
    var i int64;
    upto := int64(math.Pow10(7))
    for i = 0; i < upto; i++ {
        a = append(a, i)
    }
    fmt.Println(cap(a))
}

但后来我决定我不想要其中的大多数,所以我想最终只得到其中的 10 个。我在 Go 的 wiki 上尝试了切片和删除技术,但似乎都没有降低切片的容量。

所以这就是我的问题:Go 是否没有真正的方法来缩小切片的容量,类似于realloc()-ing 的大小参数比您之前在 C 中对同一指针的调用中更小?这是一个问题,应该如何处理?

4

6 回答 6

30

实际上,要执行切片的重新分配:

a = append([]T(nil), a[:newSize]...) // Thanks to @Dijkstra for pointing out the missing ellipsis.

如果它将newSize元素复制到一个新的内存位置,或者它在 realloc(3) 中进行了实际的就地调整大小,则完全由编译器决定。您可能想调查当前状态,如果有改进的余地,可能会提出问题。

然而,这可能是一个微优化。性能增强的第一个来源几乎总是选择更好的算法和/或更好的数据结构。使用一个巨大的向量来最终只保留一些项目可能不是内存消耗的最佳选择。

编辑:以上只是部分正确。在一般情况下,编译器不能推导是否有其他指向切片支持数组的指针。因此 realloc 不适用。上面的代码片段实际上保证执行“newSize”元素的副本。对于可能造成的任何混乱,我们深表歉意。

于 2013-05-25T10:02:37.393 回答
7

Go 没有缩小切片的方法。在大多数情况下这不是问题,但是如果您分析内存使用情况并发现您使用的过多,您可以采取一些措施:

首先,您可以创建所需大小的切片并将数据复制到其中。然后垃圾收集器将释放大片。复制内置

其次,您可以在每次希望生成大切片时重新使用它,因此您永远不会多次分配它。

最后一点,您可以使用1e7而不是math.Pow10(7).

于 2013-05-25T09:59:57.213 回答
3

此外,您可以在您的应用程序工作期间重复使用大部分分配的内存,请查看:bufs 包

PS如果你为更小的切片重新分配新内存,旧内存可能不会同时被释放,当垃圾收集器决定时它会被释放。

于 2013-05-25T13:16:33.903 回答
3

让我们看看这个例子:

func main() {
    s := []string{"A", "B", "C", "D", "E", "F", "G", "H"}
    fmt.Println(s, len(s), cap(s))  // slice, length, capacity

    t := s[2:4]
    fmt.Println(t, len(t), cap(t))

    u := make([]string, len(t))
    copy(u, t)
    fmt.Println(u, len(u), cap(u))
}

它产生以下输出:

[A B C D E F G H] 8 8
[C D] 2 6
[C D] 2 2

s是一个包含 8 个字符串的切片。t是保留部分的切片[C D]。的长度t为 2,但由于它使用相同的隐藏数组s,其容量为 6(从“C”到“H”)。问题是:如何让其中的一部分[C D]独立于 的隐藏数组s?只需创建一个长度为 2 的新字符串切片(切片u)并将其内容复制tuu的底层隐藏数组与 的隐藏数组不同s

最初的问题是这样的:您有一个大切片,然后在其上创建一个新的较小切片。由于较小的切片使用相同的隐藏数组,垃圾收集器不会删除隐藏数组。

有关更多信息,请参阅这篇文章的底部:http: //blog.golang.org/go-slices-usage-and-internals

于 2014-02-07T19:18:19.837 回答
1

您可以通过将切片的值重新分配给自身的一部分来做到这一点

  a := []int{1,2,3}
  fmt.Println(len(a), a) // 3 [1 2 3]

  a = a[:len(a)-1]
  fmt.Println(len(a), a) //2 [1 2]
于 2020-01-04T17:00:04.157 回答
0

Go 1.2 中有一个新特性叫做 3-index slice,意思是通过这种方式获取一个 slice 的一部分:

slice[a:b:c]

其中len返回切片的 应该是b-acav新切片的 应该是c-a

提示:整个过程没有副本down,它只返回一个新的切片,该切片指向&slice[a]并具有len asb-a和cav as c-a

这是您唯一需要做的事情:

slice= slice[0:len(slice):len(slice)];

然后cav切片的 将更改为len(slice) - 0,这与它的相同len,并且不进行复制。

于 2022-02-07T04:00:20.003 回答