dty98339 2013-09-16 11:20
浏览 54
已采纳

Google App Engine Go内存管理

I've run into a problem recently where App Engine would terminate my Go instance because it says it ran out of memory. The memory limit for the instance is set to 128Mb.

However, I have trouble figuring out where all that memory is being allocated. When I run the following code:

var s runtime.MemStats
runtime.ReadMemStats(&s)
c.Debugf("allocated memory: %d", s.Alloc)

it shows me that when allocated memory reaches about 39-40Mb, my app gets terminated with the following error:

Exceeded soft private memory limit with 135.082 MB after servicing 1 requests total

Similarly, when runtime.ReadMemStats(&s) indicates that I'm using 20 Mb, the App Engine console shows that my instance is using 92Mb. Redoing the same request, runtime.ReadMemStats(&s) still shows 20Mb, while the App Engine console shows 119Mb.

I have disabled appstats, still no help.

Most of my memory is used up by a memory cache, which I could reduce to fit into the constraints (or increase the memory limit on my instance), but I'd like to know where all that memory is being used. If someone could shed some light on that, or how to correctly profile memory usage on App Engine, that would help a lot.

Update: managed to reproduce this locally.

The following is a sample application that allocates some integers in one request and garbage collects them in the next request:

// Package test implements a simple memory test for Google App Engine.
package test

import (
    "net/http"
    "runtime"

    "appengine"
)

var buffer []int64

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    var s runtime.MemStats
    c := appengine.NewContext(r)
    if len(buffer) == 0 {
        // Allocate 2^22 integers.
        runtime.ReadMemStats(&s)
        c.Debugf("Memory usage: %d bytes (%d system).", s.Alloc, s.Sys)
        buffer = make([]int64, 4*1024*1024)
        for i, _ := range buffer {
            buffer[i] = int64(i*i)
        }
        runtime.ReadMemStats(&s)
        c.Debugf("Memory usage increased to: %d bytes (%d system).", s.Alloc, s.Sys)
    } else {
        // Remove all references to the slice pointed to by buffer.
        // This should mark it for garbage collection.
        runtime.ReadMemStats(&s)
        c.Debugf("Memory usage: %d bytes (%d system).", s.Alloc, s.Sys)
        buffer = nil
        runtime.GC()
        runtime.ReadMemStats(&s)
        c.Debugf("After GC event: %d bytes (%d system).", s.Alloc, s.Sys)
    }
    w.WriteHeader(http.StatusTeapot)
}

When run using the development server:

$ ./go_appengine/dev_appserver.py test

2013/09/16 12:28:28 DEBUG: Memory usage: 833096 bytes (272681032 system).
2013/09/16 12:28:28 DEBUG: Memory usage increased to: 34335216 bytes (308332616 system).
INFO     2013-09-16 12:28:28,884 module.py:593] default: "GET / HTTP/1.1" 418 -
2013/09/16 12:28:29 DEBUG: Memory usage: 34345896 bytes (308332616 system).
2013/09/16 12:28:29 DEBUG: After GC event: 781504 bytes (308332616 system).
INFO     2013-09-16 12:28:29,560 module.py:593] default: "GET / HTTP/1.1" 418 -
2013/09/16 12:28:30 DEBUG: Memory usage: 791616 bytes (308332616 system).
2013/09/16 12:28:30 DEBUG: Memory usage increased to: 34337392 bytes (308332616 system).
INFO     2013-09-16 12:28:30,276 module.py:593] default: "GET / HTTP/1.1" 418 -
2013/09/16 12:28:36 DEBUG: Memory usage: 34347536 bytes (308332616 system).
2013/09/16 12:28:36 DEBUG: After GC event: 783632 bytes (308332616 system).
INFO     2013-09-16 12:28:36,224 module.py:593] default: "GET / HTTP/1.1" 418 -

It appears that memory allocation and garbage collection works fine. However, looking at ps output, it seems that deallocating memory does not decrease the virtual memory usage of the process:

$ ps axo command,vsize,rss | ag go_app
/usr/bin/python2.7 ./go_app 381248 56608
$ ps axo command,vsize,rss | ag go_app
/usr/bin/python2.7 ./go_app 676324 57652
$ ps axo command,vsize,rss | ag go_app
/usr/bin/python2.7 ./go_app 750056 57856
$ ps axo command,vsize,rss | ag go_app
/usr/bin/python2.7 ./go_app 750056 57856

It seems that the Python process that runs the underlying Go instance keeps growing its virtual memory, but it never gets freed. It also seems that a similar thing happens on the production server: the allocated memory reported by the instance runtime is different than the used memory reported by the kernel.

As suggested by @Kluyg, it seems that the admin console shows the system allocated memory, which makes sense.

  • 写回答

1条回答 默认 最新

  • dsv73806 2013-09-16 16:15
    关注

    According to the docs Alloc field shows bytes allocated and still in use. In garbage-collected language, however, when memory is released by GC, it doesn't immediately returned to the system (it can be requested again soon, so why bother returning it back?). So what you really need to monitor is Sys field which counts bytes obtained from the system. You may be interested in this article with some insights how to minimise memory usage.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥15 绘制多分类任务的roc曲线时只画出了一类的roc,其它的auc显示为nan
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?