Skip to content

Feature mempool#343

Open
Gchuchu wants to merge 5 commits intodevfrom
feature-mempool
Open

Feature mempool#343
Gchuchu wants to merge 5 commits intodevfrom
feature-mempool

Conversation

@Gchuchu
Copy link
Collaborator

@Gchuchu Gchuchu commented Mar 16, 2026

问题描述

  1. xy.h中存在的内存泄漏问题
  2. chsrc ls ruby导致的段错误问题

方案与实现

详细描述针对该问题或功能改进的解决方案

  1. 针对问题1,采用了内存池的方案,具体来说
    1.1. 在lib中添加mempool.h并提供实现和接口,用于xy.h
    1.2. 在lib中修改xy.h中分配和释放内存的接口
    1.3. 通过valgrind --tool=memcheck --leak-check=full ./chsrc-debug get ubuntu进行测试,修改后无内存泄露。但是,减少了内存分配次数,些许增加了内存的使用量(25KB->31KB)
    注:mempool.h提供了通用的接口,不仅限于xy.h

  2. 针对问题2,修改了一处xy_strcat的使用错误,具体来说
    2.1. cli_print_target_features函数中的倒数第二处xy_strcat参数量填写错误,修改后正确运行


@Gchuchu Gchuchu self-assigned this Mar 16, 2026
@github-actions
Copy link

Hi @Gchuchu

❤️ 感谢你的贡献!我们将在最少半小时,最多5天内阅读此 PR 并回复你

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new memory-pool allocator intended to back lib/xy.h’s allocation utilities, and begins migrating existing code to use the new allocator semantics.

Changes:

  • Added a new lib/mempool.h implementation and included it from lib/xy.h.
  • Switched many xy.h allocations/frees from malloc/free/realloc/calloc to mp_* APIs.
  • Adjusted some call sites (e.g., fixed an incorrect xy_strcat argument count) and temporarily commented out a few frees in core.c.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
src/framework/core.c Comments out several frees (URL/tmpfile) in code paths that allocate dynamically.
src/chsrc-main.c Fixes xy_strcat varargs count for the default-scope message.
lib/xy.h Routes core allocation helpers and various frees through the new mempool API.
lib/mempool.h Adds the new mempool implementation and auto-init/finalize hooks.

You can also share your feedback on Copilot code review. Take the survey.


/* 释放 url 内存 */
if (url) free (url);
// if (url) free (url);
Comment on lines 1568 to +1569
remove (tmpfile);
free (tmpfile); /* 释放 tmpfile 路径内存 */
// free (tmpfile); /* 释放 tmpfile 路径内存 */
Comment on lines 1588 to +1590
chsrc_run (cmd, RunOpt_Dont_Abort_On_Failure);
remove (tmpfile);
free (tmpfile);
// free (tmpfile);
Comment on lines 1608 to +1610
chsrc_run (cmd, RunOpt_Dont_Abort_On_Failure);
remove (tmpfile);
free (tmpfile);
// free (tmpfile);
Comment on lines 1629 to +1631
chsrc_run (cmd, RunOpt_Dont_Abort_On_Failure);
remove (tmpfile);
free (tmpfile);
// free (tmpfile);
#define MEM_POOL


#include <stddef.h>
Comment on lines +57 to +62
static MemPool *_mp_create (size_t initial_size);
static void *_mp_malloc (MemPool *pool, size_t size);
static void *_mp_calloc (MemPool *pool, size_t nmemb, size_t size);
static void *_mp_realloc(MemPool *pool, void *ptr, size_t new_size);
static void _mp_free (MemPool *pool, void *ptr);
static void _mp_destroy(MemPool *pool);
Comment on lines +281 to +287
__attribute__((constructor))
void
mp_initialize (size_t size)
{
MemPool *pool = _mp_create(size);
global_pool = pool;
}
@ccmywish
Copy link
Contributor

👍👍👍

