Valgrind报告map中存在内存泄漏,即使我没有执行动态分配。

3
我运行了valgrindAddressSanitizer,在我制作的一个大项目上,它们都报告了许多地方存在内存泄漏问题。但是,在程序中我没有使用动态内存分配(没有调用new(),也没有使用指针(甚至智能指针),只使用了引用)。
不幸的是,原始代码太大了,并且受到保密条款的约束,因此我无法完全分享它。我尝试创建了一个最小的示例,但未成功。
基本上,我的代码库中有许多地方存在内存泄漏问题。
例如,考虑以下代码,其中我创建了一个新的映射(map),其条目是前一个映射std::map<WAL_Processed_Key, std::vector<double> > tmp_map中相同条目值的平均值。
{
    std::map<WAL_Processed_Key, std::vector<double> > tmp_map
    ...

    std::map<WAL_Processed_Key, double> reduced_map;

    for (auto const& entry : tmp_map)
    {
        reduced_map[entry.first]      // Line 98
            = std::accumulate(
                entry.second.begin(),
                entry.second.end(), 0.0) / entry.second.size();
    }

    return WAL_Map(
            reduced_map, start, end, time_granularity, grid_size);
}

AddressSanitizer 提示错误:

Direct leak of 112 byte(s) in 1 object(s) allocated from:
    #0 0x7f08f7978532 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99532)
    #1 0x7f08f6dc47a7 in __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > >::allocate(unsigned long, void const*) (/home/idgc/project-share/mns/common_lib/Debug/libmns.so+0x42d7a7)
    #2 0x7f08f6dc412f in std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > > >::allocate(std::allocator<std::_Rb_tree_node<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > >&, unsigned long) (/home/idgc/project-share/mns/common_lib/Debug/libmns.so+0x42d12f)
    #3 0x7f08f6dc3574 in std::_Rb_tree<mns::WAL_Map::WAL_Processed_Key, std::pair<mns::WAL_Map::WAL_Processed_Key const, double>, std::_Select1st<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> >, std::less<mns::WAL_Map::WAL_Processed_Key>, std::allocator<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > >::_M_get_node() (/home/idgc/project-share/mns/common_lib/Debug/libmns.so+0x42c574)
    #4 0x7f08f6dc1c48 in std::_Rb_tree_node<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> >* std::_Rb_tree<mns::WAL_Map::WAL_Processed_Key, std::pair<mns::WAL_Map::WAL_Processed_Key const, double>, std::_Select1st<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> >, std::less<mns::WAL_Map::WAL_Processed_Key>, std::allocator<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > >::_M_create_node<std::piecewise_construct_t const&, std::tuple<mns::WAL_Map::WAL_Processed_Key const&>, std::tuple<> >(std::piecewise_construct_t const&, std::tuple<mns::WAL_Map::WAL_Processed_Key const&>&&, std::tuple<>&&) (/home/idgc/project-share/mns/common_lib/Debug/libmns.so+0x42ac48)
    #5 0x7f08f6dc005c in std::_Rb_tree_iterator<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > std::_Rb_tree<mns::WAL_Map::WAL_Processed_Key, std::pair<mns::WAL_Map::WAL_Processed_Key const, double>, std::_Select1st<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> >, std::less<mns::WAL_Map::WAL_Processed_Key>, std::allocator<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > >::_M_emplace_hint_unique<std::piecewise_construct_t const&, std::tuple<mns::WAL_Map::WAL_Processed_Key const&>, std::tuple<> >(std::_Rb_tree_const_iterator<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> >, std::piecewise_construct_t const&, std::tuple<mns::WAL_Map::WAL_Processed_Key const&>&&, std::tuple<>&&) (/home/idgc/project-share/mns/common_lib/Debug/libmns.so+0x42905c)
    #6 0x7f08f6dbed1a in std::map<mns::WAL_Map::WAL_Processed_Key, double, std::less<mns::WAL_Map::WAL_Processed_Key>, std::allocator<std::pair<mns::WAL_Map::WAL_Processed_Key const, double> > >::operator[](mns::WAL_Map::WAL_Processed_Key const&) /usr/include/c++/5/bits/stl_map.h:483
    #7 0x7f08f6dbd886 in mns::WAL_Map::reduce_map(std::map<mns::WAL_Key, double, std::less<mns::WAL_Key>, std::allocator<std::pair<mns::WAL_Key const, double> > >, double, boost::optional<double>) ../Types/WAL_Map.cpp:98
    #8 0x4ef2f3 in mns::PMS_Algorithm_Manager::process() ../PMS_Algorithm_Manager.cpp:223
    #9 0x54ce7c in main ../main.cpp:262
    #10 0x7f08f5d4c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

reduced_map被传递为const引用来构建一个新的WAL_Map,然后超出作用域,因此它的析构函数应该自动调用并释放内部内存。

同样,在程序中没有任何地方使用动态分配或指针(当然,除了STL的内部),因此我无法解释这些“泄漏”,特别是它们由两个不同的工具报告。只有(某些)映射是泄漏的,我在代码周围使用了几个setvector,它们似乎不是问题。

我知道valgrind有时会报告错误,我的问题似乎类似于此答案。但是,export GLIBCXX_FORCE_NEW对报告没有影响。

那么我的问题是:如果没有使用指针或内存分配,是否可能存在内存泄漏(假设STL中没有错误,这将令人惊讶)?有没有办法证明这些是假阳性?

环境:

  • Ubuntu 16.04.2 LTS
  • g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
  • glibc 2.23

WAL_Processed_Key是什么?它可能会泄漏吗? - NathanOliver
它告诉你在/Types/WAL_Map.cpp的第98行发生了泄漏。 - Öö Tiib
抱歉我没有表达清楚。第98行是for循环内的行(在map中插入)。而WAL_Processed_Key不会泄漏,它只是一堆数据的元组。 - user2891462
1个回答

1
解决了!问题在于地图的键的operator<没有提供严格弱序(有关更多信息,请参见此StackOverflow问题)。
我有这样的东西:
struct WAL_Processed_Key
{
    int foo;
    int bar;
    bool operator<(const WAL_Processed_Key& o)
    {
        // Bad implementation, no strict weak ordering!
        return foo < o.foo || bar < o.bar;
    }
};

当时我应该得到像这样的东西:
struct WAL_Processed_Key
{
    int foo;
    int bar;
    bool operator<(const WAL_Processed_Key& o)
    {
        if (foo < o.foo) return true;
        if (o.foo < foo) return false;
        if (bar < o.bar) return true
        if (o.bar < bar) return false;
        return false;
    }
};

当比较运算符没有提供严格的弱序时,遍历map的行为会出现意外情况(例如,一个map可以报告map.size()大于其std::distance(map.begin(), map.end()))。我假设这是由于错误的比较实现导致map的析构函数无法访问所有内部元素进行删除。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接