首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • fwrite()库函数为什么会吃内存? [已结帖,结帖人:ceasar1134]
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    • 结帖率:
    发表于:2008-08-19 09:29:42 楼主
    以前没有注意这个问题,偶然发现自己的内存呼呼地往下降,最后定位在fwrite()函数上。

    现象是这样的:
    比如写一个300K的文件,内存就减少300K,直到减至一个稳定的值。(8M左右,我这里用的是128M的内存)
    fwrite()调用成功返回后也不释放内存,除非我的程序停掉。

    这是为什么呀,大家在写程序的时候,特别在写大文件的时候是像我这样
    fwrite(buffer,1,size,pFILE);这样使用还是有其他技巧?
    20  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • guosha
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    • 3

    发表于:2008-08-19 09:41:181楼 得分:0
    不会吧,你的buffer自己有没有去释放啊?
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • chenfeng2002
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 09:53:402楼 得分:0
    没注意过这个问题,不过想象也是正常的,
    相对来说写硬盘要慢,所以系统应该是等到写的总数达到一定数量时再写硬盘,比每fwrite()一次写一次硬盘然后释放内存效率高吧?
    个人分析的,不知道对否,有待dx解答!~
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 10:17:013楼 得分:0
    应该不是我没有释放内存,因为我写的下面的测试程序:
    C/C++ code
    /************************************************ * test.cpp ************************************************/ #include "./FileClass.h" #include "../type.h" #include <unistd.h> #include <sys/sysinfo.h> #include <sys/types.h> #define FWRITE_BUFFER_LENGTH 400*1024 int main(void) { CFile cInFile; CFile cOutFile; char* bfTemp = new char[400000]; char* bfFwrite = new char[FWRITE_BUFFER_LENGTH]; unsigned int uiFileLength=0; unsigned int uiReadFileLength=0; unsigned int uiOutFileIndex=1; char szOutFileName[MAX_FILENAME_LENGTH]; struct sysinfo MySysinfo; int iRet; cInFile.SetFullFileName("./In.jpg"); cInFile.Open(BINARY_RWTYPE,POS_START); uiFileLength = cInFile.GetFileLength(); uiFileLength = 314890; iRet = cInFile.Read(bfTemp, uiFileLength, uiReadFileLength); if(iRet==0 && uiFileLength==uiReadFileLength) { printf("== Read input file successfully\n"); } else { printf("== Read input file failed\n== Read() return %d; File length is %d; Read number is %d\n",iRet,uiFileLength,uiReadFileLength); return -1; } while(1) { snprintf(szOutFileName,MAX_FILENAME_LENGTH,"/EpData/OutFile%d.jpg",uiOutFileIndex++); cOutFile.SetFullFileName(szOutFileName); iRet = cOutFile.Open(BINARY_RWTYPE,POS_START); //cOutFile.SetFwriteNoBuffer( ); if(iRet < 0) { printf("== cOutFile.Open() failed\n"); break; } iRet = cOutFile.Write(bfTemp,uiFileLength); if(iRet < 0) { cOutFile.Close(); break; } cOutFile.Close(); sysinfo(&MySysinfo); printf("== Free memory is %ldK\n",MySysinfo.freeram/1024); sleep(1); } delete[] bfTemp; delete[] bfFwrite; cInFile.Close(); return 0; }

    cOutFile.Write()里面只做了一件事,就是fwrite(),没有申请任何内存,这样打印出来的内容还是内存不断减少
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wlabing
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 12:17:474楼 得分:0
    标准I/O有三种缓存形式:全局缓存,行缓存,无缓存.
    fwrite函数应该属于全局缓存的,你可以将缓存设置小一些,缓存满了就会写到硬盘中了.
    你也可以在fwrite之后,调用fflush函数,将缓存中的内容写到硬盘中.
    相关内容请查看 < <unix高级编程>>第5章的"标准I/O库""

    #include <stdio.h>

    void setbuf(FILE *restrict fp, char *restrict buf);

    int setvbuf(FILE *restrict fp, char *restrict buf, int mode,size_t size);

    int fflush(FILE *fp);

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 12:24:065楼 得分:0
    楼上
    我在上面有一句屏蔽掉的:
    //cOutFile.SetFwriteNoBuffer( );
    在文件类中我是使用setvbuf(this->pfFile,(char*)NULL,_IONBF,0);来实现不实用缓存的。
    但是测试下来对内存减少的情况还存在,几乎没有什么改变...

    fflush()没有测试过,马上测试一下。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 12:28:386楼 得分:0
    第一种是块模式吧(block buffered)。
    相关内容请见setbuffer的man手册(^_^,开个玩笑)
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 12:43:417楼 得分:0
    fflush()我也试过了,还是不可以
    这是结果图片
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • unilgr
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 15:00:498楼 得分:0
    我的理解:
      1            2              3            4          5
    程序缓存 => 标准IO buffer => Page cache => disk cache => disk
    从数据流向看:
    1步 你的程序循环写文件时也没有再次申请内存,如果CFile没有内存泄漏,可以排除
    2步 libc IO有自己的缓存,默认的都比较小(常见4k,8k等),而且fclose会释放,libc的内存管理器对我们是透明的,可能消耗比我们想象多一些的内存,但是可能不大
    3步 每秒循环向文件写一次400k,这个速率也很小,不太清楚Linux如何管理页面文件,有可能消耗些内存
    4 5步已经脱离了内存

    最好用命令行工具监视下进程的其他参数,因为你打印Free memory是系统级的,不好确定究竟是你的进程使用还是系统在进行缓存


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 15:12:439楼 得分:0
    怎么么人理我,要不然我把程序贴上去大家回去编译一下?
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • guosha
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    • 3

    发表于:2008-08-19 15:20:2810楼 得分:0
    把sysinfo.bufferam也打出来看看,看看这个成员是不是一直在增涨。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 16:47:2711楼 得分:0
    to:unilgr
    使用top命令查看的结果是一样的...
    CFile类文件的基本操作,我保证没有泄漏...
    在下面论坛里有个类似帖子(http://www.unix.com/high-level-programming/20235-help-fwrite-consuming-lot-memory.html):
    --------------------------
    ajphaj 

    Help -fwrite consuming lot of memory !!!
    Hi ,

    I am running a C/C++ program on a solaris 5.8 machine. This parituclar application has a module which saves data to a file. The module uses fwrite() function to save data.

    The fwrite function write about 500 MB of data to a file. The problem which I am facing is, the memory consumtion of the process increases during the fwrite but does not decrease once the fwrite is over.

    I tried fflush(filepointer) after the fwrite but did not help.

    The fwrite function writes all the data in once single fwrite() statement. I initially thought the increase in memory was because of this. So I tried writing data in smaller chunks of sizes 100 MB and 50 MB but still the memory utilization of the process does not decreases once the fwrite is over.
    As a result a lot of the main memory is being eaten by this process and thus making the system very slow.

    Please help me .

    Thanks in advance

    ajphaj
    --------------------------------------------
    Perderabo 

    That's right processes will not shrink when you're using the malloc library even indirectly. Maybe you can fork(). Let the child write the data and then exit.

    ------
    jim mcnamara 

    You can try using the open() system call with O_SYNC flag, then call write() every 1MB of data.

    O_SYNC turns off file buffering, in that every call to write waits until the underlying hardware has completed writing the data.

    For fwrite(), setvbuf() and setbuf() do somewhat the same thing.

    A word of warning: turning off buffering completely is a very bad idea in terms of performance. Especially on writing really big files.
    ------------------------------------------
    下面的这两种方法我都试过,使用子进程进行写解决不了问题。


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 16:57:0512楼 得分:0
    to:gaosha
    按你的方法打印如下
    是在增加,不过幅度没有Free memory减少的幅度大
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • NC
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 17:31:1013楼 得分:0
    用write吧
    fwrite在并发量大的情况下会有问题。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wlabing
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 17:59:2314楼 得分:5
    今天自己编码试了一下,发现只有在open函数打开文件时,设置了O_SYNC标志,内存才不明显的被消耗.
    即使在打开文件后再调用fcntl设置O_SYNC标志也是没有,内存同样的明显的消耗.个人觉得设置O_SYNC标志应该是可以完成同步I/O的,可以是我自已做的时候有某些地方没做到位的原因.
    DESCRIPTION
          The function fflush() forces a write of all user-space buffered data for the given output or update stream via the
          stream's underlying write function.  The open status of the stream is unaffected.
    fflush只是将用户空间的缓冲区数据写到流里,这并不表示写到磁盘中,可能是弄到kernel block buffered中了.
    C/C++ code
    /*----------------nobuf2.c------------------*/ #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<malloc.h> #include<sys/sysinfo.h> #include<fcntl.h> #include<sys/stat.h> #include<sys/types.h> #define MAX_LEN 1024*1024 int main(int argc,char *argv[]) { FILE *fp; int fd; int loop=20; char buf[MAX_LEN]; struct sysinfo info; int flag; memset(buf,'a',MAX_LEN); if(argc!=2) { printf("Usage:%s filename\n",argv[0]); exit(0); } if((fp=fopen(argv[1],"a+"))==NULL) { fprintf(stderr,"can not open file!\n"); exit(1); } if((fd=fileno(fp))<0) { perror("fileno"); exit(3); } if((flag=fcntl(fd,F_GETFL,0))<0) { perror("fcntl"); exit(4); } flag=flag | O_SYNC; if(fcntl(fd,F_SETFL,flag)<0) { perror("fcntrl"); exit(5); } while(loop>0) { if(fwrite(buf,sizeof(char),MAX_LEN,fp)<0) { perror("fwrite"); break; } if(fsync(fd)<0) { perror("fsync"); break; } sysinfo(&info); printf("free memory:%ld KB\n",info.freeram/1024); loop--; sleep(1); } fclose(fp); sleep(3); sysinfo(&info); printf("after closing. free memory:%ld KB\n",info.freeram/1024); }

    pds@FSSR:~> ./nobuf2 buf.dat 
    free memory:53596 KB
    free memory:52480 KB
    free memory:51488 KB
    free memory:50496 KB
    free memory:49504 KB
    free memory:48512 KB
    free memory:47520 KB
    free memory:46404 KB
    free memory:45412 KB
    free memory:44420 KB
    free memory:43428 KB
    free memory:42312 KB
    free memory:41320 KB
    free memory:40204 KB
    free memory:39212 KB
    free memory:38220 KB
    free memory:37104 KB
    free memory:36112 KB
    free memory:35120 KB
    free memory:34004 KB
    after closing. free memory:34004 KB
    pds@FSSR:~> ./nobuf2 buf.dat
    free memory:32896 KB
    free memory:31904 KB
    free memory:30912 KB
    free memory:29796 KB
    free memory:28804 KB
    free memory:27812 KB
    free memory:26820 KB
    free memory:25704 KB
    free memory:24712 KB
    free memory:23720 KB
    free memory:22604 KB
    free memory:21612 KB
    free memory:20620 KB
    free memory:19628 KB
    free memory:18512 KB
    free memory:17520 KB
    free memory:16528 KB
    free memory:15536 KB
    free memory:14420 KB
    free memory:13428 KB
    after closing. free memory:13428 KB
    pds@FSSR:~> ./nobuf2 buf.dat
    free memory:12436 KB
    free memory:11444 KB
    free memory:10328 KB
    free memory:9284 KB
    free memory:8212 KB
    free memory:7176 KB
    free memory:6140 KB
    free memory:6340 KB
    free memory:6180 KB
    free memory:5972 KB
    free memory:5628 KB
    free memory:5796 KB
    free memory:5812 KB
    free memory:5976 KB
    free memory:6000 KB
    free memory:6012 KB
    free memory:6004 KB
    free memory:6404 KB
    free memory:6180 KB
    free memory:6096 KB
    after closing. free memory:6104 KB
    pds@FSSR:~> ./nobuf2 buf.dat
    free memory:6196 KB
    free memory:5848 KB
    free memory:6008 KB
    free memory:5904 KB
    free memory:5820 KB
    free memory:5728 KB
    free memory:6352 KB
    free memory:6380 KB
    free memory:6292 KB
    free memory:6132 KB
    free memory:6300 KB
    free memory:6064 KB
    free memory:5980 KB
    free memory:5880 KB
    free memory:6036 KB
    free memory:5828 KB
    free memory:6580 KB
    free memory:6304 KB
    free memory:6096 KB
    free memory:7572 KB
    after closing. free memory:7572 KB
    上面连续执行了几次,只有当free memory达到6000KB左右,内存才基本保持不变.

    C/C++ code
    /*------------------nobuf.c------------------*/ #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<malloc.h> #include<sys/sysinfo.h> #include<fcntl.h> #include<sys/stat.h> #include<sys/types.h> #define MAX_LEN 1024*1024 int main(int argc,char *argv[]) { FILE *fp; int fd; int loop=20; char buf[MAX_LEN]; struct sysinfo info; int flag; memset(buf,'a',MAX_LEN); if(argc!=2) { printf("Usage:%s filename\n",argv[0]); exit(0); } if((fd=open(argv[1],O_WRONLY | O_CREAT | O_SYNC,0666))<0) { fprintf(stderr,"can not open file!\n"); exit(1); } /*if((flag=fcntl(fd,F_GETFL,0))<0) { perror("fcntl"); exit(3); } flag=flag | O_SYNC; if(fcntl(fd,F_SETFL,flag)<0) { perror("fcntl"); exit(2); }*/ while(loop>0) { if(write(fd,buf,MAX_LEN)<0) { perror("write"); break; } if(fsync(fd)<0) { perror("fsync"); break; } sysinfo(&info); printf("free memory:%ld KB\n",info.freeram/1024); loop--; sleep(1); } close(fd); sleep(3); sysinfo(&info); printf("after closing. free memory:%ld KB\n",info.freeram/1024); }


    pds@FSSR:~> ./nobuf buf.dat
    free memory:60556 KB
    free memory:60556 KB
    free memory:60556 KB
    free memory:60432 KB
    free memory:60424 KB
    free memory:60424 KB
    free memory:60424 KB
    free memory:60424 KB
    free memory:60424 KB
    free memory:60432 KB
    free memory:60432 KB
    free memory:60432 KB
    free memory:60432 KB
    free memory:59936 KB
    free memory:59936 KB
    free memory:59936 KB
    free memory:59936 KB
    free memory:59936 KB
    free memory:59936 KB
    free memory:59936 KB
    after closing. free memory:59936 KB

    打开时设置了O_SYNC,执行后,内存才基本上保持不变.

    上面的实验结果不知道对你有没有用,我是在虚拟机上做的,也不知道跟实际情况运行一不一样.
    我也有很多疑惑的地方,你弄明白后不要忘了告诉我一声 ^_^
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • unilgr
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-19 18:37:4715楼 得分:10
    这应该是一个操作系统调度问题,而不是一个编程问题,我在Linux内核2.6.9(内存256MB)下做了测试:
    按照LZ的那种写文件方式,确实有这个现象,不过top监控是进程本身的虚拟内存和物理内存消耗很少,基本很稳定,但是vmstat跟踪系统状态结果如下
    [root@localhost ~]# vmstat 60
    procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
    r  b  swpd  free  buff  cache  si  so    bi    bo  in    cs us sy id wa
    2  0    224 116320  4780  35520    0    0    59  125  537  216  3  5 91  1
    0  0    224  94288  5096  57304    0    0    0  371 1072  486  5  6 88  1
    0  0    224  70192  5432  81148    0    0    0  418 1057  351  2  3 95  0
    0  0    224  46312  5764 104736    0    0    0  412 1058  326  1  3 96  0
    0  0    224  21892  6284 128656    0    0    8  430 1071  395  2  3 94  1
    0  0    224  18156  5264 133056    0    0    47  414 1087  531  3  4 91  1
    0  0    224  17312  5284 133816    0    0    1  421 1132  574  3  4 92  0
    0  0    224  18620  5252 132288    0    0    0  417 1058  372  2  3 95  0
    可以看到memory下free和cache的变化趋势(间隔60秒),也就是系统空闲内存都用来做cache了,以提高写入磁盘的性能
    从http://www.faqs.org/docs/linux_admin/buffer-cache.html摘录部分段落也说明了该问题
        If the cache is of a fixed size, it is not very good to have it too big, either, because that might make the free memory too small and cause swapping (which is also slow). To make the most efficient use of real memory, Linux automatically uses all free RAM for buffer cache, but also automatically makes the cache smaller when programs need more memory.
        Under Linux, you do not need to do anything to make use of the cache, it happens completely automatically. Except for following the proper procedures for shutdown and removing floppies, you do not need to worry about it.

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-20 09:22:1316楼 得分:0
    昨天晚上看女排了,没上论坛。今天我试试大家的方法...
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • fuqd273
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-20 10:12:5217楼 得分:0
    向十五楼学习了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-20 10:47:2018楼 得分:0
    回楼上几位:
    To:NC
    write()和fwrite()的结果是一样的...

    To:Walbing
    谢谢你的测试。我试过你的O_SYNC的方法,诚然在开始阶段如你所说,但是过了一段时间后发生变化,可能是你监视的时间不够长。下面是按照你的方法的打印结果:
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • guosha
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    • 3

    发表于:2008-08-20 10:58:0619楼 得分:3
    按照15楼里引用的那段话, write跟fwrite的结果应该是一样的。是cache机制引起的,因为你频繁写硬盘,导致了大量的内存做为cache, 这个是操作系统的内部实现,我们应用程序基本可以不用担心,除非有什么其它的特殊要求或是限制
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ceasar1134
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-08-20 11:13:2820楼 得分:0
    另外,下面是我的一点理解,大家帮我看看我理解的有没有错误:

    (1)O_SYNC标志设定是体现write()操作的同步问题。如果设定了该标志,那么只有在写的内容的确从物理上写入存储设备,那么write()才返回;

    (2)对我们经常看见的空间理解

        用户空间(user-space) |  内核空间(incore-space)  |  Page cache | disk cache | disk | ram buffer   
        1.用户空间:fwrite()内申请的缓存内存空间。因为用户可以通过setbuffer()等函数对其进行设定;
        2.内核空间:write()内申请的缓存内存空间。这个是OS为写操作申请的内存,对用户来说不可见。
        3、4.Cache: 理解不是很深刻。不知道是物理cache还是可以占用内存的cache;
        5.disk:这个就不说了。
        6.buffer:  不是很理解,感觉它和Page cache是一个东西

    (3)几个同步函数
        1.fflush():The function fflush() forces a write of all user-space buffered data for the given output or update stream
                    via the stream's underlying write function.  The open status of the stream is unaffected.
                    我的理解是该函数将fwrite()里面的缓存内容强行压入底层(内核空间?cache?disk?)。至于压倒哪里就不明白了,我猜是压入到了
                      write()的内核空间。
        2.fsync(): fsync() transfers ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file
                  referred to by the file descriptor fd to the disk device (or other permanent storage device) where that file
                  resides.
                  我的理解是该函数将write()里面内核空间(修改的部分)压入到磁盘设备。但是这里的buffer cache pages又不明白和内核空间的关系了
    修改 删除 举报 引用