[c++]一个不断增加内存的bug

185 views
Skip to first unread message

sagasw

unread,
Aug 20, 2009, 9:52:57 AM8/20/09
to pongba
遇到一个问题,有些进入死胡同,跑来求救。

场景如下:

一个windows的mfc程序会一直运行(里面有timer),但是运行时大约每秒有4k的内存增长,非常稳定。问题是如果一直跑的话,程序的堆栈一定会被耗光。

想知道怎么能判断这4k内存是从哪个对象来的?使用过以下判断方式:

使用过·CRT的memory leak检查,但是没有找到问题,而且也不像是动态申请的内存,倒像是某个栈上的对象,现象是只要一拖动窗口或者做个最大最小化,内存都会急剧降低。

想用boundchecker,可应用太庞大,根本启动不起来。

试过PDH相关函数,也没有用。



不知道大家对于此类问题有什么办法?

Tiny fool

unread,
Aug 20, 2009, 9:58:18 AM8/20/09
to pon...@googlegroups.com
调试嘛还是要胆大心细,对你说的这类完全没有头绪的问题,我建议采用二分法来查找。

先注释掉一般的功能代代码,看是否现象如故,如果如故,则问题在另一半代码,反之则在被注释的代码中。依次类推,先找到有问题的代码段再说。

有些内存持续增长问题无法被leak检测工具检测出来,比如你写个LRU,但是你忘了清掉老的失效的cache,那么代码本身没有什么泄露,只是因为逻辑写错了,没有进行清除而已。

2009/8/20 sagasw <sag...@gmail.com>

welfear

unread,
Aug 20, 2009, 10:02:08 AM8/20/09
to pon...@googlegroups.com
App Verifier呢?

2009/8/20 sagasw <sag...@gmail.com>:

XiongJia Le

unread,
Aug 20, 2009, 10:12:01 AM8/20/09
to pon...@googlegroups.com
把 Timer 去掉看看,是不是还持续增长...或者把相关功能一个个屏蔽掉看看是那个引起的...


可以做个定期重新启动自己的方法,以前做一个 Server 监视的 IT 用的 UI 也是 滴滴答答 的 mem leak 后来就 mem 大了自己重启一下自己...其实用户还是能接受的...

2009/8/20 sagasw <sag...@gmail.com>

alexey

unread,
Aug 20, 2009, 10:48:39 AM8/20/09
to TopLanguage
try Deleaker!

welfear

unread,
Aug 20, 2009, 11:14:00 AM8/20/09
to pon...@googlegroups.com
2009/8/20 Tiny fool <tiny...@gmail.com>:

> 调试嘛还是要胆大心细,对你说的这类完全没有头绪的问题,我建议采用二分法来查找。
> 先注释掉一般的功能代代码,看是否现象如故,如果如故,则问题在另一半代码,反之则在被注释的代码中。依次类推,先找到有问题的代码段再说。


好像和时间有关系,能不能先去掉timer看看?

> 有些内存持续增长问题无法被leak检测工具检测出来,比如你写个LRU,但是你忘了清掉老的失效的cache,那么代码本身没有什么泄露,只是因为逻辑写错了,没有进行清除而已。
>

一般来说,堆内存问题应该都会有提示。

>

ada...@gmail.com

unread,
Aug 20, 2009, 11:22:54 AM8/20/09
to TopLanguage
你看看有没这样的情况,FindFirstFile却没有FindClose的。打开了系统的资源却没有close的

redsea

unread,
Aug 20, 2009, 11:23:33 AM8/20/09
to TopLanguage
1. 加一个对象计数器, 哪种对象一直增加, 就是它了.
2. 申请比较多的类, 轮流内部增加一个 char buffer[65536], 看看内存增加是否加快, 很快就可以找出了.

hill wang

unread,
Aug 20, 2009, 12:49:43 PM8/20/09
to pon...@googlegroups.com
去掉最新加的功能,一步一步排查吧,内存问题还是比较麻烦的,虽然效率不高,但是不会漏掉问题

2009/8/20 welfear <wel...@gmail.com>

sagasw

unread,
Aug 20, 2009, 7:53:55 PM8/20/09
to pon...@googlegroups.com
谢谢前面兄弟们的回复,昨天晚上写的比较仓促,有些背景没有介绍全面,这里补充一下。

这个问题是来自福特的cst issue,着急要搞定。而且应该不是GDIobject和userobject的问题,因为运行时基本上不需要什么复杂的绘画或者键盘鼠标操作,就是一个timer跑一些任务。但是在程序运行的另外一个部分会有handle的泄露,通过sysinternals的handle工具,发现是file的问题,这是另外一个问题了。其它的应该没有,我编了一个小的lua脚本检查这个kernal object的问题(msdn上有片文档专门介绍了哪些函数可能导致handle泄露),是从代码中检索所有可能打开handle的函数,在附件里了。另外也不是new delete的问题,sysalloc也一样,都是调用很少,而且debug log可以发现,运行时基本不用那些分配函数。

这是一个比较大的工程,而且以前从没接触(跟新的一样),基本上只能通过表面现象找问题,对于整体运行流程还是摸索研究。用代码注释来隔离问题也尝试了,问题是现在看来代码每个部分的耦合相当紧密,调用差了一点条件就跑不出那种泄漏时应该运行的状态,也不可能对单个dll或者组件做什么unit test,根本就没有。

整个项目大概将近十来个project,而且还有一些不知道有没有用的(甚至team leader也不知道),基本上问题就是要一把抓,能不能抓到看运气。

今天准备尝试一下CRT check leak加到所有的project里,然后再试一下VLD这个工具。另外把PDH检测working set放在别的地方试试。另外大家介绍的工具我也去瞅瞅。

