Резервирую под изображение память (от 100 кб до 200 Мб), через new[_datalen_].
После получения картинки идёт обработка и сохранение, а потом delete[]. Тут ничего особого.
Если производить данную процедуру один раз - странностей нет.
Однако при повторном выделении\освобождении, память остаётся занятой программой.
Например:
В 1-ом столбце указывается сколько занимает прога, во 2-ом - размер принятой картинки, далее опять в 1-ом столбце указывается сколько занимает прога, но уже после освобождения памяти для картинки.
(Мб)
Программа, Изображение
1,5
30
1,7
33,8
1,8
61,5
1,9
1,5
2,9
15,3
17,0
Видно, что если начать принимать изображения с меньшим размером, чем были до этого, то память перестаёт освобождаться. Думается, что это наподобие кеширования что-то, или особенности работы менеджера памяти. х3. Что это такое может быть и как это исправить? подразумевается, что прога будет в трее висеть, но с набалдашниками в десятки метров - перебор.
KSysGuard
(В Gnome System Monitor почти тоже самое) smile
AndreyMust19 написал:
Исходники покажите, без них - стрельба семечками по воробьям.
Упрощенный путь массива с данными изображения:
text
...
unsigned char* csl_imagedata; // объявление
...
//начало рабочего цикла программы
csl_imagedata = new unsigned char[size];
int offset = 0
while (принимать данные)
{
// приём данных с утройства
libusb_bulk_transfer (scnHandle, _epaddr, &(csl_imagedata[offset]), *datalength, &transferred, 3000);
offset += transferred; //transferred - сколько данных принято. но с этим всё нормально
}
delete[] csl_imagedata;
//конец рабочего цикла программы
Выделение памяти и приём данных идёт в другом потоке. Очистка вызывается в основном. Хотя это наврятли влияет, т.к. память всё-таки освобождается
Какую память смотрите? Вам нужна резидентная память, а не просто память.
Не думаю, что проблема какая-то страшная, зависит от того, какие файлы вы подаете программе на обработку. И про дисковый кеш не забудьте.
Исходники большие? Если нет, выложите полностью все. И приложите исполняемый файл (в случае если скомпилировать исходники будет проблематично).
Какую память смотрите? Вам нужна резидентная память, а не просто память.
Смотрю столбец VmRss - весь занимаемый объём физической памяти. Кажись это и есть резидентная.
AndreyMust19 написал:
Не думаю, что проблема какая-то страшная, зависит от того, какие файлы вы подаете программе на обработку. И про дисковый кеш не забудьте.
Исходники большие? Если нет, выложите полностью все. И приложите исполняемый файл (в случае если скомпилировать исходники будет проблематично).
Прога берёт данные со сканера, поэтому наврятли получится опробовать её. Если только написать нечто вроде эмуляции, но что-то желания делать это нет.
Исходники относительно большие. Вот куски, которые могут быть интересны, в отношении проблемы:
text
int TcanonScanLide70::cleardata()
{
pthread_mutex_lock(&imagescan_mutex);
int complete = complete_thread;
pthread_mutex_unlock(&imagescan_mutex);
cout << "call data clear...";
if (not csl_imageempty)
{
if (complete != 0)
{
printf("cleaned at ptr = %p\n",csl_imagedata);
delete[] csl_imagedata;
csl_imagedata = NULL;
csl_imageempty = true;
imagewidth_req = 0;
csl_datalength = 0;
csl_imagewidth = 0;
csl_imageheight = 0;
imagebpp = 0;
return 0;
}
else
{
cout << "not allow, scanner is reading\n";
return -2;
}
}
else
{
cout << "there no data\n";
return -1;
}
};
...
int TcanonScanLide70::allocimgdata(int size)
{
csl_imagedata = new unsigned char[size];
csl_imageempty = false;
printf("alloc image data, length = %d bytes at ptr = %p\n",size,csl_imagedata);
};
...
int TcanonScanLide70::doscan(int dpi,int bpp, int x,int y,int w,int h)
{
if (readbuttons() > -1)
{
pthread_mutex_lock(&imagescan_mutex);
int complete = complete_thread;
pthread_mutex_unlock(&imagescan_mutex);
if ((complete == 5) or (complete == 16))
{
if (x+w > maximagewidth(dpi))
{
cout << "Can`t start scan. Too high image width: " << x << "(x) + " << w << "(w) = " << x+w << " > " << maximagewidth(dpi) << "\n";
return -2;
}
if (y+h > maximageheight(dpi))
{
cout << "Can`t start scan. Too high image height: " << y << "(y) + " << h << "(h) = " << y+h << " > " << maximageheight(dpi) << "\n";
return -3;
}
if (x < 0)
{
cout << "Can`t start scan. X offset less zero\n";
return -4;
}
if (y < 0)
{
cout << "Can`t start scan. Y offset less zero\n";
return -5;
}
if (w < 1)
{
cout << "Can`t start scan. Image width less 1\n";
return -6;
}
if (h < 1)
{
cout << "Can`t start scan. Image height less 1\n";
return -7;
}
cout << "\nstart scanning\n\n";
cleardata();
int realw = ((w * 600 / dpi ) | 0x7f) * dpi / 600 + 1;
int memalloc = realw*h*bpp/8;
if (memalloc < 2000000) memalloc = 2000000;
allocimgdata(memalloc);
imagewidth_req = w;
imageheight_req = h;
// ФУНКЦИЯ В КОТОРУЮ ПЕРЕДАЁТСЯ csl_imagedata, как параметр data.
int TcanonScanLide70::bulkrun(unsigned char _epaddr, unsigned char *_data,int *_datalength, int _delay,int i)
{
int transferred;
int res;
res = libusb_bulk_transfer (TcanonScanLide70::TcanonScanLide70::scnHandle, _epaddr, _data, *_datalength, &transferred, 3000);
if (_epaddr == 2) *_datalength = transferred;
switch (res)
{
case 0 :
break;
case LIBUSB_ERROR_TIMEOUT:
printf("%d - error: LIBUSB_ERROR_TIMEOUT (the transfer timed out)",i);
libusb_clear_halt(TcanonScanLide70::scnHandle,_epaddr);
break;
case LIBUSB_ERROR_PIPE:
printf("%d - error: LIBUSB_ERROR_PIPE (the endpoint halted)",i);
break;
case LIBUSB_ERROR_OVERFLOW:
printf("%d - error: LIBUSB_ERROR_OVERFLOW (the device offered more data)",i);
break;
case LIBUSB_ERROR_NO_DEVICE:
printf("%d - error: LIBUSB_ERROR_NO_DEVICE (the device has been disconnected)",i);
break;
default:
printf("%d - error: LIBUSB ERROR = %d",i,res);
break;
}
usleep(_delay);
return res;
};
Вот лог который прога выдает - насчёт того что память должна нормально освобождаться.
text
...CanoScan Lide 70 init start
iProduct (?CanoScan)= CanoScan
NumConfigurations (?1)= 1
scanner configured, Active config value = 1
MaxPower for active config (hex) = FA, (DEC) (?250)= 250
NumInterfaces for active config (?1)= 1
num_altsetting for interface 0 (?1)= 1
NumEndpoints (?2)= 2
EndPoint 1 address (?2 out EP-2)= 2
EndPoint 2 address (?83 in EP-3)= 83
...CanoScan Lide 70 init finish
alloc image data, length = 230686720 bytes at ptr = 0x7fe5b04fb010
call data clear...cleaned at ptr = 0x7fe5b04fb010
Initialization
start calibration
call data clear...there no data
alloc image data, length = 2097152 bytes at ptr = 0x7fe5bdefb010
startmotor 15 93
startmotor 5 93
startmotor 5 d1
read scanner RAM
work with data at ptr = 0x7fe5bdefb010
reading data (total to read = 0):'
get 1959936 bytes; finish
Rollback
startmotor 5 35
call data clear...cleaned at ptr = 0x7fe5bdefb010
call data clear...there no data
DPI (75,150,300,600): 600
BPP (24,48): 24
X offset: (0..5103): 0
Width: (1..5104): 5000
Y offset: (0..7001): 0
Height: (1..7002): 2000
start scanning
call data clear...there no data
alloc image data, length = 30720000 bytes at ptr = 0x7fe5bc3af010
scan with params:
DPI: 600, BPP: 24, L: 0069, Y: 0126, R: 1468, H: 1770
startmotor 15 93
startmotor 5 93
startmotor 15 d1
read scanner RAM
work with data at ptr = 0x7fe5bc3af010
reading data (total to read = 384):`
get 979968 bytes; finish
next RAM read - image
startmotor 5 91
read scanner RAM
work with data at ptr = 0x7fe5bc3af010
reading data (total to read = 30726000):'
get 30720000 bytes; finish
Rollback
call data clear...cleaned at ptr = 0x7fe5bc3af010
wait...
startmotor 5 35
finish scanning
want new scan? (y,s - scan, c - calibration): n
Attemp to close scanner connection
call data clear...there no data
Scanner connection closed
Читал инет, говорят, что может быть фрагментация. Хотя всё равно не понимаю, почему именно в таком виде - т.е. когда выделяется память меньшего объема, чем в предыдущий раз.
ЗЫ. Когда производится калибровка, то сканер выдает 2 Мб данных (в массив изображения). После первой калибровки данные освобождаются. После второй нет (прога начинает занимать 2 метра с копейками физической памяти). Однако после 3,4 и т.п. калибровки память больше ни кушается - т.е. так и остаются висеть 2 Мб в проге.
Удалось частично решить проблему, за счёт выделения каждый раз по 220Мб (маскимально возможный объем изображения), вне зависимости от того какой объём реально потребуется для картинки. При этом физическая память потребляется как следует - выделяется столько, сколь данных пришло, а в виртуальной, как впрочем следовало, выделилось 220 Мб. После освобождения прога нагружается на несколько десятков килобайт - по видимому это связано с хранением виртуальных адресов. Одно сканирование 600dpi 24bpp добавляет около 100кб к занимаемой физической памяти. что в общем-то, не очень страшно, хотя избавится-бы от этого хотелось. Но это уже, как понимаю, надо давать какие-то указания ядру.
ЗЫ. Когда производится калибровка, то сканер выдает 2 Мб данных (в массив изображения). После первой калибровки данные освобождаются. После второй нет (прога начинает занимать 2 метра с копейками физической памяти). Однако после 3,4 и т.п. калибровки память больше ни кушается - т.е. так и остаются висеть 2 Мб в проге.
Возможно, что создаваемые потоки освобождают свою память, а вот сама ф-я doscan освободить эти 2 МБ забывает после 1-ой калибровки, отсюда они и остаются в памяти. К сожалению, функции scanthread и doscanthread вы не показываете, поэтому на их счет ничего сказать не могу.
Уже это исправил. Действительно оказалось, что new[] не потокобезопасен и теперь выделение и освобождение памяти идёт в основном потоке. Однако проблема оставалась.
В функцию выделения памяти добавил строку, которая игнорирует передаваемое значение size и ставит его в 220 метров
text
int TcanonScanLide70::allocimgdata(int size)
{
size = 220*1024*1024;
csl_imagedata = new unsigned char[size];
csl_imageempty = false;
printf("alloc image data, length = %d bytes at ptr = %p\n",size,csl_imagedata);
};
Результат - всё освобождается. Занимаемая виртуальная память растёт по мере её потреблений вплоть до 220 Мб. (т.е. если было принято 30 Мб данных картинки, то виртуальная память после освобождения так и остаётся 30 метров). Занимаемая физическая тоже растёт (килобайты) - видимо хранит нарастающее количество виртуальных адресов, для дальнейшего использования.
Теперь встаёт вопрос как освободить память выделенную для хранения виртуальных адресов.
?
Да и вообще интересует первая строка. Какой компайлер используете при компиляции такой конструкции и насколько Вы уверены, что при этом с компиляцией все ОК?
?
Да и вообще интересует первая строка. Какой компайлер используете при компиляции такой конструкции и насколько Вы уверены, что при этом с компиляцией все ОК?
g++. Комментарий - освобождаю выделенную память, далее в нуль ставлю указатель, а потом всё тоже по нулям
Более интересно - зачем завёл переменную csl_imageempty - для перестраховки, чтоб уж наверняка. smile
Увы, но чёрт не шутил. Что 0, что NULL. - без разницы.
Теперь однако-ж появилась другая фигня.
Для сохранения картинки в PNG формат с 48-и битной глубиной цвета использую pngwriter. Так вот он так-же начинает хавать память, а освобождать нет. Хотя в доках говорится, что по закрытии объекта вся память высвобождается.(Если не использовать pngwriter, и сканировать в холостую, то память не кушается. - т.е. проблема именно в использовании pngwriter).
вся прога, собственно (в вышеприведённом makefile - apptest):
int main(int argc,char** argv)
{
char* apppath;
int arg0l = strlen(argv[0]);
int lastsl = 0;
for(int i = 0 ; i < arg0l;i++)
if (argv[0][i] == '/')
lastsl = i;
apppath = new char[lastsl];
strncpy(apppath,argv[0],lastsl);
TcanonScanLide70 scanner;
if (scanner.init() < 0)
{
cout << "error CanoScan LiDE 70 initialization\n";
return -1;
}
while (scanner.getdata() == 0) {usleep(10000);}
scanner.cleardata();
do
{
bool skipscan = false;
int dpi = 75;
int bpp = 24;
int x = 0;
int w = 1;
int y = 0;
int h = 1;
cout << "DPI (75,150,300,600): ";
fflush(stdout);
cin >> dpi;
if ((dpi != 75) and (dpi != 150) and (dpi != 300) and (dpi != 600))
skipscan = true;
if (skipscan == true) goto skipquestslabel;
cout << "BPP (24,48): ";
fflush(stdout);
cin >> bpp;
if ((bpp != 24) and (bpp != 48))
skipscan = true;
if (skipscan == true) goto skipquestslabel;
cout << "X offset: (0.."<<scanner.maximagewidth(dpi)-1<<"): ";
fflush(stdout);
cin >> x;
if ((x > scanner.maximagewidth(dpi)-1) or (x < 0))
skipscan = true;
if (skipscan == true) goto skipquestslabel;
cout << "Width: (1.."<<scanner.maximagewidth(dpi)-x<<"): ";
fflush(stdout);
cin >> w;
if ((w > scanner.maximagewidth(dpi)-x) or (w < 1))
skipscan = true;
if (skipscan == true) goto skipquestslabel;
cout << "Y offset: (0.."<<scanner.maximageheight(dpi)-1<<"): ";
fflush(stdout);
cin >> y;
if ((y > scanner.maximageheight(dpi)-1) or (y < 0))
skipscan = true;
if (skipscan == true) goto skipquestslabel;
cout << "Height: (1.."<<scanner.maximageheight(dpi)-y<<"): ";
fflush(stdout);
cin >> h;
if ((h > scanner.maximageheight(dpi)-y) or (h < 1))
skipscan = true;
if (skipscan == true) goto skipquestslabel;
skipquestslabel:
if (not skipscan)
{
scanner.doscan(dpi,bpp, x,y,w,h);
while (scanner.getdata() == 0)
usleep(10000);
string filename(apppath);
filename.resize(filename.size()-2);
filename+="/scannedimage.png";cout << "saving image to \"" << filename.data() << "\"\n";
pngwriter image(w, h, 1.0, filename.data());
int r = 0;
int g = 0;
int b = 0;
if (bpp == 48)
for (int j = 0 ; j < h; j++)
for (int i = 0 ; i < w; i++)
{
scanner.getpixel(i, j, &r, &g, &b);
image.plot(i, h-j-1, r, g, b);
}
else
for (int j = 0 ; j < h; j++)
for (int i = 0 ; i < w; i++)
{
scanner.getpixel(i, j, &r, &g, &b);
image.plot(i, h-j-1, r << 8, g << 8, b << 8);
}
image.setcompressionlevel(1);
image.close();//*/
scanner.cleardata();
cout << "wait...\n ";
while ((scanner.getdata() != 5) and (scanner.getdata() != 16))
usleep(10000);
}
scanquest:
cout << "want new scan? (y,s - scan, c - calibration): ";
fflush(stdout);
char scn;
cin >> scn;
if ((scn != 'y') and (scn != 's'))
{
if (scn == 'c')
{
scanner.calibration();
while (scanner.getdata() == 0)
usleep(10000);
scanner.cleardata();
goto scanquest;
}
else
break;
}
}
while (true);
exitapp:
scanner.close();
return 0;
}
Теперь встаёт вопрос как освободить память выделенную для хранения виртуальных адресов.
вы не тот вопрос ставите: а оно вообще надо? Зачем вам понадобилось освобождать память? Она сама освободится, если это будет нужно - система полагает, что память программе МОЖЕТ понадобится, и потому её не очищает. ИМХО - это правильное поведение.
а почему вы считаете, что она сразу должна нарисоваться в свободной?
Если создавать объект динамический, то память тоже не освобождается. Здесь привел не тот вариант.
Вообще по логике, память должна высвобождаться. Не вижу особых причин, чтобы этого не происходило. Разве что ядро резервирует этот объем, чтоб потом опять его не выделять.
Вообще по логике, память должна высвобождаться. Не вижу особых причин, чтобы этого не происходило. Разве что ядро резервирует этот объем, чтоб потом опять его не выделять.
ну. так ИМХО и есть - резервирует. А зачем ей память освобождать в тот момент, когда она никому не нужна? У меня так постоянно 100% занято - не вижу трагедии, нужно будет - освободит.
А освободит-ли? smile Как-бы использование SWAP-а не стало предпочтительнее.Это ещё предстоит проверить.
я замечал, что использование swap'а имеет намного более низкий приоритет (ИМХО так и должно быть), т.е. если в системе "занято" 100% памяти, но используется скажем 30%, то если выделить ещё 70%, то своп только появляется (система зачем-то старается оставить свободными примерно 1% памяти, т.е. выделяется не 100%, а 99%, и оставшийся кусочек берётся из свопа). Наверное это как-то настраивается, но мне и так нравится.
ЗЫЖ а вот венда зачем-то старается держать свободным чуть-ли не 50% :(