在golang程序中查找内存泄漏 - reflect.Value.call的含义

时间:2016-02-18 12:08:35

标签: go memory-leaks

我试图找到一段内存泄漏的代码。

启动新的Web应用程序后,它的大小为6 MB。 在大约12k请求之后,它是28 MB。

我在启动后立即保存了它的堆

curl -s localhost:6060/debug/pprof/heap > ~/debug/heavyHeap/6mb.heap

在12k请求之后:

curl -s localhost:6060/debug/pprof/heap > ~/debug/heavyHeap/28mb.heap

然后我试图看到分配的对象不同:

go tool pprof -alloc_objects -base ~/debug/heavyHeap/6mb.heap $GOPATH/myBin ~/debug/heavyHeap/28mb.heap

运行top命令:

Entering interactive mode (type "help" for commands)
(pprof) top
73949086 of 83397023 total (88.67%)
Dropped 299 nodes (cum <= 416985)
Showing top 10 nodes out of 117 (cum >= 1802254)
      flat  flat%   sum%        cum   cum%
  62308988 74.71% 74.71%   62521981 74.97%  reflect.Value.call
   2413961  2.89% 77.61%    2424884  2.91%  calldb.fromToDiff
   1769493  2.12% 79.73%    3796564  4.55%  gopkg.in/mgo.v2/bson.(*decoder).readElemTo
   1622034  1.94% 81.67%    1622034  1.94%  gopkg.in/mgo.v2/bson.(*decoder).readCStr
   1270739  1.52% 83.20%    1401813  1.68%  reflect.(*structType).FieldByNameFunc
   1130028  1.35% 84.55%    1130028  1.35%  reflect.Value.MapKeys
    933704  1.12% 85.67%     933704  1.12%  gopkg.in/mgo.v2/bson.(*decoder).readStr
    927261  1.11% 86.79%     946166  1.13%  fmt.Sprintf
    819209  0.98% 87.77%    1119590  1.34%  my.AnchorWithClassAndDisabledAndStyle

我列出了最重的项目reflect.Value.call

(pprof) list reflect.Value.call
Total: 83397023
ROUTINE ======================== reflect.Value.call in /usr/local/go/src/reflect/value.go
  62308988   62521981 (flat, cum) 74.97% of Total
         .          .    366:   }
         .          .    367: }
         .          .    368: if !isSlice && t.IsVariadic() {
         .          .    369:   // prepare slice for remaining values
         .          .    370:   m := len(in) - n
         .      81921    371:   slice := MakeSlice(t.In(n), m, m)
         .          .    372:   elem := t.In(n).Elem()
         .          .    373:   for i := 0; i < m; i++ {
         .          .    374:     x := in[n+i]
         .          .    375:     if xt := x.Type(); !xt.AssignableTo(elem) {
         .          .    376:       panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op)
         .          .    377:     }
         .     131072    378:     slice.Index(i).Set(x)
         .          .    379:   }
         .          .    380:   origIn := in
         .          .    381:   in = make([]Value, n+1)
         .          .    382:   copy(in[:n], origIn)
         .          .    383:   in[n] = slice
         .          .    384: }
         .          .    385:
         .          .    386: nin := len(in)
         .          .    387: if nin != t.NumIn() {
         .          .    388:   panic("reflect.Value.Call: wrong argument count")
         .          .    389: }
         .          .    390: nout := t.NumOut()
         .          .    391:
         .          .    392: // Compute frame type, allocate a chunk of memory for frame
         .          .    393: frametype, _, retOffset, _ := funcLayout(t, rcvrtype)
     32769      32769    394: args := unsafe_New(frametype)
         .          .    395: off := uintptr(0)
         .          .    396:
         .          .    397: // Copy inputs into args.
         .          .    398: if rcvrtype != nil {
         .          .    399:   storeRcvr(rcvr, args)
         .          .    400:   off = ptrSize
         .          .    401: }
         .          .    402: for i, v := range in {
         .          .    403:   v.mustBeExported()
         .          .    404:   targ := t.In(i).(*rtype)
         .          .    405:   a := uintptr(targ.align)
         .          .    406:   off = (off + a - 1) &^ (a - 1)
         .          .    407:   n := targ.size
         .          .    408:   addr := unsafe.Pointer(uintptr(args) + off)
         .          .    409:   v = v.assignTo("reflect.Value.Call", targ, addr)
         .          .    410:   if v.flag&flagIndir != 0 {
         .          .    411:     memmove(addr, v.ptr, n)
         .          .    412:   } else {
         .          .    413:     *(*unsafe.Pointer)(addr) = v.ptr
         .          .    414:   }
         .          .    415:   off += n
         .          .    416: }
         .          .    417:
         .          .    418: // Call.
  62243451   62243451    419: call(fn, args, uint32(frametype.size), uint32(retOffset))
         .          .    420:
         .          .    421: // For testing; see TestCallMethodJump.
         .          .    422: if callGC {
         .          .    423:   runtime.GC()
         .          .    424: }
         .          .    425:
         .          .    426: // Copy return values out of args.
     32768      32768    427: ret := make([]Value, nout)
         .          .    428: off = retOffset
         .          .    429: for i := 0; i < nout; i++ {
         .          .    430:   tv := t.Out(i)
         .          .    431:   a := uintptr(tv.Align())
         .          .    432:   off = (off + a - 1) &^ (a - 1)

但是所有这些东西都没有给出我的代码中内存泄漏的线索。

reflect.Value.call实际意味着什么? 我不记得我在代码中使用了reflect.Value.call

1 个答案:

答案 0 :(得分:4)

gopkg.in/mgo.v2/bson使用反射来(联合国)编组BSON。但是由于问题11786,在配置文件中未显示泄漏内存的实际方法。

解决方法是将标记-runtime传递给go tool pprof