ROS节点内存泄漏问题调试

1 minute read

Published:

ROS节点内存泄漏问题调试.

data_pretreat_node节点内存泄漏问题

内存泄漏原因

原代码VisionData析构函数中if (!image) delete image;没有达到释放内存的目的,即VisionDatanew的内存在程序运行期间一直没有释放,导致内存泄漏。

// sensor_date/vision_data.hpp

class VisionData
{
public:
  VisionData()
  {
    image = new uchar[channel * width * height]();
  }
  ~VisionData()
  {
    if (!image)  delete  image;
  }

public:
  double time = 0.0;
  double seq = 0;
  int width = 600;
  int height = 600;
  int channel = 3;
  uchar *image;
};

代码修改

VisionData析构函数修改如下。

不在析构函数中进行image指针的内存释放,这样可以减少后面大量的内存拷贝操作,提高运行效率。

// sensor_date/vision_data.hpp

class VisionData
{
public:
  VisionData()
  {
    image = new uchar[channel * width * height]();
  }
  ~VisionData()
  {
    // if (!image)  delete  image;
  }
    
  void clear()
  {
    delete[] image;
  }

public:
  double time = 0.0;
  double seq = 0;
  int width = 600;
  int height = 600;
  int channel = 3;
  uchar *image;
};

修改VisionSubscriber::msg_callback函数如下(第7行与第25行)。

临时变量在析构时会调用delete,释放指针image所指向的内存,当通过push_back将指针复制到new_vision_data_中,指针image将指向无效内存。

因此直接在new_vision_data_中进行VisionData的构造,再对其末尾元素进行引用,这样不会改变指针所指向的存在空间,在对new_vision_data_进行clear等操作时,也可以对内存进行有效释放。

若不在析构函数中进行内存释放,则不会出现上述问题,使用临时变量对指针进行拷贝的操作也是可行的。

// subscriber/vision_subscriber.cpp

void VisionSubscriber::msg_callback(const sensor_msgs::ImageConstPtr &msg_ptr)
{
  buff_mutex_.lock();
    
  // VisionData avm_img;
  new_vision_data_.resize(new_vision_data_.size()+1);
  VisionData& avm_img=new_vision_data_.back();
    
  avm_img.time = msg_ptr->header.stamp.toSec() + delay_time_to_ins_;
  avm_img.seq = msg_ptr->header.seq;
  cv_bridge::CvImageConstPtr cv_ptr;
  try {
    cv_ptr = cv_bridge::toCvCopy(msg_ptr, sensor_msgs::image_encodings::BGR8); 
  } catch (cv_bridge::Exception &ex) {
    ROS_ERROR("cv_bridge exception in rgbcallback: %s", ex.what());
    exit(-1);
  }
    
  ...

  memcpy(avm_img.image, cv_ptr->image.data, sizeof(uchar) * avm_img.channel * avm_img.width * avm_img.height);

  // new_vision_data_.push_back(avm_img);
    
  buff_mutex_.unlock();
}

data_pretreat_flow.cpp中的相关代码修改如下。

在删除vision_data_buff_队列前面的数据时,需要对内存进行释放,因此调用clear函数。

第28行将vision_data_buff_最前面的元素拷贝给current_vision_data_时,需要对current_vision_data_原有数据指针指向的内存进行释放。

// data_pretreat/data_pretreat_flow.cpp

bool DataPretreatFlow::ReadData()
{
  ...

  if (start_ins_time > vision_time) {
    vision_data_buff_.front().clear();
    vision_data_buff_.pop_front();
    return false;
  }
  ...
      
  if (!sensor_inited) {
    if (!valid_ins) {
      vision_data_buff_.front().clear();
      vision_data_buff_.pop_front();
      return false;
    }
    ...
  }
  return true;
}

bool DataPretreatFlow::ValidData()
{
  current_vision_data_.clear();
  current_vision_data_ = vision_data_buff_.front();
  ...

  if (diff_ins_time < -0.05) {
    vision_data_buff_.front().clear();
    vision_data_buff_.pop_front();
    return false;
  }
  ...

  vision_data_buff_.pop_front();
  ins_data_buff_.pop_front();

  return true;
}

问题解决

修改后内存泄漏问题解决,如下图所示。

memory_leak_solve