app engine(python)如何跨请求管理内存(超出软私有内存限制)

时间:2015-10-06 22:55:22

标签: python google-app-engine memory google-app-engine-python

我在应用引擎中的各种请求处理程序中偶尔遇到Exceeded soft private memory limit错误。我知道这个错误意味着实例使用的RAM超过了分配的数量,以及它如何导致实例关闭。

我想了解可能导致错误的原因,并且首先,我想了解app引擎python实例应该如何管理内存。我的基本假设是:

  1. F2实例以256 MB
  2. 开头
  3. 启动时,它会加载我的应用程序代码 - 比方说30 MB
  4. 当它处理请求时,它有226 MB可用
    • 只要该请求不超过226 MB(+误差范围),请求就完成无错误
    • 如果它确实超过226 MB +保证金,则实例完成请求,记录超出的软私有内存限制'错误,然后终止 - 现在回到第1步
  5. 当该请求返回时,它释放的任何内存都被释放 - 即。未使用的RAM回到226 MB
  6. 对于传递给实例的每个请求,无限期地重复步骤3-4
  7. 如何推测它会起作用,但鉴于我偶尔会 / em>请求处理程序,我现在不太确定。我的问题是:

    a)第4步会发生吗?

    b)什么可能导致它不发生?还是不完全发生?例如请求之间的内存如何泄漏?

    c)模块级变量中的存储是否会导致内存使用泄漏? (我不是故意以这种方式使用模块级变量)

    d)我可以使用哪些工具/技术来获取更多数据?例如。测量请求处理程序的入口处的内存使用情况?

    在答案/评论中,如果可能,请链接到gae文档。

    [edit]额外信息:我的应用被归为threadsafe: false。如果这与答案有关,请说明它是什么。我打算很快换到threadsafe: true

    [edit] 澄清:此问题是关于内存管理的gae的预期行为。因此,像“gc.collect()'可能是相关问题的部分解决方案,他们没有完全回答这个问题。直到我理解gae预期会如何表现为止,使用gc.collect()对我来说就像伏都教编程一样。

    最后:如果我把这一切都倒退了,那么我会提前道歉 - 我真的找不到有用的信息,所以我大多猜测......

3 个答案:

答案 0 :(得分:3)

与任何其他标准Python解释器相比,App Engine的Python解释器在内存管理方面没有什么特别之处。所以,特别是,每个请求都没有特别的事情,例如你的假设步骤4.相反,只要任何对象的引用计数减少到零,Python解释器就会回收内存(模块gc仅用于处理垃圾周期 - 当一堆对象永远不会将其引用计数降低到零,因为它们相互引用即使没有可访问外部引用它们。)

因此,记忆很容易泄漏" (在实践中,虽然技术上它不是泄漏)"请求之间"如果你使用任何全局变量 - 所述变量将在处理程序类的实例及其(例如)get方法中存活 - 即你的点(c),尽管你说你没有这样做。

将模块声明为threadsafe后,实例可能会同时为多个请求提供服务(直到您在max_concurrent_requests部分automatic_scaling部分设置为.yaml您的模块的instance_class配置文件;默认值为8)。因此,您的实例的RAM需要是每个请求所需的倍数。

至于(d),到"获得更多数据" (我想你实际上意味着,获得更多内存),你唯一能做的就是为需要内存的模块配置一个更大的ndb

使用更少的内存,有许多技术 - 与App Engine无关,与Python有关,特别是与您的特定代码及其相关的一切非常具体的需求。

我能想到的一个GAE特定问题是ndb的缓存已被报告泄露 - 请参阅https://code.google.com/p/googleappengine/issues/detail?id=9610;该线程还建议了解决方法,例如关闭db缓存或移动到旧ndb(没有缓存且没有泄漏)。如果你正在使用var deck = [ {card: "Two of Clubs", value: 2}, {card: "Three of Clubs", value: 3}, {card: "Four of Clubs", value: 4}, {card: "Five of Clubs", value: 5}, {card: "Six of Clubs", value: 6}, {card: "Seven of Clubs", value: 7}, {card: "Eight of Clubs", value: 8}, {card: "Nine of Clubs", value: 9}, {card: "Ten of Clubs", value: 10}, {card: "Jack of Clubs", value: 10}, {card: "Queen of Clubs", value: 10}, {card: "King of Clubs", value: 10}, {card: "Ace of Clubs", value: 11}, {card: "Two of Hearts", value: 2}, {card: "Three of Hearts", value: 3}, {card: "Four of Hearts", value: 4}, {card: "Five of Hearts", value: 5}, {card: "Six of Hearts", value: 6}, {card: "Seven of Hearts", value: 7}, {card: "Eight of Hearts", value: 8}, {card: "Nine of Hearts", value: 9}, {card: "Ten of Hearts", value: 10}, {card: "Jack of Hearts", value: 10}, {card: "Queen of Hearts", value: 10}, {card: "King of Hearts", value: 10}, {card: "Ace of Hearts", value: 11}, {card: "Two of Spades", value: 2}, {card: "Three of Spades", value: 3}, {card: "Four of Spades", value: 4}, {card: "Five of Spades", value: 5}, {card: "Six of Spades", value: 6}, {card: "Seven of Spades", value: 7}, {card: "Eight of Spades", value: 8}, {card: "Nine of Spades", value: 9}, {card: "Ten of Spades", value: 10}, {card: "Jack of Spades", value: 10}, {card: "Queen of Spades", value: 10}, {card: "King of Spades", value: 10}, {card: "Ace of Spades", value: 11}, {card: "Two of Diamonds", value: 2}, {card: "Three of Diamonds", value: 3}, {card: "Four of Diamonds", value: 4}, {card: "Five of Diamonds", value: 5}, {card: "Six of Diamonds", value: 6}, {card: "Seven of Diamonds", value: 7}, {card: "Eight of Diamonds", value: 8}, {card: "Nine of Diamonds", value: 9}, {card: "Ten of Diamonds", value: 10}, {card: "Jack of Diamonds", value: 10}, {card: "Queen of Diamonds", value: 10}, {card: "King of Diamonds", value: 10}, {card: "Ace of Diamonds", value: 11} ] function draw(i) { var drawn = i[Math.floor(Math.random()*i.length)]; // console.log(drawn); return drawn; // you need to return something!!! } var playerCard1 = draw(deck); var dealerCard1 = draw(deck); var playerCard2 = draw(deck); var dealerCard2 = draw(deck); var player = playerCard1.value + playerCard2.value; console.log(player); 并且没有关闭其缓存,那可能是"内存泄漏的根本原因"你正在观察的问题。

答案 1 :(得分:2)

第4点是一个无效的假设,Python的垃圾收集器并不容易返回内存,Python的程序占用了那个内存,但它直到垃圾收集器才被使用有通行证。与此同时,如果某些其他请求需要更多内存 - 可能会在第一个请求的内存之上分配新内存。如果要强制Python进行垃圾回收,可以使用gc.collect()

提到的targetlist = { "server1:/var/www/wiki/", "server2:/var/www/wiki/" } for _, server in ipairs( targetlist ) do sync{ default.rsync, source="/var/www/wiki/", target=server } end

答案 2 :(得分:2)

请查看此Q& A,了解检查垃圾收集的方法以及可能的替代解释:Google App Engine DB Query Memory Usage