9
回答
C++中new引发的异常
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

我很不喜欢C++自带的库里的异常类,因为不能带调试信息,所以有些不得不使用其异常的地方抓住它抛出的异常然后抛出一个自己的,

例如抓住bad_alloc,然后抛一个自己的 MemoryException,

但是今天突然发现一个问题,我的MemoryException中使用了QString携带信息,它跟string一样都要用到堆上的空间,如果因为空间不足抛出bad_alloc,那么这个MemoryException在构造中恐怕也要抛出bad_alloc,本意是想做一个好一点的异常类,但是真要发生异常恐怕什么用都没有。

那我的问题就是:

1.象内存不足这种情况,是否允许捕捉标准异常并抛出自己的异常类?该异常类是否允许在构造过程中使用new或malloc?

2.内存不足触发bad_alloc时,通常的做法应该是什么?释放内存池?退出程序?

3.关于满天飞的new,捕捉分配失败的代码比较零散,是否应该将绝大部分使用new的类挪到内存池上去以统一管理?

就是这些问题啦

举报
共有9个答案 最后回答: 5年前

楼主先说一下,C++我只是比较熟悉,都谈不上熟练,因为对C++转为汇编的激励并没有研究过。不过C++和C比较类似(非面向对象的一些方面),以及OS和机器还是不变的,所以有些观点,你可以借鉴一下。先说下几个观点:

1、所谓异常,就是因为你的代码执行中触发了一些情况,比如硬件的问题,比如你除了0等等导致OS给出了一个中断信号。而这个中断信号对应的响应函数,(系统默认的OS或你的编译器提供的标准库或者你提供的基本库)并不是你进程的空间,也就是说你的代码的异常导致的异常处理函数的出现不会因为你的代码问题,而无法执行。

2、内存不足引发的解决方案这个得看具体项目的设计目标。有的是直接关闭,例如关键数据没有空间提供,你所有的进程线程存在已经没有意义。而有的是关闭目前的子任务,这个很常见。方法没有统一的,但原则就是,尽可能让更多有效可以运转的代码良好的活着。将问题的影响压缩到最小。

3、漫天飞的new是不应该的。即便是面向对象,更多的应该使用引用的方式,而不是函数(对象的方法里)动不动就有个局部对象声明并利用。不过,对于内存管理上,这个更应该移动给OS处理,绝大多数情况,只要自己处理异常响应的函数即可不需要自己处理内存管理模块,除非你是一个服务级的应用,你不知道需要提供服务的数量和实际并发度,而单个服务完成和新服务来时,有额外的free以及new,此时显然你可以复用以前的空间。我个人认为,除非到性能优化阶段,否则自己处理内存管理没有必要。

最后C++我不清楚,本来昨天就应该发linux 下C编程的新帖子,因为点个人的事情,耽误了,那帖子里列出了一个C里面的异常捕捉的方法。

--- 共有 3 条评论 ---
幻の上帝满天飞的new是不应该,不过不用局部对象还放到类里面?照这么说不是放到全局对象更好? 5年前 回复
幻の上帝C++的异常比这里的层次高的多,运行时自带类型信息,而且只管同步异常。除0异常这种明显不在此列。 5年前 回复
吃土的汉子大哥码字辛苦了,小弟拜谢~ new引发的异常多半是我C++的书没看全,回头继续修炼去 6年前 回复

贴几个我做的简单异常类

class Exception{
    public:
        Exception(const QString &pReason):reason(pReason){}
        Exception(const char *pReason) {
            reason=QString::fromAscii(pReason);
        }

        QString what(){ return reason; }
        QString reason;
};

class MemoryException: public Exception {
public:
    MemoryException(const QString &pReason):Exception(pReason){}
    MemoryException(const char *pReason):Exception(pReason){}
};

class TempFileException:public Exception {
public:
    TempFileException(const QString &pReason):Exception(pReason){}
    TempFileException(const char *pReason):Exception(pReason){}

    //Construct an exception from errno.
    TempFileException(int errorNo):Exception(NULL) {
        if(!errorNo) {
            reason ="Some stupid guy throw me and I don't know why";
            return;
        }
        reason =QString::fromAscii(strerror(errorNo));
    }
};

1.2 记得 effective C++里对这个有些讨论,讲的很细, 值得参考. 内存不足异常, 个人觉得直接退出程序得了. 否则你就需要设计 异常处理,在这个异常处理里 让程序想办法 退还出一部分内存来. 每个new的地方都这样考虑,非常的痛苦,所以直接重启好了.
或者你使用new (nothrow) ,然后一个个判断NULL指针??
3. 内存池应该只关心大小几乎相同的类对象,不然内存池相比new没效率优势. 若只是为了让new更好看而对每一个new使用内存池,我表示 =.=||

一点愚见.  最好不要让new失败. 当服务器内存不足而导致这样的问题, 就不必考虑让服务器能正常工作下去了. 换以冗余服务器的方式. 内存占用高也应该设下报警.