另外有个问题,能否dump整个进程用到的working set内存?我想通过二进制比较查看前几分钟和现在到底内存有什么样的变化,那么内存增量很可能就是新生成的对象。

2009/8/21 hill wang <hill.w...@gmail.com>
findkernalobject.lua
edsbuild2.lua

Thomas Iverson

unread,
Aug 20, 2009, 9:55:20 AM8/20/09
to pon...@googlegroups.com
2009/8/20 sagasw <sag...@gmail.com>:

OT一下,LZ的名字我看成了Jigsaw...

--
ghosTM55
Shanghai Linux User Group

Keep It Simple Stupid

http://ghosTunix.org 没有备案被机房和谐了 ;-(
9月份再上线 谢谢关注

sagasw

unread,
Aug 20, 2009, 8:53:59 PM8/20/09
to pon...@googlegroups.com
还有一个没尝试,忘记关掉bstr的cache了,一会试试这个。
大家如果有曾经碰到过的memory leak case,欢迎回复一下我好借鉴。

OANOCACHE=1

http://msdn.microsoft.com/en-us/library/ms221105.aspx
For example, if the application allocates a BSTR and frees it, the free block of memory is put into the BSTR cache by Automation. If the application then allocates another BSTR, it can get the free block from the cache. If the second BSTR allocation is not freed, IMallocSpy will attribute the leak to the first allocation of the BSTR. You can determine the correct source of the leak (the second allocation) by disabling the BSTR caching using the debug version of Oleaut32.dll, and by setting the environment variable OANOCACHE=1 before running the application.

2009/8/21 sagasw <sag...@gmail.com>

welfear

unread,
Aug 20, 2009, 8:57:54 PM8/20/09
to pon...@googlegroups.com
2009/8/21 sagasw <sag...@gmail.com>:

> 谢谢前面兄弟们的回复,昨天晚上写的比较仓促,有些背景没有介绍全面,这里补充一下。
>
> 这个问题是来自福特的cst
> issue,着急要搞定。而且应该不是GDIobject和userobject的问题,因为运行时基本上不需要什么复杂的绘画或者键盘鼠标操作,就是一个timer跑一些任务。但是在程序运行的另外一个部分会有handle的泄露,通过sysinternals的handle工具,发现是file的问题,这是另外一个问题了。其它的应该没有,我编了一个小的lua脚本检查这个kernal
> object的问题(msdn上有片文档专门介绍了哪些函数可能导致handle泄露),是从代码中检索所有可能打开handle的函数,在附件里了。另外也不是new
> delete的问题,sysalloc也一样,都是调用很少,而且debug log可以发现,运行时基本不用那些分配函数。
>
> 这是一个比较大的工程,而且以前从没接触(跟新的一样),基本上只能通过表面现象找问题,对于整体运行流程还是摸索研究。用代码注释来隔离问题也尝试了,问题是现在看来代码每个部分的耦合相当紧密,调用差了一点条件就跑不出那种泄漏时应该运行的状态,也不可能对单个dll或者组件做什么unit
> test,根本就没有。
>
> 整个项目大概将近十来个project,而且还有一些不知道有没有用的(甚至team leader也不知道),基本上问题就是要一把抓,能不能抓到看运气。
>
> 今天准备尝试一下CRT check leak加到所有的project里,然后再试一下VLD这个工具。另外把PDH检测working
> set放在别的地方试试。另外大家介绍的工具我也去瞅瞅。
>
> 另外有个问题,能否dump整个进程用到的working
> set内存?我想通过二进制比较查看前几分钟和现在到底内存有什么样的变化,那么内存增量很可能就是新生成的对象。
>


Windbg里的adplus可以生成dump,不知是不是这个意思。

郑海涛

unread,
Aug 20, 2009, 9:07:05 PM8/20/09
to pon...@googlegroups.com
建议 可以使用一下 rational purify来诊断一下,他可以来诊断一下内存泄露,内存使用情况。

2009/8/21 welfear <wel...@gmail.com>

up duan

unread,
Aug 20, 2009, 9:38:12 PM8/20/09
to pon...@googlegroups.com


2009/8/20 sagasw <sag...@gmail.com>

遇到一个问题,有些进入死胡同,跑来求救。

场景如下:

一个windows的mfc程序会一直运行(里面有timer),但是运行时大约每秒有4k的内存增长,非常稳定。问题是如果一直跑的话,程序的堆栈一定会被耗光。

不应该是栈上的空间。如果是,你的程序应该没有办法正常调用函数和返回。


想知道怎么能判断这4k内存是从哪个对象来的?使用过以下判断方式:

使用过·CRT的memory leak检查,但是没有找到问题,而且也不像是动态申请的内存,倒像是某个栈上的对象,现象是只要一拖动窗口或者做个最大最小化,内存都会急剧降低。

根据“稳定时4K、4K的消耗,窗口拖动或者最大最小化是内存剧烈消耗”可以大致看出是Windows的窗口系统在消耗内存。4K、4K的进行其实是因为Memory Page的大小是4K。也就是说,你的程序不是4K4K的申请,而是比4K小,累积一段实现显示为4K消耗。


想用boundchecker,可应用太庞大,根本启动不起来。

试过PDH相关函数,也没有用。



不知道大家对于此类问题有什么办法?

一般没有什么通用的办法。MFC的嵌入式平台下的版本有内存泄露,较低版本VC6[没有SP4以前]以及以前的版本也有这个问题。

up duan

unread,
Aug 20, 2009, 9:39:51 PM8/20/09
to pon...@googlegroups.com


2009/8/21 up duan <fix...@gmail.com>

比如:
    CDC* dc = GetDC();
    ReleaseDC(dc);
这样在MFC的某些版本下就有内存泄露。

许海斌

