amobbs.com 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
回复: 29

STM32F4使用free函数后,内存没有真正收回,还保留着

[复制链接]
(4010928)

打赏出0元收入0元

发表于 2020-5-23 08:37:25 | 显示全部楼层 |阅读模式
50
本帖最后由 zirong0804 于 2020-5-23 10:33 编辑

从以下程序可以看出:

1-用free释放一片内存之后,操作系统可能不会真正的收回,当你下一次用malloc分配内存的时候如果大小刚好,它会把你刚释放掉的内存分配给你。如何避免这问题?

程序目的:想对inputhead和inputrod这两个数据分别使用Filter和REfilter函数滤波,两者互不干扰

问题根源(在找解决方式):
这个两个指针的地址每次都一样,导致值互相使用,*Memory_Buffer  和 *REMemory_Buffer。
IIR滤波算法需要依赖上次的中间结果,这两个指针分别用于保存对inputhead和inputrod数据滤波的中间结果,而现在他们地址一样,导致指针指向的内容互相利用,没有独立开来

Filter函数中定义指针
float *Memory_Buffer = (float *)malloc(sizeof(float) * (Norder+1));
REfilter函数中定义指针
float *REMemory_Buffer = (float *)malloc(sizeof(float) * (RE_N+1));
因为需要保存上次执行中间结果,所以不能使用memset函数对这两个指针清零


2-程序简化如下:
#define N 2
#define Cutoff  (float)2
...
while(1)
{
...
inputhead = ...  //inputhead值 在while循环内每次更新,每次不一样
inputrod =  ...  //inputrod值 在while循环内每次更新,每次不一样
...
    Filter(N, Cutoff, inputhead);       
    REfilter(N, Cutoff, inputrod);
}
附件: 您需要 登录 才可以下载或查看,没有帐号?注册

最佳答案

查看完整内容

你需要的是全局指针,初始为空时申请一次,中间不再释放。否则你的代码不可能达到你想要的效果,因为你每次用完就释放了。
(4010927)

打赏出0元收入0元

发表于 2020-5-23 08:37:26 来自手机 | 显示全部楼层
zirong0804 发表于 2020-5-23 10:26
找到问题根源,在找解决方式:
这个两个指针的地址每次都一样,导致值互相使用,*Memory_Buffer  和 *REM ...

你需要的是全局指针,初始为空时申请一次,中间不再释放。否则你的代码不可能达到你想要的效果,因为你每次用完就释放了。
(4010794)

打赏出0元收入0元

 楼主| 发表于 2020-5-23 08:39:39 | 显示全部楼层
本帖最后由 zirong0804 于 2020-5-23 08:44 编辑

网上解释:这个跟C函数库的实现机制有关。free释放的内存并不会立刻、完全交回给系统。通常的实现是,malloc并不是每次都找内核要(因为找内核要比较慢),实际上是一次要一堆,然后再按需分配给你,因此每次free并不一定能保证把那次向内核申请的内存都不需要了。此外,即便是free了,很有可能程序很快还要申请,保留也是为了提高效率。
可是我需要它每次都重新分配内存不一样,至少保证az,REaz 这些变量地址不一样
(4010243)

打赏出0元收入0元

发表于 2020-5-23 08:48:50 来自手机 | 显示全部楼层
zirong0804 发表于 2020-5-23 08:39
网上解释:这个跟C函数库的实现机制有关。free释放的内存并不会立刻、完全交回给系统。通常的实现是,mallo ...

为啥要地址不一样,你入魔症了?只要能正常访问不得了?
(4009224)

打赏出0元收入0元

发表于 2020-5-23 09:05:49 | 显示全部楼层
wye11083 发表于 2020-5-23 08:48
为啥要地址不一样,你入魔症了?只要能正常访问不得了?

+1

一般 free() 里只会对内存块做个空闲标记,之后分配空间时不一定就会马上用到这个块
除了每次分配空间大小不固定外,空闲块有时还需要合并,完全不可能给你保证分配空间的位置

这种一般是要求本身就不合理,实在不行就只能自己实现一个 heap 管理了
(4008953)

打赏出0元收入0元

发表于 2020-5-23 09:10:20 | 显示全部楼层
本帖最后由 wye11083 于 2020-5-23 09:11 编辑
wudicgi 发表于 2020-5-23 09:05
+1

一般 free() 里只会对内存块做个空闲标记,之后分配空间时不一定就会马上用到这个块