--- 共有 2 条评论 ---
Lunar_Lin回复 @千羽鸣 : 最好不要产生new异常的情况吧. 或者new(nothrow)了. 你可以先找os要一块堆or几个预留着, 专门用来new 你的异常也不错.这样Runtime库主堆耗尽OS的内存, 至少还能有你这个堆可以记录下东西. 6年前 回复
吃土的汉子直接不捕捉new的异常,那么程序出问题自然就退出了,只是可能没有日志记录啥的…… 6年前 回复

我也遇到过这样的问题,但是不知道楼主是怎样的情况导致内存不足

1. 内存有空间,但是由于存在大量的内存碎片导致不能申请?

2. 怎么知道我的new失败是由于内存碎片导致申请空间失败?

3. 但是我的程序要继续运行下去,就必须要申请该空间,那么怎么处理呢?因为已经没有内存了啊?

引用来自“西昆仑”的答案

我也遇到过这样的问题,但是不知道楼主是怎样的情况导致内存不足

1. 内存有空间,但是由于存在大量的内存碎片导致不能申请?

2. 怎么知道我的new失败是由于内存碎片导致申请空间失败?

3. 但是我的程序要继续运行下去,就必须要申请该空间,那么怎么处理呢?因为已经没有内存了啊?

@西昆仑 

我的这个问题只是“假设出现内存不足”,因为我是学信息安全的小本,比较喜欢在这些极端情况上纠结。

通过昨晚的查阅,现在掌握到的信息:

1.new在“内存请求无法满足”时会抛出bad_alloc异常,对于内存请求,可用空间为 空闲物理内存+空闲虚拟内存 的大小,这个值应该是很大的。new在“请求无法满足”时抛出异常,而非“无内存”时抛出异常

2.内存碎片不一定就会导致内存分配失败,如果还有虚拟内存,现代的OS可以将物理内存中的内存挪出来的,因此内存碎片可能会导致可用物理内存吃紧,但不见得就会使new失败

3.new接收的参数是size_t类型,多数编译器上为 unsigned long,有人曾经在内存分配时使用了有符号类型,传入了一个负数,导致实际内存需求过大而失败

如果楼上真的遇到了内存不足,那么我建议分这么几个步骤查出问题:

1.程序设计时理论内存需求量是多少?

2.程序运行时实际吃内存量是多少(累加)?

3.该问题在何时出现?第一次分配还是若干次分配之后?如果是第一次,则可能是内存计算有问题

4.代码中出问题的分配语句在何处,如果不好找出问题 and 不涉及商业机密,还请将其贴出来

 

引用来自“千羽鸣”的答案

引用来自“西昆仑”的答案

我也遇到过这样的问题,但是不知道楼主是怎样的情况导致内存不足

1. 内存有空间,但是由于存在大量的内存碎片导致不能申请?

2. 怎么知道我的new失败是由于内存碎片导致申请空间失败?

3. 但是我的程序要继续运行下去,就必须要申请该空间,那么怎么处理呢?因为已经没有内存了啊?

@西昆仑 

我的这个问题只是“假设出现内存不足”,因为我是学信息安全的小本,比较喜欢在这些极端情况上纠结。

通过昨晚的查阅,现在掌握到的信息:

1.new在“内存请求无法满足”时会抛出bad_alloc异常,对于内存请求,可用空间为 空闲物理内存+空闲虚拟内存 的大小,这个值应该是很大的。new在“请求无法满足”时抛出异常,而非“无内存”时抛出异常

2.内存碎片不一定就会导致内存分配失败,如果还有虚拟内存,现代的OS可以将物理内存中的内存挪出来的,因此内存碎片可能会导致可用物理内存吃紧,但不见得就会使new失败

3.new接收的参数是size_t类型,多数编译器上为 unsigned long,有人曾经在内存分配时使用了有符号类型,传入了一个负数,导致实际内存需求过大而失败

如果楼上真的遇到了内存不足,那么我建议分这么几个步骤查出问题:

1.程序设计时理论内存需求量是多少?

2.程序运行时实际吃内存量是多少(累加)?

3.该问题在何时出现?第一次分配还是若干次分配之后?如果是第一次,则可能是内存计算有问题

4.代码中出问题的分配语句在何处,如果不好找出问题 and 不涉及商业机密,还请将其贴出来

 

今天才看到你的回复,首先不好意思,现在才回复你,其次,谢谢你热心的回复。

1.我是在项目中遇到了这样的问题,本身程序的应用场景是不会出现这样的场景的,但是我模拟了极端的压力情况,才会导致这个问题,而且问题比较难以重现,需要1,2天才会重现一次。

2. 你说设计时的理论内存,由于我是消息中间件,如果消息的发送频率高于接收频率,那么内存一直增长,所以必须使用一些策略,控制这种情况下的一直增长。

3. 我出现的问题时,内存使用不过800M,机器内存有4G,所以初步鉴定是没有内存泄露的,但是NEW确实失败。

4. 你说代码的分配语句,http://www.oschina.net/question/100374_52237,在这个链接下有我的代码。

 

顶部