Использование пользовательского распределителя Lua для подсчета использования памяти, но его результат отличается от collectgarbage('count')

Недавно я пытался отследить использование памяти lua в нашем проекте, и мне пришла в голову идея использовать для этой задачи настраиваемый распределитель lua_Alloc. Что ж, код распределителя выглядит просто и работает нормально.

Но вскоре эта небольшая функция столкнулась с двумя проблемами:
1. результат, который она выводит, сильно отличается от значения, полученного с помощью collectgarbage('count');
2. Предположим, что текущее использование памяти составляет M байт, тогда мы вводим некоторые нулевые ссылки и вызов gc, использование памяти окажется больше, чем M байт. например: возврат A, возврат B, возврат C,..., collectgarbage()

Ну, я слышал, что если вы правильно используете lua, утечки памяти не будет, поэтому я предполагаю, что я сделал что-то неправильно при подсчете использования памяти. Пожалуйста, помогите мне разобраться. Заранее спасибо.

Компилируемый код прилагается ниже:

extern "C"
{
#include "lua.h"
#include <lauxlib.h>
#include <lualib.h>
};

#include <cstdio>
#include <string>
#include <cstdlib>

using namespace std;

struct Tracker 
{
    size_t m_usage;
}g_tracker;

void*
Allocator(void* ud, void* ptr, size_t osize, size_t nsize)
{
    Tracker* pTracker = (Tracker*)ud;
    void* pRet = NULL;
    if( nsize == 0 )
    {
        pTracker->m_usage -= osize;
        //printf("Free %d bytes; ", osize);
        free(ptr);
    }
    else
    {
        pTracker->m_usage -= osize;
        //printf("first Free %d bytes; ", osize);
        pTracker->m_usage += nsize;
        //printf("then alloc %d bytes; ", nsize);
        pRet = realloc(ptr, nsize);
    }

    //printf("current usage: %d bytes\n", pTracker->m_usage);
    return pRet;
}

int main()
{
    lua_State* L = lua_newstate(Allocator, &g_tracker );
    luaL_openlibs(L);

    char buf[4096];
    while(true)
    {
        fgets(buf, 4096, stdin);                        

        if( strlen(buf) > 1 && 0 != luaL_dostring(L, buf) )
        {
            const char* errmsg = lua_tostring(L, -1);
            printf("%s\n", errmsg);
        }

        printf("current usage: %d bytes \n", g_tracker.m_usage);
    }
}

входная последовательность о #2:

press enter
current usage: 18867 bytes
a=nil; b=nil; c=nil;
current usage: 19311 bytes
collectgarbage()
current usage: 18900 bytes
d=nil; e=nil; f=nil;
current usage: 19345 bytes
collectgarbage()
current usage: 18900 bytes
collectgarbage()
current usage: 18900 bytes  
for a = 1, 1000, 1 do b = nil end; collectgarbage()
current usage: 19206 bytes
collectgarbage()
current usage: 18900 bytes
for i = 1, 1000, 1 do X = {}; for j = 1, 1000, 1 do X[j] = j end ; X = nil; collectgarbage() ; end
current usage: 19391 bytes
collectgarbage()
current usage: 18900 bytes

person zaexage    schedule 15.04.2012    source источник
comment
результат, который он выводит, сильно отличается от значения, заданного collectgarbage('count'); Насколько он отличается? Постоянно больше, постоянно меньше и т.д.? И не забывайте: значение, которое вы получаете, находится в кило байтах.   -  person Nicol Bolas    schedule 15.04.2012
comment
Спасибо за ответ. чтобы быть конкретным, collectgarbage('count') всегда возвращает меньшее число, чем распределитель. Однако в этой небольшой программе это будут сотни байтов.   -  person zaexage    schedule 16.04.2012
comment
О, я отредактировал ветку и разместил наименьший полный код, чтобы воспроизвести обстоятельства   -  person zaexage    schedule 16.04.2012


Ответы (1)


Как указано в документации Lua, collectgarbage('collect') возвращает значение в килобайтах. 1 КБ == 1024 байта. Поэтому, когда collectgarbage говорит, что вы используете 19,4775390625 КБ, это соответствует 19945 байтам. Что точно равно одному из многих значений, полученных вашим распределителем.

Ваша функция проверки памяти всегда будет соответствовать тому, что Lua получает ровно в один момент: момент, когда collectgarbage возвращается. Любые выделения/освобождения Lua, которые происходят до или после него (например, необходимые для вызова print для отображения значения), сбивают ваш счет.

Короче говоря, вы ожидаете точности в байтах, когда такая точность просто невозможна. По крайней мере, не с кодом, который у вас есть.

В общем, не стоит об этом беспокоиться.

person Nicol Bolas    schedule 16.04.2012
comment
Я внес несколько изменений в код и провел еще несколько тестов, думаю, ваш ответ хорошо объясняет № 1; Что касается № 2 (я разместил для этого группу входных последовательностей), хотя между начальным значением и промежуточным значением есть некоторая разница, я думаю, что этот распределитель все же можно использовать в качестве метода проверки наличия утечки. - person zaexage; 16.04.2012