unread,
Aug 20, 2009, 10:44:46 PM8/20/09
to TopLanguage
记得有一次也是一个疑似内存泄漏的问题,其实是有个容器,每隔一段时间往里塞数据,正确的做法是塞数据前应该clear容器的,但是忘记写这条语句了,
当时把这个问题当成内存泄漏去查了,花了好多时间才找到真正原因

On 8月21日, 上午9时39分, up duan <fixo...@gmail.com> wrote:
> 2009/8/21 up duan <fixo...@gmail.com>


>
>
>
>
>
>
>
> > 2009/8/20 sagasw <sag...@gmail.com>
>
> >> 遇到一个问题,有些进入死胡同,跑来求救。
>
> >> 场景如下:
>

> >> 一个windows的mfc程序会一直运行(里面有timer),但是运行时大约每秒有4k的内存增长,非常稳定。问题是如果一直跑的话,程序的堆栈一定会被耗-光。


>
> > 不应该是栈上的空间。如果是,你的程序应该没有办法正常调用函数和返回。
>
> >> 想知道怎么能判断这4k内存是从哪个对象来的?使用过以下判断方式:
>
> >> 使用过·CRT的memory
> >> leak检查,但是没有找到问题,而且也不像是动态申请的内存,倒像是某个栈上的对象,现象是只要一拖动窗口或者做个最大最小化,内存都会急剧降低。
>

> > 根据"稳定时4K、4K的消耗,窗口拖动或者最大最小化是内存剧烈消耗"可以大致看出是Windows的窗口系统在消耗内存。4K、4K的进行其实是因为Mem-ory


> > Page的大小是4K。也就是说,你的程序不是4K4K的申请,而是比4K小,累积一段实现显示为4K消耗。
>
> >> 想用boundchecker,可应用太庞大,根本启动不起来。
>
> >> 试过PDH相关函数,也没有用。
>
> >> 不知道大家对于此类问题有什么办法?
>
> > 一般没有什么通用的办法。MFC的嵌入式平台下的版本有内存泄露,较低版本VC6[没有SP4以前]以及以前的版本也有这个问题。
>
> 比如:
> CDC* dc = GetDC();
> ReleaseDC(dc);

> 这样在MFC的某些版本下就有内存泄露。- 隐藏被引用文字 -
>
> - 显示引用的文字 -

sagasw

unread,
Aug 20, 2009, 11:04:48 PM8/20/09
to pon...@googlegroups.com
用的是VC2003,PC机(不是CE)。

2009/8/21 up duan <fix...@gmail.com>

up duan

unread,
Aug 20, 2009, 11:12:10 PM8/20/09
to pon...@googlegroups.com


2009/8/21 sagasw <sag...@gmail.com>
用的是VC2003,PC机(不是CE)。


多线程下CString就极易内存泄露。

jinhu wang

unread,
Aug 20, 2009, 11:15:42 PM8/20/09
to pon...@googlegroups.com
为什么多线程下CString就极易内存泄露。

2009/8/21 up duan <fix...@gmail.com>

jinhu wang

unread,
Aug 20, 2009, 11:18:30 PM8/20/09
to pon...@googlegroups.com
1、你的程序里面有没有关闭线程的场景?
2、泄露的内存是不是都是你自己显示的申请释放的?
3、大小固定,泄露规律,应该不是太难找根源,折半吧

2009/8/21 sagasw <sag...@gmail.com>

up duan

unread,
Aug 20, 2009, 11:18:58 PM8/20/09
to pon...@googlegroups.com


2009/8/21 up duan <fix...@gmail.com>



2009/8/21 sagasw <sag...@gmail.com>
用的是VC2003,PC机(不是CE)。


多线程下CString就极易内存泄露。
http://www.vchome.net/tech/vc117.htm这儿描述了一个现象,跟你的现象有点类似,你看看。

up duan

unread,
Aug 20, 2009, 11:23:44 PM8/20/09
to pon...@googlegroups.com


2009/8/21 jinhu wang <wangji...@gmail.com>
为什么多线程下CString就极易内存泄露。

MFC老版本的CString实现采用RefCount + COW,但是没有用Lock,所以多线程竞态条件经常导致起RefCount失效。

up duan

unread,
Aug 20, 2009, 11:24:07 PM8/20/09
to pon...@googlegroups.com


2009/8/21 up duan <fix...@gmail.com>



2009/8/21 jinhu wang <wangji...@gmail.com>
为什么多线程下CString就极易内存泄露。

MFC老版本的CString实现采用RefCount + COW,但是没有用Lock,所以多线程竞态条件经常导致起RefCount失效。
新版本的不知道是不是已经改进了。

Cheng, Long

unread,
Aug 20, 2009, 9:40:05 PM8/20/09
to pon...@googlegroups.com
到底是栈还是堆出问题了?如果是栈出问题的话,是调用死递归之类的。不过看你
的现象,多半还是堆里面的内存泄露。
如果是堆内存泄露的话,可以试试windbg里面的umdh工具

sagasw 写道:

jinhu wang

unread,
Aug 20, 2009, 11:43:21 PM8/20/09
to pon...@googlegroups.com
你意思是多个线程公用一个CString变量还是共用CString类?

2009/8/21 up duan <fix...@gmail.com>

up duan

unread,
Aug 21, 2009, 12:14:41 AM8/21/09
to pon...@googlegroups.com


2009/8/21 jinhu wang <wangji...@gmail.com>
你意思是多个线程公用一个CString变量还是共用CString类?
不知道什么叫“共用CString类”。
0: CString a = "123abc";
1: CString b = a; //now b is not allocate memory, only a's refcount inc
2:b = "345def"; //now b is allocate memory, and a's refcount dec
注意:0、1、2都不是原子操作,都可能被截断。在多线程环境下你可以构造出很多让这个过程出现失误的场景。

