【问题标题】:fstreams not deleted when delete is called in release mode在发布模式下调用 delete 时 fstreams 未删除
【发布时间】:2014-10-29 03:11:19
【问题描述】:

我遇到了一个有趣的问题,这似乎是由于 Visual Studio 2013 在调用删除运算符时实际上并未删除 fstreams 的结果。下面的代码是一个简单的程序,当在 Visual Studio 2013 中使用默认调试模式编译时,它的执行完全符合预期。当代码在发布模式下编译时(打开生成调试信息以便可以进行一些调试),删除操作符的行为取决于删除操作符的内容。如果 delete 运算符包含一个 cout,它会立即为分配的部分内存调用,如果它不包含一个 cout,它根本不会被调用,直到它运行 crtexe.c 中的退出函数,到它的时候调用 foo 互斥锁处于某种无效状态,导致“访问冲突读取位置 0xFEEEEF6”。

#include <cstdlib>

#include <fstream>
#include <mutex>
#include <iostream>
using namespace std;

mutex foo;

void* operator new(unsigned int size)
{
    lock_guard<mutex> memoryLock(foo);
    void* alloc = malloc(size);
    cout << "Allocating " << size << " bytes for " << alloc << endl;
    return alloc;
}

void* operator new[](unsigned int size)
{
    lock_guard<mutex> memoryLock(foo);
    void* alloc = malloc(size);
    cout << "Allocating " << size << " bytes for " << alloc << endl;
    return alloc;
}

void operator delete(void* ptr)
{
    lock_guard<mutex> memoryLock(foo);
    cout << "Deallocating " << ptr << endl;
    free(ptr);
}

void operator delete[](void* ptr)
{
    lock_guard<mutex> memoryLock(foo);
    cout << "Deallocating " << ptr << endl;
    free(ptr);
}

int main()
{
    cout << "Address of Mutex: " << &foo << endl;
    cout << "Creating fstream... " << endl;
    fstream *blarg = new fstream("blarg.txt", ios::out);
    cout << "Deleteing fstream..." << endl;
    delete blarg;
    cout << "fstream deleted..." << endl;

    cout << "Exiting main..." << endl;
    return 0;
}

这个程序输出:

Address of Mutex: 00DD6658
Creating fstream...
Allocating 192 bytes for 003ABB00
Allocating 8 bytes for 00392A40
Deleteing fstream...
Deallocating 003ABB00
fstream deleted...
Exiting main...

然后因访问冲突而崩溃。

如果您在 delete 运算符内设置断点,您将看到对它的调用完全按照输出指示发生。如果您将这些断点保留在其中,但注释掉 cout 行,则被调试的对象在遇到 crtexe.c 中的退出函数之前永远不会遇到 delete 运算符。

最大的问题是,由于某种原因,fstream(或至少不是它的全部资源)在看起来其他全局资源已被破坏后被删除,这意味着 fstream 的动态分配的内存对象正在调用自定义删除使用的资源无效后的运算符。

我应该能够为我的应用程序生成一个解决方法,此时这真的不是我关心的问题,我只是想了解正在发生的事情背后的“原因”。为什么 fstream 的析构函数和/或内存的释放会被延迟?如果有的话,可以做些什么来防止这种行为?我可以期望其他类可能会产生这种行为吗?

【问题讨论】:

  • 了解事件发生的顺序会很有用。互斥锁类有可能在其构造函数/析构函数中调用 New 和 Delete 吗?这会在这里造成问题。我会在 main 的每一行之前以及您的 new 和 delete 运算符中放置一些 COUT。
  • 在程序中使用 couts 时发现了一些有趣的效果。老实说,这变得很奇怪。使用新信息编辑问题。
  • 这些信息至少能让我了解如何在不删除所有资源的情况下删除对象。无论 fstream 分配的 8 字节是什么,都可能以某种方式排队等待应用程序退出时被删除。我仍在试图解决为什么在删除 cout 时调试器没有命中 operator delete 的问题。操作员实际上没有运行,还是调试器没有机会看到它?
  • 刚刚尝试添加“this_thread::sleep_for(chrono::seconds(5));”在退出线程之前,看看是否给定时间另一个线程会执行这 8 个字节的释放,没有这样的运气。
  • 尝试将 cout 放在释放器中的锁之前。我比以前更确信您的互斥对象正在调用您的删除运算符。

标签: c++ memory-management visual-studio-2013 fstream


【解决方案1】:

我可以期望其他类可能会产生这种行为吗?

如果该类有自定义的operator delete() 方法,则全局operator delete() 函数is not called

在这种情况下,fstream 可能有这样的方法,并且您的全局函数仅在操作系统运行时准备好释放对象时才被调用。也有可能 Visual C++ 正在做一些完全不同的事情。

【讨论】:

    【解决方案2】:

    如果打印出来

    class MyMutex : public mutex
    {
    public:
        virtual ~MuMutex () { cout << "Deleting Mutex" << endl ; }
    } mutex ;
    

    问题是互斥锁正在调用您的删除函数并试图锁定自己。

    【讨论】:

    • 没有。仅仅因为调用了析构函数并不意味着调用了删除运算符。仅对动态分配的对象调用删除运算符。如果互斥体使用动态分配的内存,我们会看到 new 运算符在输出互斥体内存地址的初始 cout 之前被调用。此外,如果我为 fstream 以外的东西分配内存然后删除它,问题就会消失。问题明显与 fstream 相关,而不是互斥体。
    • 我运行了代码,但它唯一显示的是,正如我所料,互斥锁在 fstream 分配的 8 字节数据被删除之前被删除。如果互斥体的析构函数正在调用 delete,它将继续调用它,而不管是否分配了 fstream。因为当 fstream 被删除时问题就消失了,所以不能将问题归咎于互斥锁,它会在其析构函数期间继续调用 delete。
    • 将你的 cout 语句移到锁之前,你应该会看到问题。
    • 你是对的,我发现了问题。我发现删除操作符被调用的内存地址与 fstream 分配的 8 字节内存相同,然后在尝试锁定互斥锁时崩溃......这意味着 fstream 的一些资源在 fstream 的删除中幸存下来,并且仅在互斥体被解构后才被删除。
    猜你喜欢
    • 2010-09-20
    • 2021-12-11
    • 1970-01-01
    • 2014-02-18
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 2011-01-05
    • 2014-07-13
    相关资源
    最近更新 更多