Memory management go程序中函数中神秘且过多的内存分配
我有下面的代码,它使用了比预期要高的内存音调。Memory management go程序中函数中神秘且过多的内存分配,memory-management,go,profiling,Memory Management,Go,Profiling,我有下面的代码,它使用了比预期要高的内存音调。 我曾经使用过pprof工具,它显示函数NewEdge正在分配程序分配的所有内存的94%以上 我的问题是,这段代码有什么问题,占用了这么多内存: type Vertex struct { Id string `json:"id"` // must be unique Properties map[string]stri
我曾经使用过
pprof
工具,它显示函数NewEdge
正在分配程序分配的所有内存的94%以上
我的问题是,这段代码有什么问题,占用了这么多内存:
type Vertex struct {
Id string `json:"id"` // must be unique
Properties map[string]string `json:"properties"` // to be implemented soon
verticesThisIsConnectedTo map[string][]string `json:"-"` //id for the edges *Edge // keys are Vertex ids, each pair of vertices can be connected to each other with multiple edges
verticesConnectedToThis map[string][]string `json:"_"` //id for the edges *Edge // keys are Vertex ids,
}
type Edge struct {
id string `json:"-"` // for internal use, unique
Label string `json:"label"`
SourceId string `json:"source-id"`
TargetId string `json:"terget-id"`
Type string `json:"type"`
Properties map[string]string `json:"properties"` // to be implemented soon
}
func (v *Vertex) isPartof(g *Graph) bool {
_, b := g.Vertices[v.Id]
return b
}
func (g *Graph) NewEdge(source, target *Vertex, label, edgeType string) (Edge, error) {
if source.Id == target.Id {
return Edge{}, ERROR_NO_EDGE_TO_SELF_ALLOWED
}
if !source.isPartof(g) || !target.isPartof(g) {
return Edge{}, errors.New("InvalidEdge, source or target not in this graph")
}
e := Edge{id: <-nextId, Label: label, SourceId: source.Id, TargetId: target.Id, Type: edgeType}
g.Edges[e.id] = &e
source.verticesThisIsConnectedTo[target.Id] = append(source.verticesThisIsConnectedTo[target.Id], e.id)
target.verticesConnectedToThis[source.Id] = append(target.verticesConnectedToThis[source.Id], e.id)
return e, nil
}
问题/谜团:
我可以创建数千个顶点
s,内存使用非常合理。但是对NewEdge
的调用非常占用内存。我首先注意到代码使用的是记忆音调。我用-memprofile
运行了pprof
,然后使用go工具pprof
得到了以下结果:
(pprof) top10
Total: 9.9 MB
8.9 89.9% 89.9% 8.9 89.9% main.(*Graph).NewEdge
0.5 5.0% 95.0% 0.5 5.0% allocg
0.5 5.0% 100.0% 0.5 5.0% fmt.Sprintf
0.0 0.0% 100.0% 0.5 5.0% _rt0_go
0.0 0.0% 100.0% 8.9 89.9% main.fakeGraph
0.0 0.0% 100.0% 0.5 5.0% main.func·003
0.0 0.0% 100.0% 8.9 89.9% main.main
0.0 0.0% 100.0% 0.5 5.0% mcommoninit
(pprof)
非常感谢您的帮助。@ali我认为这段记忆中没有什么神秘之处。 首先,若你们检查你们的结构的大小,你们会看到边结构比顶点结构大2倍。(您可以通过unsafe.Sizeof()检查结构的大小) 因此,如果您将调用fakeGraph(Aragog,2000,1),Go将分配:
- 2000顶点结构
- 至少2000*20=40000个边缘结构
如您所见,NewEdge()将分配比fakeGraph()至少多40倍的内存
是的,我看到了您从未使用过的返回结构,但我不确定Go编译器是否会检查调用方的上下文并跳过边复制您没有显示如何分配顶点。也许开销实际上是将边缘id分配给源id映射和目标id映射?你要为这10MB创建多少个要分配的对象?@not_a_Golfer谢谢,我编辑了这个问题,现在它包括了我如何创建它们。我看不出有什么问题。这取决于您创建的边的数量和各种ID的大小。您的
顶点中的切片可以快速增长。您可能需要检查它们的容量。如果是这样的话,除了默认的代码>附录< /代码>之外,你可以考虑另一种分配策略。我想你在Go 1.3下尝试过了吗?对于堆栈上的值,GC得到了改进。我这样问是因为您的NewEdge
确实分配了。。然后在调用中丢弃。我已经看到了1.3版本中内存使用量减半的截图——那么不妨试一试?不会受伤吗?谢谢@varyous我不知道这是否解释了所有的问题,但我忽略了基本数学(边的数量),这是我发疯的主要原因。我还需要更深入地挖掘,但这解释了很多问题,而且作为记录,我在几个小时前就发现了这个问题,当时我试图向一个朋友解释这个问题,并开始计算物体的数量。不过,还是要感谢您花时间阅读代码。我很感激。
(pprof) top10
Total: 9.9 MB
8.9 89.9% 89.9% 8.9 89.9% main.(*Graph).NewEdge
0.5 5.0% 95.0% 0.5 5.0% allocg
0.5 5.0% 100.0% 0.5 5.0% fmt.Sprintf
0.0 0.0% 100.0% 0.5 5.0% _rt0_go
0.0 0.0% 100.0% 8.9 89.9% main.fakeGraph
0.0 0.0% 100.0% 0.5 5.0% main.func·003
0.0 0.0% 100.0% 8.9 89.9% main.main
0.0 0.0% 100.0% 0.5 5.0% mcommoninit
(pprof)