jinhu wang

unread,
Aug 21, 2009, 12:24:14 AM8/21/09
to pon...@googlegroups.com
a是临街资源,你当然要自己保护了

Moore Liu

unread,
Aug 21, 2009, 12:49:09 AM8/21/09
to pon...@googlegroups.com
1) 什么timer是以一秒为周期的?
2) 什么object一次刚好分了4k?
 

2009/8/20 sagasw <sag...@gmail.com>
遇到一个问题,有些进入死胡同,跑来求救。

场景如下:

一个windows的mfc程序会一直运行(里面有timer),但是运行时大约每秒有4k的内存增长,非常稳定。问题是如果一直跑的话,程序的堆栈一定会被耗光。

up duan

unread,
Aug 21, 2009, 1:13:17 AM8/21/09
to pon...@googlegroups.com


2009/8/21 jinhu wang <wangji...@gmail.com>
a是临街资源,你当然要自己保护了

如果a是int,是不是我也要保护?
我是说CString在多线程下面很容易出现内存泄露。
可以把CString改造的能够在多线程下轻松使用的。只要不采用在多线程下显得非常愚蠢的COW和RefCount就行。

jinhu wang

unread,
Aug 22, 2009, 5:26:22 AM8/22/09
to pon...@googlegroups.com
是的!即便是int也要保护。这是多线程编程的基础

2009/8/21 up duan <fix...@gmail.com>

up duan

unread,
Aug 23, 2009, 9:17:28 PM8/23/09
to pon...@googlegroups.com


2009/8/22 jinhu wang <wangji...@gmail.com>
是的!即便是int也要保护。这是多线程编程的基础

问题是a不是临界资源。因为不需要多个线程都访问a。
向a赋值是原子操作。所以不会有CString带来的问题。
ps:这个线索我不回复了。

Linker

unread,
Aug 23, 2009, 10:08:00 PM8/23/09
to pon...@googlegroups.com
向a赋值是原子操作?

2009/8/24 up duan <fix...@gmail.com>
向a赋值是原子操作。



--
Regards,
Linker Lin
linker...@gmail.com

jinhu wang

unread,
Aug 23, 2009, 10:10:09 PM8/23/09
to pon...@googlegroups.com
c语言中不知道哪个操作是原子操作。
至少我确认赋值操作不是。
而c++中。。。

2009/8/24 up duan <fix...@gmail.com>

hyifeng

unread,
Aug 24, 2009, 1:05:17 AM8/24/09
to TopLanguage
>c语言中不知道哪个操作是原子操作。
>至少我确认赋值操作不是。
>而c++中。。。

为什么赋值操作不是原子的?

> a是临街资源,你当然要自己保护了

说得是轻松,
如果任何临界资源都保护得好好的,任何要注意的地方都处理得妥妥当当,这个世界就没那么多BUG了。

up duan的意思很明白,就是说容易不小心忽略了CString 的COW特性。

On 8月24日, 上午10时10分, jinhu wang <wangjinhu...@gmail.com> wrote:
> c语言中不知道哪个操作是原子操作。
> 至少我确认赋值操作不是。
> 而c++中。。。
>

> 2009/8/24 up duan <fixo...@gmail.com>
>
>
>
>
>
> > 2009/8/22 jinhu wang <wangjinhu...@gmail.com>

Tiny fool

unread,
Aug 24, 2009, 1:09:06 AM8/24/09
to pon...@googlegroups.com
赋值操作经常不是原子的,这不取决于语言,关键在于cpu是不是可以通过一个指令完成一个赋值操作。你看下 a=b;的汇编结果就知道了。

在多线程的情况下,你不好好考虑临界资源,当然会出问题,而且往往不会是小问题。

2009/8/24 hyifeng <hyi...@gmail.com>

hyifeng

unread,
Aug 24, 2009, 1:14:49 AM8/24/09
to TopLanguage
但,我可没说c++赋值语句一定是原子的。
往内存中写一个立即数就可以一条指令完成。所以"确认赋值操作不是" 这句话就不对。

我想说别人说话可能不是十分严谨,能明白人家的意思就成。
up duan意思是说,int 的值复制通常是线程安全的,而CString 则比较危险。这有什么难理解的吗?

On 8月24日, 下午1时09分, Tiny fool <tinyf...@gmail.com> wrote:
> 赋值操作经常不是原子的,这不取决于语言,关键在于cpu是不是可以通过一个指令完成一个赋值操作。你看下 a=b;的汇编结果就知道了。
> 在多线程的情况下,你不好好考虑临界资源,当然会出问题,而且往往不会是小问题。
>

> 2009/8/24 hyifeng <hyif...@gmail.com>

XiongJia Le

unread,
Aug 24, 2009, 1:18:45 AM8/24/09
to pon...@googlegroups.com
往内存中写一个立即数就可以一条指令完成。所以"
确认赋值操作不是" 这句话就不对。

没有具体测过,不过我觉得,如果是 "往内存中写一个立即数" 不一定是 一条指令...
因为 写的那片内存可能正好 需要换页...
 
2009/8/24 hyifeng <hyi...@gmail.com>

jinhu wang

unread,
Aug 24, 2009, 1:26:50 AM8/24/09
to pon...@googlegroups.com
google一下“对int变量赋值是原子操作吗”吧:)
另外COW是什么?

2009/8/24 hyifeng <hyi...@gmail.com>

jinhu wang

unread,
Aug 24, 2009, 1:32:05 AM8/24/09
to pon...@googlegroups.com
我们可能都理解up duan的意思,只是想纠正他的理解偏差。
CString比较危险这句话说的很不靠谱,很容易误导初学者。
我的意思是,任何一个全局变量,如果存在多线程访问,都是“比较危险”的!

