MSVC14 (VS2015)에서 std::unordered_map
의 이상한 동작을 관찰하고 있습니다. 다음 시나리오를 고려하십시오. 정렬되지 않은 맵을 생성하고 상당한 양의 메모리를 소비하는 더미 구조체로 채우면 1Gb, 전체 100k 요소가 삽입됩니다. 그런 다음지도에서 요소를 삭제하기 시작합니다. 당신이 요소의 절반을 삭제했다고 가정하면, 절반의 메모리가 해제 될 것이라고 예상 할 수 있습니다. 권리? 잘못된! 맵의 요소 수가 임계 값을 넘었을 때 메모리가 해제 된 것을 볼 수 있습니다. 제 경우에는 1443 요소였습니다. VirtualAllocEx
또는 HeapAlloc
을 사용하여 OS에서 큰 청크를 할당하는 것은 malloc
최적화라고 말할 수 있습니다. 실제로 최적화가 정책을 지정하고 이후에 이미 할당 된 메모리를 다시 사용할 수 있도록하기 위해 시스템에 메모리를 해제하지 않습니다. allocate_shared
에 대한 사용자 지정 할당자를 사용한이 경우를 제거하기 위해 트릭을하지 않았습니다. 그래서 주요한 질문은 왜 일어나는 것이며, 무엇이 "compact"메모리로 수행 할 수 있습니까? unordered_map
에 의해 사용됩니까?
std :: unordered_map이 메모리를 해제하지 않습니다.
#include <windows.h>
#include <memory>
#include <vector>
#include <map>
#include <unordered_map>
#include <random>
#include <thread>
#include <iostream>
#include <allocators>
HANDLE heap = HeapCreate(0, 0, 0);
template <class Tp>
struct SimpleAllocator
{
typedef Tp value_type;
SimpleAllocator() noexcept
{}
template <typename U>
SimpleAllocator(const SimpleAllocator<U>& other) throw()
{};
Tp* allocate(std::size_t n)
{
return static_cast<Tp*>(HeapAlloc(heap, 0, n * sizeof(Tp)));
}
void deallocate(Tp* p, std::size_t n)
{
HeapFree(heap, 0, p);
}
};
template <class T, class U>
bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&)
{
return true;
}
template <class T, class U>
bool operator!=(const SimpleAllocator<T>& a, const SimpleAllocator<U>& b)
{
return !(a == b);
}
struct Entity
{
Entity()
{
_6 = std::string("a", dis(gen));
_7 = std::string("b", dis(gen));
for(size_t i = 0; i < dis(gen); ++i)
{
_9.emplace(i, std::string("c", dis(gen)));
}
}
int _1 = 1;
int _2 = 2;
double _3 = 3;
double _4 = 5;
float _5 = 3.14f;
std::string _6 = "hello world!";
std::string _7 = "A quick brown fox jumps over the lazy dog.";
std::vector<unsigned long long> _8 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
std::map<long long, std::string> _9 = {{0, "a"},{1, "b"},{2, "c"},{3, "d"},{4, "e"},
{5, "f"},{6, "g"},{7, "h"},{8, "e"},{9, "j"}};
std::vector<double> _10{1000, 3.14};
std::random_device rd;
std::mt19937 gen = std::mt19937(rd());
std::uniform_int_distribution<size_t> dis = std::uniform_int_distribution<size_t>(16, 256);
};
using Container = std::unordered_map<long long, std::shared_ptr<Entity>>;
void printContainerInfo(std::shared_ptr<Container> container)
{
std::cout << std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())
<< ", Size: " << container->size() << ", Bucket count: " << container->bucket_count()
<< ", Load factor: " << container->load_factor() << ", Max load factor: " << container->max_load_factor()
<< std::endl;
}
int main()
{
constexpr size_t maxEntites = 100'000;
constexpr size_t ps = 10'000;
stdext::allocators::allocator_chunklist<Entity> _allocator;
std::shared_ptr<Container> test = std::make_shared<Container>();
test->reserve(maxEntites);
for(size_t i = 0; i < maxEntites; ++i)
{
test->emplace(i, std::make_shared<Entity>());
}
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<size_t> dis(0, maxEntites);
size_t cycles = 0;
while(test->size() > 0)
{
size_t counter = 0;
std::cout << "Press any key..." << std::endl;
std::cin.get();
while(test->size() > 1443)
{
test->erase(dis(gen));
}
printContainerInfo(test);
std::cout << "Press any key..." << std::endl;
std::cin.get();
std::cout << std::endl;
}
return 0;
}
상황이 지금까지 시도 코드 : 부하 계수가 어떤 임계 값에 도달하면 /재탕 크기를 조정하는 것을 시도하십시오 - 제전 while
에서 나던 때 그런 다음이
if(test->load_factor() < 0.2)
{
test->max_load_factor(1/test->load_factor());
test->rehash(test->size());
test->reserve(test->size());
printContainerInfo(test);
test->max_load_factor(1);
test->rehash(test->size());
test->reserve(test->size());
}
같은 것을 추가 임시 컨테이너 만들기, 남은 항목 복사/이동, 원래 항목 지우기, temp에서 원본으로 복사/이동과 같은 무언가를 시도하십시오. 이
if(test->load_factor() < 0.2)
{
Container tmp;
std::copy(test->begin(), test->end(), std::inserter(tmp, tmp.begin()));
test->clear();
test.reset();
test = std::make_shared<Container>();
std::copy(tmp.begin(), tmp.end(), std::inserter(*test, test->begin()));
}
같은 것을 마지막으로, allocate_shared
와 shared_ptr
를 교체하고 그것에 SimpleAllocator
인스턴스를 전달합니다.
또한 std::unordered_map's
vector
(의 msvc stl 구현은 list
및 vector
을 기반으로 함)에 std::vector::shrink_to_fit
을 호출하는 것처럼 여기 저기에 STL 코드를 수정 했으므로 작업하지 못했습니다.
EDIT001 : 신자가 아닌 모든 사람들에게. 다음 코드는 이전 코드와 다소 차이가 있지만 unordered_map
대신 std::vector<Entity>
을 사용합니다. 은 OS가 회수 한입니다.
#include <memory>
#include <vector>
#include <map>
#include <random>
#include <thread>
#include <iostream>
struct Entity
{
Entity()
{
_6 = std::string("a", dis(gen));
_7 = std::string("b", dis(gen));
for(size_t i = 0; i < dis(gen); ++i)
{
_9.emplace(i, std::string("c", dis(gen)));
}
}
int _1 = 1;
int _2 = 2;
double _3 = 3;
double _4 = 5;
float _5 = 3.14f;
std::string _6 = "hello world!";
std::string _7 = "A quick brown fox jumps over the lazy dog.";
std::vector<unsigned long long> _8 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
std::map<long long, std::string> _9 = {{0, "a"}, {1, "b"}, {2, "c"}, {3, "d"}, {4, "e"},
{5, "f"}, {6, "g"}, {7, "h"}, {8, "e"}, {9, "j"}};
std::vector<double> _10{1000, 3.14};
std::random_device rd;
std::mt19937 gen = std::mt19937(rd());
std::uniform_int_distribution<size_t> dis = std::uniform_int_distribution<size_t>(16, 256);
};
using Container = std::vector<std::shared_ptr<Entity>>;
void printContainerInfo(std::shared_ptr<Container> container)
{
std::cout << std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())
<< ", Size: " << container->size() << ", Capacity: " << container->capacity() << std::endl;
}
int main()
{
constexpr size_t maxEntites = 100'000;
constexpr size_t ps = 10'000;
std::shared_ptr<Container> test = std::make_shared<Container>();
test->reserve(maxEntites);
for(size_t i = 0; i < maxEntites; ++i)
{
test->emplace_back(std::make_shared<Entity>());
}
std::random_device rd;
std::mt19937 gen(rd());
size_t cycles = 0;
while(test->size() > 0)
{
std::uniform_int_distribution<size_t> dis(0, test->size());
size_t counter = 0;
while(test->size() > 0 && counter < ps)
{
test->pop_back();
++counter;
}
++cycles;
if(cycles % 7 == 0)
{
std::cout << "Inflating..." << std::endl;
while(test->size() < maxEntites)
{
test->emplace_back(std::make_shared<Entity>());
}
}
std::this_thread::sleep_for(std::chrono::seconds(1));
printContainerInfo(test);
std::cout << std::endl;
}
return 0;
}
메모리가 해제되지 않은 것을 어떻게 알 수 있습니까? –
작업 관리자의 커밋 크기 또는 Sysinternals의 RAMMap의 "total memory"가 표시됩니다. – kreuzerkrieg
@kreuzerkrieg 해제 된 메모리는 실행중인 프로세스에서 실제로 OS로 반환되지 않습니다. 당신은 작업 관리자에서 그것을 볼 수 없을 것입니다. valgrind 같은 도구를 사용하여 메모리 누수를 감지하십시오. –