从效率上考虑,做内存合并所花的时间非常少,只需要往前看一次,再往后看一次(内存分配块的前后都有相应的分配信息),所以通常都应该是立即合并的。具体实现我还真做过测试,在32位程序上连续创建1.95GB的1M块,隔一个释放一个,再申请一个2MB的块就失败了,但是如果连续释放一批是可以立刻重新创建1.9GB的超大块。

还有一点,所有的new/delete似乎都会调用到系统内核,因为我也实测过,I5机器每秒撑死了new/delete 1000000次左右。考虑到内存访问~100ns延时,剩下的~900ns只可能被系统内核给吃了。
(4008857)

打赏出0元收入0元

 楼主| 发表于 2020-5-23 09:11:56 | 显示全部楼层
wye11083 发表于 2020-5-23 08:48
为啥要地址不一样,你入魔症了?只要能正常访问不得了?

谢谢!因为需要对inputhead和inputrod这两个数据分别使用Filter和REfilter函数滤波,两者互不干扰
(4008665)

打赏出0元收入0元

 楼主| 发表于 2020-5-23 09:15:08 | 显示全部楼层
wye11083 发表于 2020-5-23 09:10
从效率上考虑,做内存合并所花的时间非常少,只需要往前看一次,再往后看一次(内存分配块的前后都有相应 ...


谢谢经验分享,目前需要在stm32f407解决这问题
(4008218)

打赏出0元收入0元

发表于 2020-5-23 09:22:35 | 显示全部楼层
zirong0804 发表于 2020-5-23 09:15
谢谢经验分享,目前需要在stm32f407解决这问题

嵌入式平台应该也差不多的,MicroLIB 不知道它的具体实现,但像 FreeRTOS 的 heap_4.c, 每次 vPortFree() 释放完一块内存后会把它放到 freelist 中
freelist 是个按块起始地址排序的链表,插入后马上就会检查是否有相邻的空闲块可合并
(4007970)

打赏出0元收入0元

发表于 2020-5-23 09:26:43 | 显示全部楼层
另外 LZ 先确认一下,你说的要“两者互不干扰”有什么意义吗?
这种顺序执行的代码,也没有用到全局变量的话,本身这么调不会出什么问题呀
(4006690)

打赏出0元收入0元

发表于 2020-5-23 09:48:03 | 显示全部楼层
要不一样的两个空间那简单,直接申请两个啊
(4004683)

打赏出0元收入0元

发表于 2020-5-23 10:21:30 | 显示全部楼层
这程序看着难受,两次if(!t)return t;都没有释放已申请的内存。
(4004370)

打赏出0元收入0元

 楼主| 发表于 2020-5-23 10:26:43 | 显示全部楼层
wudicgi 发表于 2020-5-23 09:26
另外 LZ 先确认一下,你说的要“两者互不干扰”有什么意义吗?
这种顺序执行的代码,也没有用到全局变量的 ...

找到问题根源,在找解决方式:
这个两个指针的地址每次都一样,导致值互相使用,*Memory_Buffer  和 *REMemory_Buffer。
IIR滤波算法需要依赖上次的中间结果,这两个指针分别用于保存对inputhead和inputrod数据滤波的中间结果,而现在他们地址一样,导致指针指向的内容互相利用,没有独立开来

Filter函数中定义指针
float *Memory_Buffer = (float *)malloc(sizeof(float) * (Norder+1));
REfilter函数中定义指针
float *REMemory_Buffer = (float *)malloc(sizeof(float) * (RE_N+1));
因为需要保存上次执行中间结果,所以不能使用memset函数对这两个指针清零
(4004322)

打赏出0元收入0元

 楼主| 发表于 2020-5-23 10:27:31 | 显示全部楼层
本帖最后由 zirong0804 于 2020-5-23 10:29 编辑
armstrong 发表于 2020-5-23 10:21
这程序看着难受,两次if(!t)return t;都没有释放已申请的内存。


你说的对,改
        if(!t)
        {
                free(as);
                free(bs);
                free(Memory_Buffer);
                return t;
        }
目前找到问题根源,在找解决方式:
这个两个指针的地址每次都一样,导致值互相使用,*Memory_Buffer  和 *REMemory_Buffer。
IIR滤波算法需要依赖上次的中间结果,这两个指针分别用于保存对inputhead和inputrod数据滤波的中间结果,而现在他们地址一样,导致指针指向的内容互相利用,没有独立开来

Filter函数中定义指针
float *Memory_Buffer = (float *)malloc(sizeof(float) * (Norder+1));
REfilter函数中定义指针
float *REMemory_Buffer = (float *)malloc(sizeof(float) * (RE_N+1));
因为需要保存上次执行中间结果,所以不能使用memset函数对这两个指针清零
(4003644)

打赏出0元收入0元

发表于 2020-5-23 10:38:49 | 显示全部楼层
zirong0804 发表于 2020-5-23 10:27
你说的对,改
        if(!t)
        {

对于你这样的业务代码,我是没心思去看明白的。内存申请了就应该释放,而且要完整释放;你修改之后还是少释放了几个。
要我说点业务部分感受,我觉得你的设计是错误的。明明有更好的办法,为什么要用这么奇葩的做法呢?别人是不会遇到的。
(4003180)

打赏出0元收入0元

 楼主| 发表于 2020-5-23 10:46:33 | 显示全部楼层
armstrong 发表于 2020-5-23 10:38
对于你这样的业务代码,我是没心思去看明白的。内存申请了就应该释放,而且要完整释放;你修改之后还是少 ...

恩,内存释放问题,刚才代码修改只贴出部分,谢谢
(4002923)

打赏出0元收入0元

发表于 2020-5-23 10:50:50 | 显示全部楼层
这是 C 的 free 机制,定下来的,除非你自己重新做一个特制的 C 编译器。

你的问题没必要来回分配和释放,只要一次性申请几个足够大的 buffer ,用几个标志位和几个指针就搞定了。
(4000701)

打赏出0元收入0元

发表于 2020-5-23 11:27:52 | 显示全部楼层
wye11083 发表于 2020-5-23 09:10
从效率上考虑,做内存合并所花的时间非常少,只需要往前看一次,再往后看一次(内存分配块的前后都有相应 ...

那是你的编译器的问题。
没有人规定malloc究竟是怎么执行。是直接调用操作系统的内存分配器,还是在用户程序里面自己维护一个小的内存池。
你测到的好像是VC的默认实现,他是直接调用系统HeapAlloc的。
其他的C编译器不会这么做。这么做的效率很低。
比如BCB就是自己内部维护了内存池,平常不轻易调用系统的内存分配函数,这样速度比VC快很多倍。我实测过。

至于单片机,根本没有“系统”这个概念。你free一块内存,只代表让C库接下来可以把这一块内存分配给别处使用,其他的就没有了。
你自己有责任保证你不再使用被free了的内存,否则发生什么错误是你自己的事情。
通常的单片机上,free了的内存继续使用,在短时间内几乎肯定没问题,等这块内存下次被分配出去,然后在别处使用,覆盖了你现在使用的这个地方,出问题的时候早已经不在这个现场了,该你慢慢去查究竟错在哪里吧
如果没有特殊理由最好是静态分配所有内存,不要用这些动态内存分配函数。。。。
(3999460)

打赏出0元收入0元

发表于 2020-5-23 11:48:33 | 显示全部楼层
这么简单的问题讨论这么久,你们不累吗?简单说就是STM32F407不能用free、malloc。这样申请的内存并不会释放掉。

网上野火、正点原子的例子里面有一个mymalloc.c      .h的文件。用这个里面的函数去申请、释放,就可以了。

我这样回复,没办法直接发文件给你,你自己去搜索吧。
(3998772)

打赏出0元收入0元

发表于 2020-5-23 12:00:01 | 显示全部楼层
分配完想辦法清空那一片分配好的內存重新用
(3996444)

打赏出0元收入0元

发表于 2020-5-23 12:38:49 来自手机 | 显示全部楼层
honami520 发表于 2020-5-23 11:48
这么简单的问题讨论这么久,你们不累吗?简单说就是STM32F407不能用free、malloc。这样申请的内存并不会释 ...

能用。标准就是这么说的。
它的实现是完全符合标准的,虽然你看起来有点怪。标准没说free要把被它释放的内存清空。只说free了以后可以被下次重新malloc分配岀来使用。
为了节约,free了都是不清内容的,反正malloc得到的内存也规定为没有初值。这俩是配合的。你malloc得到新的内存了得自己清空。就这么规定的。
你说你做了个特殊的分配函数替人在分配和释放的时候自动清空,那是你的做法,并不是可移植的。其它平台上malloc和free不会这样。
我也喜欢做个包装器在分配后自动清零,但这不叫malloc,得改个名,不然别人看到会误解的。
(3996305)

打赏出0元收入0元

发表于 2020-5-23 12:41:08 来自手机 | 显示全部楼层
honami520 发表于 2020-5-23 11:48
这么简单的问题讨论这么久,你们不累吗?简单说就是STM32F407不能用free、malloc。这样申请的内存并不会释 ...

默认不清空的原因是C的原则,不为你用不到的功能付任何代价。很多人需要非零的初值,所以你强制清零就浪费了这些人的时间。
(3994188)

打赏出0元收入6元

发表于 2020-5-23 13:16:25 | 显示全部楼层
mcu的free只是标记内存块未被使用而已, 并不会对内存数据做任何处理.
你如果再申请一个一摸一样的大小的, 必定就是刚刚释放的那个.
建议你不要释放就好了...
或者释放之前, 自己清除一次内存数据, 再释放.
(3992462)

打赏出0元收入0元

 楼主| 发表于 2020-5-23 13:45:11 | 显示全部楼层
本帖最后由 zirong0804 于 2020-5-23 13:46 编辑
正点原子 发表于 2020-5-23 13:16
mcu的free只是标记内存块未被使用而已, 并不会对内存数据做任何处理.
你如果再申请一个一摸一样的大小的,  ...


谢谢,
1-因为滤波需要利用上一次中间值,不可以清零
2-因为这个函数为子函数,需要被外部调用,尝试在子函数所在.c文件的头部使用malloc定义时(楼上提示的全局指针),提示出错
float *Memory_Buffer = (float *)malloc(12);
initializer element is not constant
3-尝试原子的内存管理章节
(3990644)

打赏出0元收入6元

发表于 2020-5-23 14:15:29 | 显示全部楼层
zirong0804 发表于 2020-5-23 13:45
谢谢,
1-因为滤波需要利用上一次中间值,不可以清零
2-因为这个函数为子函数,需要被外部调用,尝试在子 ...

额, 那就是不要释放, 全部用完再释放.
全局变量, 全局指针都是可以的.
(3986350)

打赏出0元收入0元

发表于 2020-5-23 15:27:03 | 显示全部楼层
redroof 发表于 2020-5-23 11:27
那是你的编译器的问题。
没有人规定malloc究竟是怎么执行。是直接调用操作系统的内存分配器,还是在用户 ...

比较认同你的观点,不过觉得LZ的做法有些奇葩, 直接使用全局变量 或者 在初始化时申请好内存空间后不再释放, 这样不是更好。
(3961843)

打赏出0元收入0元

发表于 2020-5-23 22:15:30 来自手机 | 显示全部楼层
foxpro2005 发表于 2020-5-23 15:27
比较认同你的观点,不过觉得LZ的做法有些奇葩, 直接使用全局变量 或者 在初始化时申请好内存空间后不再 ...


楼主的做法是很奇怪。
用户不能依赖内存分配器的行为,只能相信标准规定的那些特性。
其实个人感觉VC在调试状态的那种做法很好,可以让初学者彻底忘记任何对内存分配器的不切实际的假设:
你malloc得到的内存保证不是零,而是著名的烫烫烫或屯屯屯。你free一块内存,那里面的内容立刻变成0x feed(读音类似freed)
哈哈
(3925345)

打赏出0元收入0元

发表于 2020-5-24 08:23:48 来自手机 | 显示全部楼层
redroof 发表于 2020-5-23 22:15
楼主的做法是很奇怪。
用户不能依赖内存分配器的行为,只能相信标准规定的那些特性。
其实个人感觉VC在调 ...

vc只有debug模式才会初始化成0xcc(这是个软件异常指令),释放时写成0xbad。release下不做任何修改。
(3922529)

打赏出0元收入0元

发表于 2020-5-24 09:10:44 来自手机 | 显示全部楼层
wye11083 发表于 2020-5-24 08:23
vc只有debug模式才会初始化成0xcc(这是个软件异常指令),释放时写成0xbad。release下不做任何修改。 ...

就是啊,debug状态下见到这种数据,像楼主这样的就会放弃幻想了。老老实实自己去初始化。。。
(3725150)

打赏出0元收入0元

发表于 2020-5-26 16:00:23 | 显示全部楼层
是不是堆的空间设置不对
回帖提示: 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子论坛 ( 公安交互式论坛备案:44190002001997 粤ICP备09047143号-1 )

GMT+8, 2020-7-8 18:46

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表