【问题标题】:Custom memory manager works fine in release mode, but not in debug mode自定义内存管理器在发布模式下工作正常,但在调试模式下不行
【发布时间】:2020-09-10 00:36:03
【问题描述】:

我正在尝试实现一个简单的内存管理器来试验内存池机制并跟踪内存泄漏。我正在使用 VS2019,到目前为止,我的代码仅在发布 x86 模式下运行。将构建配置更改为调试或将目标平台设置为 x64,会导致访问冲突错误。具体来说,在调试模式下,计算可用池大小的以下行会引发异常“引发未处理的异常:读取访问冲突。p 为 nullptr。”

    return p->end - p->next;

我的问题是为什么发布模式可以正常工作以及如何修复调试模式配置中的访问冲突?对实施的任何评论、建议或评估也表示赞赏。这是我当前的代码:

#include <iostream>
#include <string>
#include <array>


struct free_store {
    char* next;
    char* end;
};

const int POOL_SIZE {500};

// memory counters
size_t alloc_count {0};
size_t dealloc_count {0};

// containers to trace memory info
std::array<void*, POOL_SIZE> m_adrs; // addresses of the reserved memory
std::array<size_t, POOL_SIZE> m_sizes; // sizes of the reserved memory
std::array<std::string, POOL_SIZE> m_adrs_str;

// memory management functionality
using pool = free_store;
pool* create_pool(size_t);
void destroy_pool(pool*);
size_t available_pool(pool*);
void* alloc_memory(pool*, size_t);
void free_memory(void* memory);

// test class
class Student {
private:
    const size_t NUM_OF_COURSES {5};
    double* scores;

public:
    Student() {
        scores = new double[NUM_OF_COURSES];
    }
    ~Student() {
        // uncomment to prevent leaks
        // delete[] scores;
    }
};

// customizing new and delete 
pool* my_pool = create_pool(sizeof(Student) * POOL_SIZE);

void* operator new(size_t sz) {
    //void* ptr {malloc(sz)};
    void* ptr = alloc_memory(my_pool, sz);
    return ptr;
}
void operator delete(void* ptr) {
    free_memory(ptr);
    //free(ptr); // I destroy the pool in the end of program
}

void test_1() {
    int* id {new int(208748301)};
    double* pass {new double(15)};
    double* bounds = {new double[2] {0, 20}};
    Student* st1 = new Student;
    Student* st2 = new Student;
    delete pass;
    delete[] bounds;
    delete st1;
    delete st2;
}

void display_results();


int main() {

    // test allocation/deallocation
    test_1();

    // show results
    display_results();

    // release resources
    destroy_pool(my_pool);

    system("pause");

} // end main function



pool* create_pool(size_t size) {
    pool* p = (pool*)malloc(size + sizeof(pool));
    p->next = (char*)&p[1];
    p->end = p->next + size;
    return p;
}

void destroy_pool(pool* p) {
    free(p);
}

size_t available_pool(pool* p) {
    return p->end - p->next;
}

void* alloc_memory(pool* p, size_t sz) {
    std::cout << "Pool Available: " << available_pool(my_pool) << " bytes" << std::endl;
    if(available_pool(p) < sz) return nullptr;
    void* memory = (void*)p->next;
    p->next += sz;
    m_adrs.at(alloc_count) = memory;
    char buf[128];
    sprintf_s(buf, "%p", memory);
    m_adrs_str.at(alloc_count) = buf;
    m_sizes.at(alloc_count) = sz;
    alloc_count++;
    return memory;
}

void free_memory(void* memory) {
    auto it {std::find(m_adrs.begin(), m_adrs.end(), memory)};
    auto idx {std::distance(m_adrs.begin(), it)};
    m_adrs.at(idx) = nullptr;
    dealloc_count++;
}

void display_results() {
    std::cout << std::endl;
    std::cout << "Number of allocations: " << alloc_count << std::endl;
    std::cout << "Number of deallocations: " << dealloc_count << std::endl << std::endl;

    std::cout << "Sizes of the reserved memory:" << std::endl;
    for(size_t i {}; i < m_sizes.size(); i++) {
        if(m_adrs_str[i] != "") {
            std::cout << "Address: " << m_adrs_str[i] << ", Size: " << m_sizes[i] << " bytes" << std::endl;
        }
    }

    std::cout << std::endl;
    std::cout << "Addresses of leaks:" << std::endl;
    for(const auto& a : m_adrs) {
        if(a != nullptr) {
            std::cout << a << std::endl;
        }
    }
}

Update-1

我发现将运行时库选项设置为/MD 可以正确编译代码。因此,使用 CL 在命令提示符下编译和链接程序:

>cl /MD app.cpp

所以我想问题是这个选项如何解决内存访问问题。

【问题讨论】:

  • 您是否在调试器中单步执行了代码?在初始化my_pool 之前调用new 的全局替换。电话似乎来自std::array&lt;std::string, POOL_SIZE&gt; m_adrs_str;
  • 感谢您的关注。我猜想发布模式通过重新排列/优化我的代码以某种方式自动解决了这个问题,因为在发布模式下调试时,my_pool 被正确初始化,因此没有错误。那么有没有办法找出发布构建优化是什么?在重载new之前如何重新排列代码以初始化my_pool有什么想法吗?

标签: c++ visual-studio memory-management memory-leaks pool


【解决方案1】:

根据Microsoft documentation on Run-Time Library options/MD 将程序链接到MSVCRT.lib,这反过来又使链接器能够解析外部引用。这显然修复了调试模式引发的未处理异常(内存访问冲突错误)。

我不确定是否应该将此标记为问题的答案或其他详细信息,因此如果您认为有必要,请随时编辑。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    相关资源
    最近更新 更多