zlib inflateReset вызывает утечку памяти (нет)
В настоящее время я работаю над приведенным ниже требованием. Вот требование: на стороне сервера большой файл делится на 4000-байтовые блоки (кадры). Каждый блок, в свою очередь, сжимается (используя zlib) и отправляется клиентскому процессу. Например, если размер файла составляет 12000 байт, он разделяется на 3 блока.
Выше файл будет иметь 3 блока => Блок-0, Блок-1, Блок-2
При получении клиент распаковывает каждый блок (или кадр) и записывает данные в буфер, выделенный в куче. Когда клиент получает все блоки, соответствующие всему файлу, несжатая версия результирующего файла записывается на диск.
Я написал подпрограмму inflateData, которая выполняет следующие действия на основе полученного блока:
Когда первый блок получен, - inflateInit - inflate - inflateReset Когда получены промежуточные блоки, - inflate - inflateReset Когда последний блок получен, - inflate - inflateEnd
С помощью описанной выше процедуры распаковка блоков происходит, как и ожидалось. Но проблема, с которой я сталкиваюсь, заключается в том, что она потребляет много памяти и в какой-то момент вся система замедляется. При проверке с помощью valgrind утечка памяти сообщается с помощью inflateInit2_. Это приводит к исчерпанию системных ресурсов.
== 30359 == 57,312 байт в 6 блоках, возможно, потеряны в записи потерь 64 из 67
== 30359 == в 0x4A069EE: malloc (vg_replace_malloc.c: 270)
== 30359 == по 0x3E57808F1E: inflateInit2_ (в /lib64/libz.so.1.2.3)
== 30359 == по 0x40C220: inflateData (productMaker.c:1668)
Ниже приводится процедура inflateData.
int inflateData(
char* const inBuf,
unsigned long inLen,
unsigned int isFirstBlk,
unsigned int isLastBlk,
const char* outBuf,
unsigned long* outLen)
{
int have;
int readsz;
int bsize;
static z_stream zstrm;
int zerr;
int flush;
char out[CHUNK_SZ];
char in[CHUNK_SZ];
int ret,nwrite,idx = -1;
int savedByteCntr=0;
unsigned char *dstBuf;
int firstCall = 1;
int totalBytesIn=0;
int inflatedBytes=0;
int decompByteCounter = 0;
int num=0;
ret = Z_OK;
readsz = 0;
bsize = CHUNK_SZ;
dstBuf = (unsigned char *) outBuf;
if(isFirstBlk){
memset(&zstrm, '\0', sizeof(z_stream));
zstrm.zalloc = Z_NULL;
zstrm.zfree = Z_NULL;
zstrm.opaque = Z_NULL;
if ((zerr = inflateInit(&zstrm)) != Z_OK) {
uerror("ERROR %d inflateInit (%s)",
zerr, decode_zlib_err(zerr));
return -1;
}
}
while(totalBytesIn < inLen ) {
int compChunkSize = ((inLen - totalBytesIn) > 5120) ? 5120 :
(inLen - totalBytesIn);
memcpy(in, inBuf + totalBytesIn, compChunkSize);
zstrm.avail_in = inLen - totalBytesIn;
zstrm.next_in = in ;
zstrm.avail_out = CHUNK_SZ;
zstrm.next_out = out;
inflatedBytes = 0;
while(ret != Z_STREAM_END) {
ret = inflate(&zstrm, Z_NO_FLUSH);
if(ret < 0) {
uerror(" Error %d inflate (%s)", ret, decode_zlib_err(ret));
(void)inflateEnd(&zstrm);
return ret;
}
inflatedBytes = CHUNK_SZ - zstrm.avail_out;
if(inflatedBytes == 0) {
unotice("\n Unable to decompress data - truncated");
break;
}
totalBytesIn += zstrm.total_in;
decompByteCounter += inflatedBytes;
memcpy(dstBuf + savedByteCntr, out, inflatedBytes);
savedByteCntr = decompByteCounter;
}
// Reset inflater for additional input
ret = inflateReset(&zstrm);
if(ret == Z_STREAM_ERROR){
uerror(" Error %d inflateReset (%s)", ret, decode_zlib_err(ret));
(void)inflateEnd(&zstrm);
return ret;
}
}
if(isLastBlk){
ret = inflateEnd(&zstrm);
if(ret < 0) {
uerror("Fail inflateEnd %d [%s] ", ret, decode_zlib_err(ret));
return (ret);
}
}
*outLen = decompByteCounter;
return 0;
}
Заранее спасибо за поддержку.
Спасибо, Сатья.
1 ответ
Вы делаете ошибку при использовании вашего inflateData()
рутина.
Во-первых, использование статической переменной таким образом - ужасная идея. Если вы позвоните inflateData()
дважды с isFirstBlk
правда без промежуточного вызова с isLastBlk
Значение true, тогда вы сотрете ссылку на первый набор выделений, что приведет к утечке памяти.
Чтобы избежать такого рода ошибок, вы должны следить за тем, zstrm
инициализируется или нет, и отклонить любую попытку инициализировать уже инициализированный поток. Еще лучше было бы даже не иметь isFirstBlk
и просто инициализировать zstrm
на первый звонок и на любой звонок, который следует сразу за звонком с isLastBlk
правда.
Таким образом, вы либо делаете вышеуказанное, звоните дважды с isFirstBlk
истина или не удается позвонить с isLastBlk
правда.