//我想说别人说话可能不是十分严谨,能明白人家的意思就成。
//up duan意思是说,int 的值复制通常是线程安全的,而CString 则比较危险。这有什么难理解的吗?
2009/8/24 hyifeng <hyi...@gmail.com>

xxmplus

unread,
Aug 24, 2009, 1:36:26 AM8/24/09
to pon...@googlegroups.com
http://en.wikipedia.org/wiki/Copy-on-write

2009/8/24 jinhu wang <wangji...@gmail.com>:

--
Any complex technology which doesn’t come with documentation must be the best
available.
Sent from Sydney, Nsw, Australia

hyifeng

unread,
Aug 24, 2009, 1:43:52 AM8/24/09
to TopLanguage
> google一下"对int变量赋值是原子操作吗"吧:)
> 另外COW是什么?

COW 是 Copy on write 缩写。
而 "对int变量赋值是原子操作吗",我对你那句话的纠正如果存在谬误,请指教。

> CString比较危险这句话说的很不靠谱,很容易误导初学者。
> 我的意思是,任何一个全局变量,如果存在多线程访问,都是"比较危险"的!

我认为最危险的事就是,你认为自己做得足够安全却其实不是这样。

On 8月24日, 下午1时32分, jinhu wang <wangjinhu...@gmail.com> wrote:
> 我们可能都理解up duan的意思,只是想纠正他的理解偏差。
> CString比较危险这句话说的很不靠谱,很容易误导初学者。
> 我的意思是,任何一个全局变量,如果存在多线程访问,都是"比较危险"的!
>
> //我想说别人说话可能不是十分严谨,能明白人家的意思就成。
> //up duan意思是说,int 的值复制通常是线程安全的,而CString 则比较危险。这有什么难理解的吗?

> 2009/8/24 hyifeng <hyif...@gmail.com>

jinhu wang

unread,
Aug 24, 2009, 2:07:54 AM8/24/09
to pon...@googlegroups.com
前面tiny和xiaojia le已经给了你足够的提示了吧!
我确信:对int变量赋值不是原子操作。如Tiny所言:关键在于cpu是不是可以通过一个指令完成一个赋值操作。
如果多线程环境下有一个int型全局变量i。
  • 按照我的理解,我肯定会用操作系统提供的同步原语对他进行保护。而且会尽量用volatile修饰这个变量。

 

你的第二句陈述是完全正确的。

 

2009/8/24 hyifeng <hyi...@gmail.com>

hyifeng

unread,
Aug 24, 2009, 2:58:31 AM8/24/09
to TopLanguage
偏离了主题了。
相比斗口舌,我对楼主如这个问题更感兴趣。

jinhu wang

unread,
Aug 24, 2009, 3:22:25 AM8/24/09
to pon...@googlegroups.com
不过你和up duan的问题还是很有意思的,因为我一下找不出反例来证明赋值语句不是原子操作。
在下面这个blog里有vc示例证明++操作不是原子的
刚才我突然又想起loki库里有对原子操作的封装,loki对赋值原子操作的封装。
以下是loki在win环境下对赋值原子操作的封装:
static void AtomicAssign(IntType& lval, volatile IntType& val)
        { InterlockedExchange(&lval, val); }
2009/8/24 hyifeng <hyi...@gmail.com>
偏离了主题了。
相比斗口舌,我对楼主如这个问题更感兴趣。

hyifeng

unread,
Aug 24, 2009, 4:00:14 AM8/24/09
to TopLanguage
你没明白我的意思,
C++中
int a = 1;
32位机下相当于指令
MOV a, 1

在实模式下是原子的,在保护模式下仍然是具有原子性的。比如,
MOV a,EAX
缺页异常是不可能EAX低16和高16位拷贝中间发生的。

所以即使系统有分页机制不会影响它的原子性的。

On 8月24日, 下午3时22分, jinhu wang <wangjinhu...@gmail.com> wrote:
> 不过你和up duan的问题还是很有意思的,因为我一下找不出反例来证明赋值语句不是原子操作。
> 在下面这个blog里有vc示例证明++操作不是原子的http://blog.joycode.com/peon/archive/2007/04/03/100308.joy


> 刚才我突然又想起loki库里有对原子操作的封装,loki对赋值原子操作的封装。
> 以下是loki在win环境下对赋值原子操作的封装:
> static void AtomicAssign(IntType& lval, volatile IntType& val)
> { InterlockedExchange(&lval, val); }

> 2009/8/24 hyifeng <hyif...@gmail.com>
>
>
>
> > 偏离了主题了。
> > 相比斗口舌,我对楼主如这个问题更感兴趣。

jinhu wang

unread,
Aug 24, 2009, 4:07:43 AM8/24/09
to pon...@googlegroups.com
我不熟悉汇编,我觉得下面说的更有说服力一些,
//以下描述非原创
如果把"赋值"操作限定在指令集上,则所有处理器的指令集操作都是原子的,该原子性由硬件提供。

否则,语言层面的"赋值"操作很难说是否是原子的。

比如说一个例子,a, b都放在存储区里:
int a = 10;

int b = 1;

int

main()

{

    b = a;       //赋值

    return 0;

}


那么在X86上的汇编(没有优化)是[gcc 3.3.3]

movl a, %eax
movl %eax, b

显然,虽然两条movl是原子的,但是b = a;这样的赋值操作不是。

而同样的赋值语句b = a;在load-store型的arm上,结果是[gcc3.3.6 arm-cross]