我的疑惑是:

  1. 修改之前 valgrind 能查看到 xy.h 内存泄漏的具体情况吗?

#300 已经修过一次了 (SeqMap 的实现暂时不管),上次暂时没有发现还有哪里泄漏

  1. 按这个PR的代码来看,只是在原位置切换了内存管理函数,为什么这样就消除了内存泄漏?这个 mempool 有整体释放的步骤吗?

@ccmywish
Copy link
Contributor

ccmywish commented Mar 17, 2026

对于我上个评论的回复

这个 mempool 有整体释放的步骤吗?

代码通过使用 __attribute__((constructor))__attribute__((destructor)) 隐式调用了 mp_initialize()mp_finalize(),这两个在 main() 前后得到执行。


属性的使用问题

  1. GCCClangcon/destructor 有支持,但是MSVC没有(尽管xy.h实现的时候没有考虑MSVC编译的可能性)。

  2. xy.h 这个库的使用,必须首先调用 xy_init(),既然已经存在这个 init 的时机了,我觉得不应该依赖编译器的这种隐式调用,而应该显式调用

  3. 这种属于 hack,平凡的程序尽量不要依赖 hack,中规中矩更适合长时间维护


逻辑上的泄漏依然没有消除

这样存在的问题是:实际上逻辑上存在的代码泄漏没有修复,只不过现在有 mempool 来兜底释放,让 valgrind 的结果看起来似乎没有问题了。xy.h 本身的目标是针对命令行程序,命令行程序都不需要释放,因为都有OS兜底。

抛开“命令行程序”这个程序范畴,内存管理库,从其功能本身来看,整体释放的时机也不应该是在 main 之后,因为OS已经承担了这个工作。


引入 mempool 带来的真正价值没有体现

接上一条,真正需要让内存不泄露的场景应该是:长时间运行的程序,内存不会一直无限增长。如果 xy.h 有意支持这种长时间运行的程序(命令行程序也可能长时间运行),这个 mempool 就应该达到这个目的。

@ccmywish
Copy link
Contributor

ccmywish commented Mar 17, 2026

所以我的建议是:

找到 xy.h 真正存在内存泄漏的函数,因为 #300 已经修过一次了,按理说应该很少了。如果有,大概率是因为实现比较复杂,要分配非常多次,就索性不管了。我觉得 mempool 可以运用在这种场景上:一个函数,内部实现的非常复杂,大量循环性的分配,这个时候用 mempool 接管这个函数,就非常省心,这才能体现 mempool 的价值。

@ccmywish ccmywish requested a review from Mikachu2333 March 17, 2026 03:07
@Mikachu2333
Copy link
Collaborator

  1. 我没看懂引入图啥(如 ccmywish 所说)
  2. 我也没看懂为何现在就不漏了
  3. 确实还有漏点,刚刚仔细找了半天,又借助ai审了一遍xy.h,不过漏的都是以几个kb为单位的玩意,说实话我都不想修……比如说

str = xy_strcat (3, prompt, ": ", xy_str_to_blue (content));
其中 xy_str_to_blue (content) 的这段空间被使用了以后没主动释放,要是改得改成

char *colored = xy_str_to_blue (content);
str = xy_strcat (3, prompt,  ": ", colored);
free (colored);

但是说实话,这种玩意漏就漏了,咱又不是嵌入式,相信搞编程需要设置环境的电脑内存肯定>4G……并且理论上来说chsrc(截至目前)也不会长时间地运行并调用这种神奇东西,就算让它漏还能漏几个点?

@ccmywish
Copy link
Contributor

ccmywish commented Mar 17, 2026

@Gchuchu

@Mikachu2333 新提交了一个 #344 ,修复了一部分代码泄漏的部分。但是像 _xy_log()_xy_log_brkt() 这两个函数,内部为了写的方便分配了许多内存,没有释放。这个是用 mempool 的好位置:写函数的人只管随意用 mp_malloc() 分配,最后在函数结束的时候用 mp_finalize() 释放就行了。

