Есть два сервера с идентичным ПО, железом и рабочим окружением. ОС старый добрый 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). Она работает несколько на другом принципе: для каждой точки аллокации она собирает статистику по количеству аллокаций/деаллокаций, чтениям/записям, а также времени жизни аллокаций. Её также возможно использовать для неявных утечек, но ИМХО вывод оной интерпретировать сложнее и её лучше оставить для поиска более тонких ошибок/оптимизаций: поиска сохраняемых, но не читаемых данных, оптимизаций избыточной аллокации, поиск выделений слишком больших блоков итп.
на вход дается положительное целое число, нужно найти последующее четное число, причем программа должна быть БЕЗ использования условного...
В методе actionPerformed одной JButton у меня выполняются определенные операции с переменными, например:
Имеется объект типа Entry со своими полями где хранятся ключ (Long) и значение (String) и ссылка на следующий объект этого же типаЭти объекты в свою...
Сам не первый год пишу на java, но лишь в рамках хобби, с многопоточностью приходится не так часто работать