Есть два сервера с идентичным ПО, железом и рабочим окружением. ОС старый добрый 6 дебиан. Разница лишь в количестве оперативки: 4 гига на тестовом и 128 на рабочем. Есть код на C++, который обрабатывает сетевые соединения, получает данные и пишет их в базу. При выполнении идентичного запроса на тестовом сервере использование оперативки и виртуальной памяти держится на одном уровне, а на рабочем только растет. Утечек памяти valgrind не нашел, структуры чисты, руками очищается даже выделяемая "лишняя" память векторов, плюс руками же чистится куча. Собственно вопрос: в какую сторону еще можно копнуть, чтобы разрешить проблему?
ps ef -o vsize,rss,%mem -p pidof непалюсь
Тестовый сервер
VSZ RSS %MEM
448768 40832 0.6
Рабочий сервер
VSZ RSS %MEM
38446720 23682724 17.9
Для поиска неявных утечек можно воспользоваться другим инструментом valgrind'а: massif
. Это профайлер кучи, который работает по следующему принципу: во время выполнения программы логируются все выделения/освобождения памяти, а также периодически делаются снимки использования памяти. В некоторых «детализированных» снимках сохраняется подробная статистика, сколько в каждой «точке аллокации» (функции в бектрейсе к malloc'у) было выделено, но не освобождено памяти. Логируются только точки аллокации, выделенная память из кучи к которым превосходит некий порог (по умолчанию ≥1% от общего объёма выделенного из кучи).
Пара контейнеров, которые равномерно заполняются и освобождаются, но в Стеке элемент «Забыли» извлечь:
#include <iostream>
#include <limits>
#include <memory>
#include <stack>
#include <queue>
#include <cstdlib>
class Container {
public:
virtual void store(int s) = 0;
virtual int unstore() = 0;
virtual ~Container() {};
};
class Stack: public Container {
std::stack<int> data;
public:
virtual void store(int s) { data.push(s); }
virtual int unstore() {
if (data.size() !=0 ) {
auto rv = data.top();
// data.pop();
return rv;
} else {
return std::numeric_limits<int>::min();
}
}
virtual ~Stack() {};
};
class Queue: public Container {
std::queue<int> data;
public:
virtual void store(int s) { data.push(s); }
virtual int unstore() {
if (data.size() !=0 ) {
auto rv = data.front();
data.pop();
return rv;
} else {
return std::numeric_limits<int>::min();
}
}
virtual ~Queue() {};
};
int main(void) {
std::unique_ptr<Container> cont1 (new Stack);
std::unique_ptr<Container> cont2 (new Queue);
for (int i = 0; i < 1024*1024; ++i) {
int num = rand();
if (num<RAND_MAX/4) {
cont1->store(num);
} else if (num<RAND_MAX/4*2) {
cont1->unstore();
} else if (num<RAND_MAX/4*3) {
cont2->store(num);
} else {
cont2->unstore();
}
}
return 0;
}
После запуска valgrind --tool=massif ./mem_eater
получаем massif.out.28861
.
Далее просматриваем его с помощью родной утилитки (к сожалению, сторонних графических инструментов для этого пока нет) ms_print massif.out.28861
и получаем здоровенную простынку с текстом (оставлена только одна секция для наглядности):
--------------------------------------------------------------------------------
Command: ./mem_eater
Massif arguments: (none)
ms_print arguments: massif.out.28861
--------------------------------------------------------------------------------
MB
1.116^ #
| @@@#
| @@@@@@@#
| @:@@@@@@@@@#
| @:@:@@@@@@@@@#
| ::::@:@:@@@@@@@@@#
| @@@:::: :@:@:@@@@@@@@@#
| @@@@@ @:::: :@:@:@@@@@@@@@#
| :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| @:@@:@@@ @@@ @:::: :@:@:@@@@@@@@@#
| ::::@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| :@@:: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| ::@:::@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| :@:: @: :@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| :::@@:@:: @: :@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| ::::: :@ :@:: @: :@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| @@:: ::: :@ :@:: @: :@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| :::@@ :: ::: :@ :@:: @: :@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| ::::: :@@ :: ::: :@ :@:: @: :@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
| :@: ::: :@@ :: ::: :@ :@:: @: :@ :: :@:@ :@@@ @@@ @:::: :@:@:@@@@@@@@@#
0 +----------------------------------------------------------------------->Mi
0 302.7
Number of snapshots: 64
Detailed snapshots: [2, 8, 9, 16, 17, 19, 22, 25, 30, 32, 34, 35, 36, 37, 38, 39, 40, 46, 48, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 (peak)]
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
0 0 0 0 0 0
1 5,073,918 83,184 82,976 208 0
.....
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
40 224,967,222 841,304 829,744 11,560 0
98.63% (829,744B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->87.51% (736,256B) 0x10AB96: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (new_allocator.h:104)
| ->87.51% (736,256B) 0x10A919: std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (alloc_traits.h:436)
| ->87.51% (736,256B) 0x10A398: std::_Deque_base<int, std::allocator<int> >::_M_allocate_node() (stl_deque.h:600)
| ->87.45% (735,744B) 0x109D4C: void std::deque<int, std::allocator<int> >::_M_push_back_aux<int const&>(int const&) (deque.tcc:471)
| | ->87.45% (735,744B) 0x109875: std::deque<int, std::allocator<int> >::push_back(int const&) (stl_deque.h:1527)
| | ->87.03% (732,160B) 0x109499: std::stack<int, std::deque<int, std::allocator<int> > >::push(int const&) (stl_stack.h:219)
| | | ->87.03% (732,160B) 0x10912E: Stack::store(int) (mem_eater.cpp:19)
| | | ->87.03% (732,160B) 0x108F11: main (mem_eater.cpp:55)
| | |
| | ->00.43% (3,584B) in 1+ places, all below ms_print's threshold (01.00%)
| |
| ->00.06% (512B) in 1+ places, all below ms_print's threshold (01.00%)
|
->08.64% (72,704B) 0x4EC16EE: ??? (in /usr/lib64/gcc/x86_64-pc-linux-gnu/6.4.0/libstdc++.so.6.0.22)
| ->08.64% (72,704B) 0x400F448: call_init.part.0 (in /lib64/ld-2.25.so)
| ->08.64% (72,704B) 0x400F559: _dl_init (in /lib64/ld-2.25.so)
| ->08.64% (72,704B) 0x4000B88: ??? (in /lib64/ld-2.25.so)
|
->02.45% (20,608B) 0x10AC08: __gnu_cxx::new_allocator<int*>::allocate(unsigned long, void const*) (new_allocator.h:104)
| ->02.45% (20,608B) 0x10A9DB: std::allocator_traits<std::allocator<int*> >::allocate(std::allocator<int*>&, unsigned long) (alloc_traits.h:436)
| ->02.45% (20,608B) 0x10A566: std::_Deque_base<int, std::allocator<int> >::_M_allocate_map(unsigned long) (stl_deque.h:614)
| ->02.45% (20,608B) 0x10A81A: std::deque<int, std::allocator<int> >::_M_reallocate_map(unsigned long, bool) (deque.tcc:929)
| | ->02.45% (20,608B) 0x10A36D: std::deque<int, std::allocator<int> >::_M_reserve_map_at_back(unsigned long) (stl_deque.h:2116)
| | ->02.45% (20,608B) 0x109D34: void std::deque<int, std::allocator<int> >::_M_push_back_aux<int const&>(int const&) (deque.tcc:470)
| | ->02.45% (20,608B) 0x109875: std::deque<int, std::allocator<int> >::push_back(int const&) (stl_deque.h:1527)
| | ->02.43% (20,464B) 0x109499: std::stack<int, std::deque<int, std::allocator<int> > >::push(int const&) (stl_stack.h:219)
| | | ->02.43% (20,464B) 0x10912E: Stack::store(int) (mem_eater.cpp:19)
| | | ->02.43% (20,464B) 0x108F11: main (mem_eater.cpp:55)
| | |
| | ->00.02% (144B) in 1+ places, all below ms_print's threshold (01.00%)
| |
| ->00.00% (0B) in 1+ places, all below ms_print's threshold (01.00%)
|
->00.02% (176B) in 1+ places, all below ms_print's threshold (01.00%)
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
41 230,069,860 857,944 846,128 11,816 0
42 235,254,410 875,624 863,536 12,088 0
43 240,656,059 892,784 880,432 12,352 0
44 246,176,028 911,504 898,864 12,640 0
45 251,968,045 931,264 918,320 12,944 0
46 258,089,356 952,584 939,312 13,272 0
98.61% (939,312B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->88.79% (845,824B) 0x10AB96: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (new_allocator.h:104)
| ->88.79% (845,824B) 0x10A919: std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (alloc_traits.h:436)
| ->88.79% (845,824B) 0x10A398: std::_Deque_base<int, std::allocator<int> >::_M_allocate_node() (stl_deque.h:600)
| ->88.74% (845,312B) 0x109D4C: void std::deque<int, std::allocator<int> >::_M_push_back_aux<int const&>(int const&) (deque.tcc:471)
| | ->88.74% (845,312B) 0x109875: std::deque<int, std::allocator<int> >::push_back(int const&) (stl_deque.h:1527)
| | ->88.47% (842,752B) 0x109499: std::stack<int, std::deque<int, std::allocator<int> > >::push(int const&) (stl_stack.h:219)
| | | ->88.47% (842,752B) 0x10912E: Stack::store(int) (mem_eater.cpp:19)
| | | ->88.47% (842,752B) 0x108F11: main (mem_eater.cpp:55)
| | |
| | ->00.27% (2,560B) in 1+ places, all below ms_print's threshold (01.00%)
| |
| ->00.05% (512B) in 1+ places, all below ms_print's threshold (01.00%)
|
->07.63% (72,704B) 0x4EC16EE: ??? (in /usr/lib64/gcc/x86_64-pc-linux-gnu/6.4.0/libstdc++.so.6.0.22)
| ->07.63% (72,704B) 0x400F448: call_init.part.0 (in /lib64/ld-2.25.so)
| ->07.63% (72,704B) 0x400F559: _dl_init (in /lib64/ld-2.25.so)
| ->07.63% (72,704B) 0x4000B88: ??? (in /lib64/ld-2.25.so)
|
->02.16% (20,608B) 0x10AC08: __gnu_cxx::new_allocator<int*>::allocate(unsigned long, void const*) (new_allocator.h:104)
| ->02.16% (20,608B) 0x10A9DB: std::allocator_traits<std::allocator<int*> >::allocate(std::allocator<int*>&, unsigned long) (alloc_traits.h:436)
| ->02.16% (20,608B) 0x10A566: std::_Deque_base<int, std::allocator<int> >::_M_allocate_map(unsigned long) (stl_deque.h:614)
| ->02.16% (20,608B) 0x10A81A: std::deque<int, std::allocator<int> >::_M_reallocate_map(unsigned long, bool) (deque.tcc:929)
| | ->02.16% (20,608B) 0x10A36D: std::deque<int, std::allocator<int> >::_M_reserve_map_at_back(unsigned long) (stl_deque.h:2116)
| | ->02.16% (20,608B) 0x109D34: void std::deque<int, std::allocator<int> >::_M_push_back_aux<int const&>(int const&) (deque.tcc:470)
| | ->02.16% (20,608B) 0x109875: std::deque<int, std::allocator<int> >::push_back(int const&) (stl_deque.h:1527)
| | ->02.15% (20,464B) 0x109499: std::stack<int, std::deque<int, std::allocator<int> > >::push(int const&) (stl_stack.h:219)
| | | ->02.15% (20,464B) 0x10912E: Stack::store(int) (mem_eater.cpp:19)
| | | ->02.15% (20,464B) 0x108F11: main (mem_eater.cpp:55)
| | |
| | ->00.02% (144B) in 1+ places, all below ms_print's threshold (01.00%)
| |
| ->00.00% (0B) in 1+ places, all below ms_print's threshold (01.00%)
|
->00.02% (176B) in 1+ places, all below ms_print's threshold (01.00%)
.....
Из графика видно, что использование памяти для данного игрушечного примера постоянно растёт. А из последующего отчёта видно, что ко времени снимка 46
для хранения стека уже используется (845,824+20,608) байт, или же больше 90% всей кучи используемой программой, что указывает на очевидную ошибку. Интерпретация вывода не механическая и требует некоторых усилий и сноровки, но это помогает увидеть на что стоит обратить внимание.
Если поправить злосчастный комментарий, то получается практически чистый вывод с несколькими ложноположительными срабатываниями связанными с простым пиковым потреблением памяти:
--------------------------------------------------------------------------------
Command: ./mem_eater
Massif arguments: (none)
ms_print arguments: massif.out.29399
--------------------------------------------------------------------------------
KB
77.60^ #:: : :
| :@:::::::::::::::::::::::#: :::::::::::::::::::::::@::::::::@::::
| :::::::@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
| :: :: :@::: ::: :: ::::: :::: :#: :: :::::::: :: :::::: :@::::::::@::::
0 +----------------------------------------------------------------------->Mi
0 319.7
Number of snapshots: 66
Detailed snapshots: [7, 26 (peak), 50, 60]
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
0 0 0 0 0 0
1 6,357,999 74,624 74,544 80 0
2 11,037,520 74,104 74,032 72 0
3 18,736,364 74,104 74,032 72 0
4 23,440,137 74,104 74,032 72 0
5 27,510,682 74,104 74,032 72 0
6 33,147,787 75,664 75,568 96 0
7 38,465,295 75,664 75,568 96 0
99.87% (75,568B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->96.09% (72,704B) 0x4EC16EE: ??? (in /usr/lib64/gcc/x86_64-pc-linux-gnu/6.4.0/libstdc++.so.6.0.22)
| ->96.09% (72,704B) 0x400F448: call_init.part.0 (in /lib64/ld-2.25.so)
| ->96.09% (72,704B) 0x400F559: _dl_init (in /lib64/ld-2.25.so)
| ->96.09% (72,704B) 0x4000B88: ??? (in /lib64/ld-2.25.so)
|
->03.38% (2,560B) 0x10AC9E: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (new_allocator.h:104)
| ->03.38% (2,560B) 0x10AA21: std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (alloc_traits.h:436)
| ->03.38% (2,560B) 0x10A4A0: std::_Deque_base<int, std::allocator<int> >::_M_allocate_node() (stl_deque.h:600)
| ->02.71% (2,048B) 0x109DD6: void std::deque<int, std::allocator<int> >::_M_push_back_aux<int const&>(int const&) (deque.tcc:471)
| | ->02.71% (2,048B) 0x1098A1: std::deque<int, std::allocator<int> >::push_back(int const&) (stl_deque.h:1527)
| | ->02.03% (1,536B) 0x1095AF: std::queue<int, std::deque<int, std::allocator<int> > >::push(int const&) (stl_queue.h:243)
| | | ->02.03% (1,536B) 0x109232: Queue::store(int) (mem_eater.cpp:35)
| | | ->02.03% (1,536B) 0x108F5E: main (mem_eater.cpp:59)
| | |
| | ->00.68% (512B) in 1+ places, all below ms_print's threshold (01.00%)
| |
| ->00.68% (512B) in 1+ places, all below ms_print's threshold (01.00%)
|
->00.40% (304B) in 1+ places, all below ms_print's threshold (01.00%)
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
8 43,466,034 75,664 75,568 96 0
9 50,687,976 75,744 75,648 96 0
10 54,674,907 76,784 76,672 112 0
11 61,489,426 76,784 76,672 112 0
12 66,913,719 76,784 76,672 112 0
13 73,503,124 76,784 76,672 112 0
14 80,840,510 75,744 75,648 96 0
15 85,776,021 75,744 75,648 96 0
16 93,894,556 76,784 76,672 112 0
17 98,955,219 76,264 76,160 104 0
18 104,342,732 76,784 76,672 112 0
19 109,566,108 75,744 75,648 96 0
20 116,286,750 76,784 76,672 112 0
21 124,342,177 76,784 76,672 112 0
22 128,350,479 77,824 77,696 128 0
23 133,653,415 76,264 76,160 104 0
24 139,392,895 77,824 77,696 128 0
25 144,750,550 78,864 78,720 144 0
26 151,838,741 79,464 79,312 152 0
99.81% (79,312B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->91.49% (72,704B) 0x4EC16EE: ??? (in /usr/lib64/gcc/x86_64-pc-linux-gnu/6.4.0/libstdc++.so.6.0.22)
| ->91.49% (72,704B) 0x400F448: call_init.part.0 (in /lib64/ld-2.25.so)
| ->91.49% (72,704B) 0x400F559: _dl_init (in /lib64/ld-2.25.so)
| ->91.49% (72,704B) 0x4000B88: ??? (in /lib64/ld-2.25.so)
|
->07.73% (6,144B) 0x10AC9E: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (new_allocator.h:104)
| ->07.73% (6,144B) 0x10AA21: std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (alloc_traits.h:436)
| ->07.73% (6,144B) 0x10A4A0: std::_Deque_base<int, std::allocator<int> >::_M_allocate_node() (stl_deque.h:600)
| ->07.09% (5,632B) 0x109DD6: void std::deque<int, std::allocator<int> >::_M_push_back_aux<int const&>(int const&) (deque.tcc:471)
| | ->07.09% (5,632B) 0x1098A1: std::deque<int, std::allocator<int> >::push_back(int const&) (stl_deque.h:1527)
| | ->03.87% (3,072B) 0x1095AF: std::queue<int, std::deque<int, std::allocator<int> > >::push(int const&) (stl_queue.h:243)
| | | ->03.87% (3,072B) 0x109232: Queue::store(int) (mem_eater.cpp:35)
| | | ->03.87% (3,072B) 0x108F5E: main (mem_eater.cpp:59)
| | |
| | ->03.22% (2,560B) 0x1094A9: std::stack<int, std::deque<int, std::allocator<int> > >::push(int const&) (stl_stack.h:219)
| | ->03.22% (2,560B) 0x10912E: Stack::store(int) (mem_eater.cpp:19)
| | ->03.22% (2,560B) 0x108F11: main (mem_eater.cpp:55)
| |
| ->00.64% (512B) in 1+ places, all below ms_print's threshold (01.00%)
|
->00.58% (464B) in 1+ places, all below ms_print's threshold (01.00%)
.....
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
61 318,903,255 77,384 77,264 120 0
62 322,953,503 77,384 77,264 120 0
63 327,012,737 77,384 77,264 120 0
64 331,088,164 76,864 76,752 112 0
65 335,212,302 76,864 76,752 112 0
Кроме massif
в valgrind'е есть другая схожая утилита: DHAT (exp-dhat). Она работает несколько на другом принципе: для каждой точки аллокации она собирает статистику по количеству аллокаций/деаллокаций, чтениям/записям, а также времени жизни аллокаций. Её также возможно использовать для неявных утечек, но ИМХО вывод оной интерпретировать сложнее и её лучше оставить для поиска более тонких ошибок/оптимизаций: поиска сохраняемых, но не читаемых данных, оптимизаций избыточной аллокации, поиск выделений слишком больших блоков итп.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
на вход дается положительное целое число, нужно найти последующее четное число, причем программа должна быть БЕЗ использования условного...
В методе actionPerformed одной JButton у меня выполняются определенные операции с переменными, например:
Имеется объект типа Entry со своими полями где хранятся ключ (Long) и значение (String) и ссылка на следующий объект этого же типаЭти объекты в свою...
Сам не первый год пишу на java, но лишь в рамках хобби, с многопоточностью приходится не так часто работать