ldr r3, .L2                  #取a的地址,放在r3
ldr r2, [r3, #0]          #[]操作取r3地址里的值,放在r2
ldr r3, .L2+4             #取b的地址,放在r3
str r2, [r3, #0]          #赋值
...
L2:
    .word a
    .word b

这就更明显。由于a,b都是存储区变量,赋值b = a;被翻译成4条指令集语句。


2009/8/24 hyifeng <hyi...@gmail.com>

hyifeng

unread,
Aug 24, 2009, 4:26:01 AM8/24/09
to TopLanguage
我初衷不是要说服你c++赋值语句是原子的。
所以我赞同你。

我可惜的是偏离主题太远了,因为别人口述上的一点瑕疵就开始跑题,其实他要传递的内容大家都心里都明白。

jinhu wang

unread,
Aug 24, 2009, 4:39:58 AM8/24/09
to pon...@googlegroups.com
我的迷惑是从“多线程下CString就极易内存泄露”这句开始的。因为我想弄明白为什么CString容易内存泄漏。
因为我的某个多线程环境下程序里有不少CString变量。。。

2009/8/24 hyifeng <hyi...@gmail.com>

Shuo Chen

unread,
Aug 24, 2009, 9:24:40 AM8/24/09
to TopLanguage
有没有考虑多CPU时的cache一致性?

比如CPU1往地址a写了一个数,CPU2读的时候还是读到原来的值。
这时,需要用memory barrier来强制刷新。比如Intel x86的 lfence/sfence/mfence 指令。

另外,如果地址a不是充分对齐的(比如*a是32-bit,但是地址a是奇数),即便是一条写入指令,那也有可能分成两个机器周期来完成。这时,从另一
个CPU看,数据是不完整的。

Shuo Chen

unread,
Aug 24, 2009, 9:31:44 AM8/24/09
to TopLanguage
volatile在C++里不是用来解决多线程问题的,除非C++标准明确它有acquire/release语义(那帮大爷们还在吵语法和库,管不上
我们这些生活在水深火热中的程序员的真实需求)。
目前只有VC 2005/2008保证了这个语义,而GCC一言未发。

volatile的三种用途:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n2016.html

Java 5的volatile倒是很强大,Java不愧是支持concurrent programming最强的主流语言。

On Aug 24, 2:07 pm, jinhu wang <wangjinhu...@gmail.com> wrote:
> 前面tiny和xiaojia le已经给了你足够的提示了吧!
> 我确信:对int变量赋值不是原子操作。如Tiny所言:关键在于cpu是不是可以通过一个指令完成一个赋值操作。
> 如果多线程环境下有一个int型全局变量i。
>

> - 按照我的理解,我肯定会用操作系统提供的同步原语对他进行保护。而且会尽量用volatile修饰这个变量。
>
> 你的第二句陈述是完全正确的。
>
> 2009/8/24 hyifeng <hyif...@gmail.com>

hyifeng

unread,
Aug 24, 2009, 8:50:11 PM8/24/09
to TopLanguage
> 另外,如果地址a不是充分对齐的(比如*a是32-bit,但是地址a是奇数),即便是一条写入指令,那也有可能分成两个机器周期来完成。这时,从另一
> 个CPU看,数据是不完整的。

如果我没搞错,应该不会发生这种情况。
当cache刷到内存的时候是锁总线的,另一个cpu没办法读到半成品。

hyifeng

unread,
Aug 24, 2009, 9:44:38 PM8/24/09
to TopLanguage
看了一点零散的资料,还没有弄清楚cache和内存同步的行为是怎样的,
各个CPU有独立的 cache会怎样,共享cache又会怎样。

Shuo Chen说的多CPU的确是个麻烦的问题,有那位懂这些的能介绍一下。

techabc

unread,
Aug 25, 2009, 4:41:21 AM8/25/09
to pon...@googlegroups.com
内存啊内存~
跑题一下,对于内存碎片,有否第三方工具可以解决呢?就像磁盘碎片那种的。
场景:遗留系统,改造成本较高,需要长期运行。有否工具实现内存碎片率的监控并对其整理?谁曾经遭遇过这方面问题?

Linker

unread,
Aug 25, 2009, 5:51:07 AM8/25/09
to pon...@googlegroups.com
Cache的行为是 不确定的,随机的。

2009/8/25 hyifeng <hyi...@gmail.com>

看了一点零散的资料,还没有弄清楚cache和内存同步的行为是怎样的,
各个CPU有独立的 cache会怎样,共享cache又会怎样。

Shuo Chen说的多CPU的确是个麻烦的问题,有那位懂这些的能介绍一下。

hyifeng

unread,
Aug 25, 2009, 6:26:48 AM8/25/09
to TopLanguage
Thanks to Shuo Chen!
你说的问题的确是存在的,内存位址对齐才能确保存储访问是原子性的。

CPU cache 以 cache line 为单位,一般是 32 或 64 bytes。
一条读存储的机器指令如果内存地址不对齐,则需要 2个总线周期或以上。
在cache中情况一样,而且读取的内存块可能在cache line 边界上(跨cache line)。

当cache line 被修改就会被标记为dirty,其他core对应的cache line 被标记为invalid。
这时若其他core要访问这个数据就会导致cache同步。
如果一个32bit数据刚好被分在两个cache line边界上,就可能会发生CPU-A只更新了其中一个cache-line 就被CPU-B拿去
用,导致一致性的问题。

若仅仅是内存位置不对齐,可能也是类似原因导致一致性问题。

SMP多线程下很多无法在一个总线周期内完成的指令都有可能出问题,例如ADD、XCHG,如果要保证它的原子性要加上LOCK前缀。

最简单的方式就是给共享资源都加上锁,一了百了。

Shuo Chen

unread,
Aug 25, 2009, 7:33:47 AM8/25/09
to TopLanguage
恭喜你找到答案了:

Intel的手册说:

8.1.1 Guaranteed Atomic Operations
The Intel486 processor (and newer processors since) guarantees that
the following basic memory operations will always be carried out
atomically:
*Reading or writing a byte
*Reading or writing a word aligned on a 16-bit boundary
*Reading or writing a doubleword aligned on a 32-bit boundary
The Pentium processor (and newer processors since) guarantees that the
following additional memory operations will always be carried out
atomically:
*Reading or writing a quadword aligned on a 64-bit boundary
*16-bit accesses to uncached memory locations that fit within a 32-bit
data bus
The P6 family processors (and newer processors since) guarantee that
the following additional memory operation will always be carried out
atomically:
*Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit
within a cache line
Accesses to cacheable memory that are split across bus widths, cache
lines, and page boundaries are not guaranteed to be atomic by the
Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Pentium 4,
Intel Xeon, P6 family, Pentium, and Intel486 processors. The Intel
Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Pentium 4, Intel
Xeon, and P6 family processors provide bus control signals that permit
external memory subsystems to make split accesses atomic; however,
nonaligned data accesses will seriously impact the performance of the
processor and should be avoided.

Shuo Chen

unread,
Aug 25, 2009, 8:59:08 AM8/25/09
to TopLanguage
Java ? C++ ?
要是C++,恭喜你了,没辙。
整理内存碎片,要搬移某个地址的数据到另一个地址,那就要知道所有指向这块地址的指针,这在C/C++里是无解的。

多长期?一周重启一次行不行?真要是长期运行,程序一开始就不能这么写。

techabc

unread,
Aug 25, 2009, 9:52:24 PM8/25/09
to pon...@googlegroups.com
长期:一周肯定是不行的,最少也得三五个月
确实是C++,MFC,开始时候未作防碎片处理
这么说是真的无解了?

2009/8/25 Shuo Chen <gian...@gmail.com>

Cheng, Long

unread,
Aug 25, 2009, 9:56:00 PM8/25/09
to pon...@googlegroups.com
换一个好点的内存分配器,网上搜搜有很多的。

techabc 写道:


> 长期:一周肯定是不行的,最少也得三五个月
> 确实是C++,MFC,开始时候未作防碎片处理
> 这么说是真的无解了?
>

> 2009/8/25 Shuo Chen <gian...@gmail.com <mailto:gian...@gmail.com>>


>
> Java ? C++ ?
> 要是C++,恭喜你了,没辙。
> 整理内存碎片,要搬移某个地址的数据到另一个地址,那就要知道所有指向
> 这块地址的指针,这在C/C++里是无解的。
>
> 多长期?一周重启一次行不行?真要是长期运行,程序一开始就不能这么写。
>
> On Aug 25, 4:41 pm, techabc <tech...@gmail.com

sagasw

unread,
Aug 26, 2009, 2:03:50 AM8/26/09
to pon...@googlegroups.com
这么长期运行的项目,也许应该搭配独立的内存分配模块,自己控制这些分配逻辑。


 
2009/8/26 techabc <tec...@gmail.com>

techabc

unread,
Aug 26, 2009, 2:18:45 AM8/26/09
to pon...@googlegroups.com
系统搭建的时候没这根弦,所以……
btw,Windows、*nix下的那些C、C++写的服务程序,都需要单独考虑内存分配机制吗?

2009/8/26 sagasw <sag...@gmail.com>

sagasw

unread,
Aug 26, 2009, 2:49:31 AM8/26/09
to pon...@googlegroups.com
apache是有的,其他的不了解

2009/8/26 techabc <tec...@gmail.com>

Shuo Chen

unread,
Aug 26, 2009, 10:40:10 AM8/26/09
to TopLanguage
我们不考虑,程序每周重启,系统也每周重启。

On Aug 26, 2:18 pm, techabc <tech...@gmail.com> wrote:
> 系统搭建的时候没这根弦,所以......btw,Windows、*nix下的那些C、C++写的服务程序,都需要单独考虑内存分配机制吗?


>
> 2009/8/26 sagasw <sag...@gmail.com>
>
> > 这么长期运行的项目,也许应该搭配独立的内存分配模块,自己控制这些分配逻辑。
>

> > 2009/8/26 techabc <tech...@gmail.com>


>
> > 长期:一周肯定是不行的,最少也得三五个月 确实是C++,MFC,开始时候未作防碎片处理
> >> 这么说是真的无解了?
>

> >> 2009/8/25 Shuo Chen <giantc...@gmail.com>

Shuo Chen

unread,
Aug 26, 2009, 10:40:49 AM8/26/09
to TopLanguage
可以用一个好一点的内存分配器,估计能撑得久一点。

On Aug 26, 9:52 am, techabc <tech...@gmail.com> wrote:
> 长期:一周肯定是不行的,最少也得三五个月确实是C++,MFC,开始时候未作防碎片处理
> 这么说是真的无解了?
>
> 2009/8/25 Shuo Chen <giantc...@gmail.com>

sagasw

unread,
Aug 26, 2009, 9:20:57 PM8/26/09
to pon...@googlegroups.com
http://sunxiunan.com/?p=1289

Debug step by step(1) Memeory leak问题调试常用手段

1, check handle leak.

Use Lua script to search the codes.

2, check memory leak.

If you use CRT, you could use:

_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
//_CrtSetBreakAlloc(182366);

// http://msdn.microsoft.com/en-US/library/e5ewb1h3(VS.80).aspx

3, get performance data.

We could use PDH functions. CPDHData

4, 注释代码隔离问题。

5,

1. 加一个对象计数器, 哪种对象一直增加, 就是它了.
2. 申请比较多的类, 轮流内部增加一个 char buffer[65536], 看看内存增加是否加快, 很快就可以找出了.

6, VLD boundchecker ADPlus WinDbg SysInternals-Tools (handle)

7, OANOCACHE=1
http://msdn.microsoft.com/en-us/library/ms221105.aspx
For example, if the application allocates a BSTR and frees it, the free block of memory is put into the BSTR cache by Automation. If the application then allocates another BSTR, it can get the free block from the cache. If the second BSTR allocation is not freed, IMallocSpy will attribute the leak to the first allocation of the BSTR. You can determine the correct source of the leak (the second allocation) by disabling the BSTR caching using the debug version of Oleaut32.dll, and by setting the environment variable OANOCACHE=1 before running the application.

8, some tools Mozilla uses:

http://www.mozilla.org/performance/tools.html

9, 启用或禁用内存诊断可以调用全局函数 AfxEnableMemoryTracking()

#ifdef _DEBUG
CMemoryState oldMemState, newMemState, diffMemState;
oldMemState.Checkpoint();
#endif

(被测试的代码)

#ifdef _DEBUG
newMemState.Checkpoint();
if(diffMemState.Difference(oldMemState, newMemState)) {
TRACE(“Memory Leaked Here:\n\n” );
}
#endif

抄袭了toplanguage讨论中的一些想法。

http://groups.google.com/group/pongba/browse_thread/thread/6c871ba9a79be74a#




2009/8/26 Shuo Chen <gian...@gmail.com>

jinhu wang

unread,
Aug 26, 2009, 10:15:21 PM8/26/09
to pon...@googlegroups.com
这个问题你定位出来了吗?

2009/8/27 sagasw <sag...@gmail.com>

Lai Jiangshan

unread,
Aug 27, 2009, 9:28:02 PM8/27/09
to pon...@googlegroups.com
"atomic" 这个定义是由问题来定义的(是由critical region的大小来定义的, 有时atomic是指线程没被切换....)
对对齐的32位, cpu只保证读写这32位是atomic
如果涉及到多个变量, 就不能认为是atomic(注意atomic的定义已经变了)

注意即使这种情况也不是atomic的
//global volatile int something_done;
do_something();
something_done = 1; // it's not atomic actually. it may be set earlier
than you think.

因为这里涉及smp, cache, 流水线的优化.
可以看这里的9页纸http://lwn.net/Articles/250967/
<What every programmer should know about memory>

没有一个32位的内存的变量是独立的吧(如是,就可以把这个变量删除),
如果你使用了 some_int = value这种最低层的atomic, 你必须在代码的很多地方使用很多内存屏障语句来保证运行的正确性.
linux 内核提供完整的atomic api和 内存屏障 api 做这些事情.
用户空间的库不知道有没有.

所以最好是加锁. 锁阻止了一切对多cpu多线程访问的有害优化, 也考虑了各种的污垢.

z.s. jiang

unread,
Aug 28, 2009, 11:10:17 PM8/28/09
to pon...@googlegroups.com
我习惯用od直接拖进去,屠杀。。。。
上次私服的服务端经常崩溃,用od砍几下就好了。。。

unread,
Sep 29, 2009, 5:22:01 AM9/29/09
to TopLanguage
关于4字节泄漏,我也曾遇到过
那次的情况是因为调用约定不一致导致的栈内数据未出栈
一般在调用第3方库的时候比较容易碰到这个问题。

gaxxx

unread,
Sep 29, 2009, 10:02:05 PM9/29/09
to pon...@googlegroups.com

Google vld.h,程序退出会在output窗口打印memory leak

在 2009 年 8 20 日,9:54 下午,"sagasw" <sag...@gmail.com>编写:

遇到一个问题,有些进入死胡同,跑来求救。

场景如下:

一个windows的mfc程序会一直运行(里面有timer),但是运行时大约每秒有4k的内存增长,非常稳定。问题是如果一直跑的话,程序的堆栈一定会被耗光。

想知道怎么能判断这4k内存是从哪个对象来的?使用过以下判断方式:

使用过·CRT的memory leak检查,但是没有找到问题,而且也不像是动态申请的内存,倒像是某个栈上的对象,现象是只要一拖动窗口或者做个最大最小化,内存都会急剧降低。

想用boundchecker,可应用太庞大,根本启动不起来。

试过PDH相关函数,也没有用。



不知道大家对于此类问题有什么办法?

Eric.Wang

unread,
Sep 30, 2009, 11:51:58 PM9/30/09
to TopLanguage

你的确信是错的。

伪代码如下:

global int a = 0;

thread A
{
while (true)
{
a = MAX_INT;
}
}

thread B
{
while(true)
{
a = 0;
}
}

thread C
{
int x = a;
if (x != 0 && x != MAX_INT)
{
assert(false);
}
}

你可以写个小程序运行上面的伪代码。如果给int赋值不是原子操作,则a必定会出现0和1混合的例如0x00FF之类的赋值,则必定assert
(false)。

wangji...@gmail.com

unread,
Oct 9, 2009, 2:48:01 AM10/9/09
to TopLanguage
无聊了吧!
if (x != 0 && x != MAX_INT)本身就不是原子操作,你却用它去检验。。。
不是我确不确信,而是事实就是那个样子。
或许你的这段代码在某个平台上不会出问题,从而掩盖了你的诡异的bug:-)
关于赋值的原子性tiny等大侠都已经说明过了:"关键在于cpu是不是可以通过一个指令完成一个赋值操作。"
即便不是一条指令,在32bit的cpu下,也不会出现你说的:"a必定会出现0和1混合的例如0x00FF之类的赋值"的情景。白话文一点:而是出
现,一个线程刚把变量mov到寄存器准备赋0这时候操作被打断,切换到另一个线程mov这个变量到寄存器赋值全f。这时候切换回第一个线程又会把它重新
mov及赋0。

> > > > > > > > > ps:这个线索我不回复了。- 隐藏被引用文字 -
>
> - 显示引用的文字 -

Reply all
Reply to author
Forward
0 new messages