netch80: (Default)
[personal profile] netch80
Окружение: Linux/x86-64/glibc

I. Питон.

Берём небольшую тестовую программку:


import os, gc
def ps():
    os.system("ps -p %s -o vsz,rss | tail -n +2" % (os.getpid(),))
class A:
    pass
ps()
a = [A() for x in range(0,3000000)]
ps()
del a; gc.collect()
ps()


Если её запустить под 2.7 в описанном виде, получаем:

 20996  4456
1182596 1161900
936816 920336


Сделав одно небольшое изменение - A наследуется от object (и становится new style class) - получаем:

 20996  4452
314112 293528
 93032 76660


Формально new style легче буквально на десяток байт, но меняется что-то другое. Теперь запустим вообще под python3:

 28872  6956
249692 224164
 28872  7196


Раньше мы меряли потребление памяти тяжёлым процессом по пиковым нагрузкам, но это, как видно, уже неадекватный результат.
Кстати, окончательное освобождение из-под объектов происходит именно при gc.collect(). До этого чистится чуть-чуть (насколько я понял, собственно список, но не объекты).

Но!!! Как только мы создаём не пустой объект, а с реальным содержимым, память перестаёт освобождаться по удалению объектов:(

II. Уже Си (чтобы избавиться от возможных эффектов Питона). Тестовая программа порождает 50 тредов, после каждых 10 рассказывая объём процесса, каждый тред делает malloc(1) и уходит в вечный цикл сна. Запускаем... ой...

  6120   372
743572  2640
1480892 6736
2218212 8972
2365708 13068
2447668 13068


64MB на тред не включая 8MB стека до какого-то порога - это пять. косяков.
Теория см. здесь. И точно:

$ env MALLOC_ARENA_MAX=1 ./t1
  6120   372
612500  4688
694460  4688
776420  4868
858380  4868
940340  5060


А всего-то хотелось соотнести объём процесса с нагрузкой и придумать, как определять, что с ним что-то не так (например. забытый, но не удалённый и не отцеплённый, мусор в памяти). Но, видимо, просто не решится.

Date: 2012-06-14 06:18 am (UTC)
ext_605364: geg MOPO4 (Default)
From: [identity profile] gegmopo4.livejournal.com
Из документации по gc.collect:
The free lists maintained for a number of built-in types are cleared whenever a full collection or collection of the highest generation (2) is run.

Кстати, на 32-бит разницы по памяти после освобождения между old style class и new style class (в 2.7) практически нет.

Date: 2012-06-14 08:51 am (UTC)
From: [identity profile] gul-kiev.livejournal.com
А почему vss беспокоит?
Пусть хоть терабайт запрашивает - реально-то оно выделится только при использовании.
Размер vss одного процесса вполне может превышать общее количество имеющейся виртуальной памяти в системе, и я перестал понимать, какой смысл смотреть на этот параметр.

Date: 2012-06-14 09:09 am (UTC)
From: [identity profile] stepancheg.livejournal.com
В линуксах ещё бывает vm.overcommit_ratio (http://www.win.tue.nl/~aeb/linux/lk/lk-9.html).

Date: 2012-06-14 09:28 am (UTC)
From: [identity profile] netch80.livejournal.com
> А почему vss беспокоит?

За неимением лучшего.
Тот показатель, который бы реально что-то значил - а именно, объём страниц, требующих backing store в свопе - отсутствует или его получить слишком сложно.
По факту есть два ничего не значащих показателя: VSZ и RSS. И надо делать какой-то вывод о состоянии процесса, наблюдая оба и делая суммарный вывод определения погоды на Земпе по среднему между Юпитером (в окрестностях Большого Красного Пятна) и Венерой.

Date: 2012-06-14 11:07 am (UTC)
ext_605364: geg MOPO4 (Default)
From: [identity profile] gegmopo4.livejournal.com
А в /proc/[pid]/stat нет? Или в /proc/[pid]/smaps?

Date: 2012-06-14 01:41 pm (UTC)
From: [identity profile] netch80.livejournal.com
О! smaps я и не заметил.


#!/usr/bin/env python

import sys

if __name__ == '__main__':
    xpid = sys.argv[1]
    f = open('/proc/%s/smaps' % (xpid,), 'r')
    sum = 0
    for line in f:
        if line.find('_Dirty:') == -1:
            continue
        s1 = int(line.split()[1])
        sum += s1
    f.close()
    print 'sum_dirty =', sum

Date: 2012-06-14 02:00 pm (UTC)
ext_605364: geg MOPO4 (Default)
From: [identity profile] gegmopo4.livejournal.com
Да, это я и имел в виду.

Можно короче:
sum(int(line.split()[1]) for line in open('/proc/%s/smaps' % (xpid,)) if '_Dirty:' in line)

Или на шелле:
grep _Dirty: /proc/$1/smaps | numsum -c | cut -d' ' -f2

Profile

netch80: (Default)
netch80

January 2026

S M T W T F S
    1 23
45678910
11121314151617
18192021222324
25262728293031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 2nd, 2026 02:43 pm
Powered by Dreamwidth Studios