【问题标题】:placement new unique pointer放置新的唯一指针
【发布时间】:2017-09-29 01:15:40
【问题描述】:

这是我的第一篇文章。我希望它包含正确的、最少的信息。如果我遗漏了什么,请告诉我。

我正在尝试使用placement new 来提高以下代码块的效率(我想减少内存分配调用的次数,因为number_of_actions 变量很大(> 500k))。

首先,我使用了两个类,它们的关系可以概括为:

class txn {
public:
  int i;
  txn(): i(0) {};
};

class ActionClass {
private:
  txn* t;
public:
  ActionClass(txn* t): t(t) {};
  ~ActionClass() { delete t; }
};

我最初用于创建指向对象的指针数组的代码:

std::vector<std::unique_ptr<IBatchAction>> allocate_actions(unsigned int number_of_actions) {
  std::vector<std::unique_ptr<IBatchAction>> res;
  for (unsigned int i = 0; i < number_of_actions; i++) {
    // construct the action
    std::unique_ptr<IBatchAction> act = std::make_unique<ActionClass>(new TestTxn());

    res.push_back(std::move(act));
  }
  return res;
}

改用placement new后的代码:

std::vector<std::unique_ptr<IBatchAction>> allocate_actions(unsigned int number_of_actions) {
  std::vector<std::unique_ptr<IBatchAction>> res(number_of_actions);

  // allocate all of the memory for actions up front to amortize the cost.
  ActionClass* actions = 
    reinterpret_cast<ActionClass*>(new char[number_of_actions * sizeof(ActionClass)]);
  txn* txns = reinterpret_cast<txn*>(new char[number_of_actions * sizeof(TestTxn)]);

  // use placement new to initialize actions and assign them to unique_ptrs
  for (unsigned int i = 0; i < number_of_actions; i++) {
    // construct the action using placement new from the memory allocated above.
    res[i].reset(new(&(actions[i])) ActionClass(new(&(txns[i])) TestTxn()));
  }
  return res;
}

在 main.cpp 中,我只是多次调用上述函数,对其计时并返回 0。从上述函数返回的向量在循环迭代之间被破坏。结果,我在段错误之前得到以下堆栈跟踪:

#0  std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >::~unique_ptr (
    this=<optimized out>, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/unique_ptr.h:236
#1  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > (
    __pointer=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:93
#2  std::_Destroy_aux<false>::__destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*> (__last=<optimized out>, __first=0x7ffff7f06018)
    at /usr/include/c++/5/bits/stl_construct.h:103
#3  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*> (
    __last=<optimized out>, __first=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:126
#4  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*, std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > (__last=0x7ffff7fc9510, 
    __first=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:151
#5  std::vector<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >, std::allocator<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > >::~vector (this=0x7fffffffd910, 
    __in_chrg=<optimized out>) at /usr/include/c++/5/bits/stl_vector.h:424
#6  time_workload_creation (exp_conf=...) at start_batch/main.cc:18
#7  0x000000000040320c in main (argc=<optimized out>, argv=<optimized out>)
    at start_batch/main.cc:44

我对安置新的概念不熟悉。以下是我用来编写上述代码的:

  1. Can I use placement new to reset an object within a shared_ptr? -- 使用 reset 来分配使用placement new 创建的新对象
  2. placement new on shared_ptr make seg fault when delete——一个非常相似的问题。这个问题的答案对我有帮助:(

我可能在做一些明显错误的事情,但我想不通。帮助?如果您需要 main(简化)中的实际代码,这里是(actions.h 包含我讨论过的函数)。

#include "actions.h"

#include <chrono>
#include <vector>

void time_workload_creation(unsigned int act_num) {
  std::chrono::system_clock::time_point time_start, time_end;
  std::vector<double> results;
  for (unsigned int i = 0; i < 10; i++) {
    time_start = std::chrono::system_clock::now();
    auto workload = allocate_actions(act_num);
    time_end = std::chrono::system_clock::now();
    results.push_back(
        std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_start).count());
  }

  for (unsigned int i = 0; i < 10; i++) {
    std::cout << i << "\t\t" <<  
      act_num << "\t\t" <<  
      results[i] << "\t\t" <<  
      act_num / results[i] << std::endl;
  }
};

int main(int argc, char** argv) {
  time_workload_creation(1000000);
  return 0;
}

编译使用:gcc 版本 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)

【问题讨论】:

    标签: c++ segmentation-fault unique-ptr


    【解决方案1】:

    更改为使用新位置后的代码:
    [...]

    此代码具有未定义的行为。您使用new[] 创建一个char 数组,然后重置N 个unique_ptr 对象,这将尝试删除N 个不同的ActionClass 对象。你希望它如何工作?您需要使用delete[] 来释放char 数组,而不是对char 数组的块使用N 个delete 操作。

    同样,每个ActionClass 都会尝试deletetxn 对象,但这也是完全未定义的,因为您没有分配N 个txn 对象,而是分配了另一个char 数组。

    这不是内存分配的工作方式。你不能分配一个大块然后释放它的一部分。 (嗯,您可以,但只能通过编写您自己的自定义(解除)分配函数或分配器)。

    【讨论】:

    • 好吧,他至少应该为unique_ptr 使用自定义删除器,以避免在每个元素上调用delete
    • @EvanTeran 这将改变函数的返回类型(如果这是一个选项,有更好的方法来解决它)。而且不会删除char 数组,所以它会泄漏内存。
    • 我同意你的评价;我只是在介绍选项。毕竟,处理内存泄漏可能是可能的,但很可能不值得。
    • 谢谢!我想我的分配想法是错误的。你有什么好的指针可以用来阅读为什么“分配一个大块并在其中释放小块”不起作用吗?总的来说不会有内存泄漏吧?
    • @Ebstein 的原因与您不能执行 void* p = malloc(3); free(2 + (char*)p); free(1+(char*)p); free(p); 的原因相同 如果您使用 new[] 分配 char 的数组,那么您必须使用delete[]。你不能只是把它切碎并返回它的任意部分,底层的malloc 怎么知道你到底想做什么,或者你还在使用哪些内存?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多