服务器vector内存泄漏排查过程

        在开发过程中出现内存泄漏反复排查具体原因,终于定位到是因为vector操作不当引起内存泄漏,再次记录此次过程

struct FieldInfo {

    std::string value;

    std::vector<std::string> multi_value;

    bool valid_discrete_feature_ = false;

    FieldInfo() {

    }

    FieldInfo(std::string& single_value) : value(single_value) {

    }

};

std::vector<FieldInfo> ad_fields(FLAGS_max_feature_count); (FLAGS_max_feature_count = 1024)

vector主要就是保存上面的结构体,上面结构体相对简单,主要是给value赋值,代码如下:

ad_fields[FIELD_INDUSTRY_LEVEL1_ID].value = location_result.industry_level1_id;

(直接使用ad_fields位置 直接赋值给其内部结构体value)

ad_fields向量需要循环引用赋值(具体是业务需要对ad级别特征不断填充、覆盖)

每次对ad_fields填充前进行了clear()操作。

定位到内存泄漏在

ad_fields[FIELD_INDUSTRY_LEVEL1_ID].value = location_result.industry_level1_id;

字符串赋值 导致内存不断上涨。但是注释掉这些赋值代码,内存就不在上涨了。

我开始怎么也想不明白,由于使用std::string,感觉字符串不应该产生内存泄漏。(字符串长度越大,内存上涨越快)

查看std::string赋值构造函数(实现如下)

1、本身长度大于拷贝目标的长度,自己拷贝到已指向字符串位置。无需释放,重新申请内存

2、本身长度小于拷贝目标的长度,需要释放到自己内存,在重新申请,之后进行字符串的拷贝。

/**
 * 赋值构造函数,调用了assign函数
 */
basic_string& operator=(const basic_string& __str) { return this->assign(__str); }

/**
 * 调用了_M_assign函数
 */
basic_string& assign(const basic_string& __str) {
    this->_M_assign(__str);
    return *this;
}

/**
 * 赋值的核心函数
 */
template <typename _CharT, typename _Traits, typename _Alloc>
void basic_string<_CharT, _Traits, _Alloc>::_M_assign(const basic_string& __str) {
    if (this != &__str) {
        const size_type __rsize = __str.length();
        const size_type __capacity = capacity();

        /**
         * 如果capacity不够用,需要进行重新分配
         */
        if (__rsize > __capacity) {
            size_type __new_capacity = __rsize;
            pointer __tmp = _M_create(__new_capacity, __capacity);
            _M_dispose();
            _M_data(__tmp);
            _M_capacity(__new_capacity);
        }

        /**
         * 将__str指向的内存拷贝到当前对象指向的内存上
         */
        if (__rsize) this->_S_copy(_M_data(), __str._M_data(), __rsize);

        _M_set_length(__rsize);
    }
}

发现string是有内存释放的,不应该是字符串的赋值构造函数原因。

之后开始查看vector,代码如下:

std::vector<FieldInfo> ad_fields(FLAGS_max_feature_count);// FLAGS_max_feature_count为1024
说明构造函数就已经分配好空间和数据。源码实现主要这些代码
bool _Buy(size_type _Capacity)
		{	// allocate array with _Capacity elements
		this->_Myfirst() = pointer();
		this->_Mylast() = pointer();
		this->_Myend() = pointer();

		if (_Capacity == 0)
			return (false);
		else if (max_size() < _Capacity)
			_Xlen();	// result too long
		else
			{	// nonempty array, allocate storage
			this->_Myfirst() = this->_Getal().allocate(_Capacity);//申请内存
			this->_Mylast() = this->_Myfirst();
			this->_Myend() = this->_Myfirst() + _Capacity;//设置end位置,也就是容器capacity 大小
			}
		return (true);
		}

explicit vector(size_type _Count)
		: _Mybase()
		{	// construct from _Count * value_type()
		if (_Buy(_Count))
			{	// nonzero, fill it
			_TRY_BEGIN
			_Uninitialized_default_fill_n(this->_Myfirst(), _Count,
				this->_Getal());//默认填充
			this->_Mylast() += _Count;//设置last位置,也是就是size大小
			_CATCH_ALL
			_Tidy();
			_RERAISE;
			_CATCH_END
			}
		}

构造函数运行完 我们能够得到capacity = size = 1024 。

前面我们说过每次使用ad特征前都会对vector进行清理。代码如下

ad_fields.clear();//当调用clear函数后 capacity =1024 size=0

void clear() _NOEXCEPT
		{	// erase all
		this->_Orphan_all();
		_Destroy(this->_Myfirst(), this->_Mylast());//会调用类的析构函
		this->_Mylast() = this->_Myfirst();//size为空
		}

说明调用clear函数并没有对vector内存块进行释放。只不过把vector里的对象都清空了。

这样即使vector的局部对象析构了,也不会在对里头成员进行析构了,因为clear函数已经清空对象了。

之后我们来看上面排查到内存泄漏的位置,对vector内存对象直接操作并进行类成员赋值操作,

ad_fields[FIELD_INDUSTRY_LEVEL1_ID].value = location_result.industry_level1_id;//字符串的复制构造函数

value会根据字符串长短进行销毁自己内部内存申请新内存,但是没有最后一次的局部成员vector析构函数调用,导致string没有释放内存。

希望能对你有一些帮助,任何看是不可能,只要你最求其数源一定会得到你想要的答案。

    

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值