为了达到这个效果,mempool 的设计理念和API可能要调整,应该叫做 arena_malloc() 之类的,mempool 更多的表示是 即用即放,但是我们这里的场景是 随意用,最后放,所以叫 arena, 可参考: https://stackoverflow.com/questions/12825148/what-is-the-meaning-of-the-term-arena-in-relation-to-memory

Copy link
Collaborator

@Mikachu2333 Mikachu2333 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个地方还是请 ccmywish 大佬详细审吧,我本人非科班出身,且对c的研究仅为初学者水平,确实无力提供更多意见。

@happy-game
Copy link
Collaborator

chsrc 的使用场景来看, 它并不是一个需要一直运行的后台应用,在大多数的场景都是进行 测速, 换源,重置源等等。尽管在这个过程中有几处内存泄漏,但我不认为这是一个重要的问题,因为马上就运行结束了,内存就被回收了,并不需要专门引入一个 mempool进行管理,反而会增加本项目的复杂度。

因此我认为不需要 引入 mempool

但是PR中提到的

chsrc ls ruby导致的段错误问题

我觉得是有必要的,不过建议新开一个 PR 进行处理。感谢你的贡献

@Gchuchu
Copy link
Collaborator Author

Gchuchu commented Mar 17, 2026

针对仍然潜在的内存泄漏点:

  1. xy_run_iter_lines函数中使用malloc分配buf,使用xy_str_delete_suffix后buf未释放。PR已经修改;
  2. _xy_log_brkt函数中嵌套的泄漏问题,已提出;
  3. xy_parent_dir函数中!last分支仍然需要释放dir
  4. xy_strcatxy_run_get_stdout函数中在realloc时需要检测返回值。
    修复xy.h中所有可能导致潜在内存泄漏的代码 #344 中已经修改1

针对mempool

针对mempool的引入必要性:

  1. 起初是发现xy.h中的申请和释放点不易发现,且存在内存泄漏,才想要引入mempool,来解决这个问题。
  2. 引入mempool的本意是消除用户使用xy.h所造成的内存泄漏,如果没有必要,那可以丢掉这部分内容。
  3. 为了尽可能小的修改函数逻辑来解决问题,所以只是替换了其中的alloc和free部分。
  4. 如果要引入,con/destructor这种方式可以去除,显式使用,使其更易于维护。

针对mempool的分配和释放方式:

  1. 写的mempool本身支持即用即放。
  2. 因为xy.h中申请和释放内存的点不明显。所以采用随意用,最后放是迫不得已,是兜底。
  3. 就当前场景来说,支持改成arena

针对mempool在做什么:

  1. 用全局的链表管理分配的内存。
  2. 在程序开始时预分配一定大小的内存,中途按需扩大,程序主动释放时回收再用,并在程序退出时统一释放。
  3. mempool的示例结构图如下
image

针对具体对xy.h中函数的分析结果:

  1. xy_malloc0的功能和calloc一致,建议变为calloc的某一具体实现版本。
  2. xy_2strcat因为参数可能是堆内存,也有可能是字面量,所以没有在函数内部释放。
  3. 实际上static并没有起到函数的限制在当前文件的作用,因为xy.h的使用方式是包含。如core.c中仍然可以使用xy_malloc0。所以即便xy.h自身做到内存安全,暴露的部分接口仍会导致内存泄漏,这是core.c中也要修改的原因。
    例如core.c
char *buf = xy_malloc0 (64);
sprintf (buf, "%.2f %s", speed, scale[i]);
  1. xy_str_strip 函数的额外问题:函数去除两边的空格,返回中间的指针,同时会破坏记录内存大小的量,所以返回后释放行为是未定义的,不确定释放多少内存,程序free此指针会直接崩溃。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

改善加强 改善加强

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants