Деструктор std :: unordered_map не освобождает память? - PullRequest
2 голосов
/ 07 февраля 2020

Согласно стандарту, когда вызывается деструктор std :: unordered_map (например, когда он покидает область, в которой он создан), ожидается, что память, которую он выделяет, будет освобождена. Однако простой эксперимент, который я проводил на нескольких машинах, кажется, противоречит этому? Рассмотрим следующую программу:

    #include <chrono>
    #include <iostream>
    #include <map>
    #include <unordered_map>
    #include <memory>
    #include <thread>
    #include <vector>

    void CreateMap() {
        std::unordered_map<int, int> testMap;

        for (int i=0; i < 10000000; i++) {
            testMap[i] = 5;
        }

        std::cout << "finished building map" << std::endl;

        std::this_thread::sleep_for (std::chrono::seconds(15));

        std::cout << "about to exit from CreateMap()" << std::endl;
    }

    int main()
    {
        CreateMap();

        CreateMap(); 

        CreateMap();
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    return 0;
}

На моей машине 10% оперативной памяти используется, когда карта заканчивается, но мы в конце спим в CreateMap (). Однако после выхода из ОЗУ снижается только до 8% (можно играть с картами разных размеров, чтобы показать, что сама карта отвечает за более чем 2%), поэтому можно ожидать, что CreateMap утечка памяти? Однако 3 вызова CreateMap () или только один вызов, кажется, не имеют значения (поэтому память используется программой, а не ОЗУ?).

Может ли это быть чем-то странным в управлении памятью ОС, что я не понимаете, т. е. возможно ли, чтобы программа освободила память для своего собственного будущего использования (распределения в будущем), но не для ОС (т. е. для выделения памяти в других программах)?

Ответы [ 2 ]

2 голосов
/ 07 февраля 2020

Вы тестируете неправильно. Код не протекает, но освобожденная память не обязательно становится доступной для других процессов (что вы измеряете) - она, вероятно, останется востребованной для будущего распределения тем же процессом.

Например, после удаляя бесконечное число l oop и уменьшая ограничение на i, чтобы оно соответствовало моей тестовой песочнице, я запустил код под Valgrind:

valgrind --leak-check=full ./60112215   
==3396096== Memcheck, a memory error detector
==3396096== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3396096== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==3396096== Command: ./60112215
==3396096== 
finished building map
about to exit from CreateMap()
finished building map
about to exit from CreateMap()
finished building map
about to exit from CreateMap()
==3396096== 
==3396096== HEAP SUMMARY:
==3396096==     in use at exit: 0 bytes in 0 blocks
==3396096==   total heap usage: 300,044 allocs, 300,044 frees, 13,053,168 bytes allocated
==3396096== 
==3396096== All heap blocks were freed -- no leaks are possible
==3396096== 
==3396096== For lists of detected and suppressed errors, rerun with: -s
==3396096== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Если вы хотите действительно продемонстрировать это для себя, вы можете Вызовите CreateMap() еще много раз и увидите, что использование памяти процессом не увеличивается:

int main()
{
    for (auto i = 0;  i < 100;  ++i) {
        CreateMap();
    }
}

Это явно повторное использование памяти, которая была освобождена в предыдущей итерации.

0 голосов
/ 07 февраля 2020

Убедитесь, что std::unordered_map не протекает. Вы не можете использовать системный монитор, чтобы проверить, протекает ли ваша программа, точка. Во всяком случае, вы должны смотреть на физическое использование памяти, а не общий или виртуальный. Даже физическая память не является точной в использовании оперативной памяти, поскольку страницы могут быть выгружены на диск (файл подкачки). Даже тогда, оптимизация, например. уровень языка ОС или c ++ может повторно использовать кучу памяти (, как указано в комментарии ) для оптимизации. Здесь задействовано слишком много факторов, и это просто слишком большая ценность, чтобы быть полезным для чего-то столь же деликатного, как обнаружение утечки памяти. Простым (и часто эффективным) подходом к поиску утечек является поиск кучи при завершении программы, например. используя _CrtSetDbgFlag или аналогичные инструменты. Само собой разумеется, что избегая ручного управления памятью и используя семантику владения, можно избежать утечки кода.

...