<p>假设您使用的是顶部的“VIRT”列,则无法从该数字推断出python对象的数量正在增长,或者至少增长到足以解释虚拟地址空间的总大小</p>
<p>例如,您知道python在其线程代码下面使用pthreads吗?这一点很重要,因为pthreads采用“ulmimit-s”*1K中的值作为默认堆栈大小。因此,python中任何新线程的默认堆栈大小在Linux的某些变体上通常为8MB甚至40MB,除非您在进程的父进程中显式更改“ulimit-s”值。许多服务器应用程序都是多线程的,因此即使有两个额外的线程,也会产生比Pympler输出中显示的更多的虚拟内存。您必须知道进程中有多少线程以及默认堆栈大小,才能了解线程对总VIRT值的贡献</p>
<p>此外,在其自身的分配机制下,python混合使用mmap和malloc。在malloc的情况下,如果程序是多线程的,那么在linux libc上malloc将使用多个arena,一次保留64MB的范围(称为堆),但只使这些堆的开头可读写,并在需要内存之前使这些范围的尾部不可访问。这些不可访问的尾部通常很大,但就进程的提交内存而言,实际上并不重要,因为不可访问的页面不需要任何物理内存或交换空间。尽管如此,“VIRT”中的top计数仍然包含整个范围,包括范围开始时的可访问开始和范围结束时的不可访问开始</p>
<P>例如,考虑一个相当小的Python程序,其中主THTEAD启动16个附加线程,每个线程在Python分配中使用的内存不多:</P>
<pre><code>import threading
def spin(seed):
l = [ i * seed for i in range(64) ]
while True:
l = [ i * i % 97 for i in l ]
for i in range(16):
t = threading.Thread(target=spin, args=[i])
t.start()
</code></pre>
<p>我们不希望该程序会产生这么大的一个过程,但下面是我们在顶部看到的,只看一个过程,它显示了超过1GB的VIRT:</p>
<pre><code>Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
Cpu(s): 1.0%us, 1.9%sy, 3.3%ni, 93.3%id, 0.5%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 264401648k total, 250450412k used, 13951236k free, 1326116k buffers
Swap: 69205496k total, 17321340k used, 51884156k free, 104421676k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9144 tim 20 0 1787m 105m 99m S 101.8 0.0 13:40.49 python
</code></pre>
<p>我们可以通过获取运行程序的活动内核(例如使用gcore)并使用chap打开生成的内核、可在<a href="https://github.com/vmware/chap" rel="nofollow noreferrer">https://github.com/vmware/chap</a>找到的开源和运行如下所示的命令来理解此类程序(以及您的程序)的高VIRT值:</p>
<pre><code>chap> summarize writable
17 ranges take 0x2801c000 bytes for use: stack
14 ranges take 0x380000 bytes for use: python arena
16 ranges take 0x210000 bytes for use: libc malloc heap
1 ranges take 0x1a5000 bytes for use: libc malloc main arena pages
11 ranges take 0xa9000 bytes for use: used by module
1 ranges take 0x31000 bytes for use: libc malloc mmapped allocation
2 ranges take 0x7000 bytes for use: unknown
62 writable ranges use 0x28832000 (679,682,048) bytes.
</code></pre>
<p>从上面我们可以看到,最大的单次内存使用是17个堆栈。如果我们使用“descripe writable”,我们会看到其中16个堆栈每个占用40MB,这都是因为我们忽略了显式地将堆栈大小调整为更合理的大小</p>
<p>我们可以对不可访问(不可读、不可写或不可执行)区域执行类似的操作,并看到16个“libc malloc heap tail reservation”范围占用了几乎1GB的VIRT。事实证明,这对进程的承诺内存来说并不重要,但它确实对VIRT数做出了相当可怕的贡献</p>
<pre><code>chap> summarize inaccessible
16 ranges take 0x3fdf0000 bytes for use: libc malloc heap tail reservation
11 ranges take 0x15f9000 bytes for use: module alignment gap
16 ranges take 0x10000 bytes for use: stack overflow guard
43 inaccessible ranges use 0x413f9000 (1,094,684,672) bytes.
</code></pre>
<p>有16个这样的范围的原因是每个旋转线程都在重复分配,这导致libc malloc在后台运行,因为它被python分配器使用,从而划分出16个领域</p>
<p>您可以对只读内存执行类似的命令(“summary readonly”),也可以通过将“summary”更改为“description”来获取任何命令的更多详细信息</p>
<p>我不能确切地说,当您在自己的服务器上运行它时,您会发现什么,但REST服务器似乎很可能是多线程的,所以我猜这将是TOP向您展示的数字的一个非常重要的贡献</p>
<p>这仍然不能解释为什么这个数字还在继续攀升,但如果你看看这些数字,你就能知道下一步该往哪里看。例如,在上面的摘要中,python在不使用mmap的情况下处理的分配不会占用3.5MB的内存:</p>
^{pr5}$
<p>在您的情况下,数字可能会更大,因此您可以尝试以下任一方法:</p>
<pre><code>describe used
describe free
summarize used
summarize free
</code></pre>
<p>请注意,上述命令还将涵盖本机分配(例如,由共享库完成的分配)以及python分配</p>
<p>在你自己的程序中遵循类似的步骤应该会让你更好地理解你看到的那些“顶级”数字</p>