为什么这个函数会导致内存泄漏?

huangapple go评论62阅读模式
英文:

Why does this function cause memory leaks?

问题

I'm playing around with libfprint and I created a really simple function:

#include <libfprint-2/fprint.h>
#include <glib-2.0/glib-unix.h>

void start_device() {
    FpContext *ctx = fp_context_new();
    fp_context_enumerate(ctx);
}

int main() {
    start_device();
}

I'm compiling with gcc using gcc -fsanitize=address and I'm getting memory leaks and I can't figure out why.

Edit: Output

=================================================================
==55025==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 3 byte(s) in 1 object(s) allocated from:
    #0 0x7f14a8aba6af in __interceptor_malloc (/lib64/libasan.so.8+0xba6af)
    #1 0x7f14a56d3017 in __vasprintf_internal (/lib64/libc.so.6+0x80017)
    #2 0x7af10c9e9cbbccff  (<unknown module>)

SUMMARY: AddressSanitizer: 3 byte(s) leaked in 1 allocation(s).

Edit 2: If I delete `fp_context_enumerate(ctx);` I don't get memory leaks, so I guess there's the problem but I don't know why.

<details>
<summary>英文:</summary>

I&#39;m playing around with `libfprint` and I created a really simple function:
```c
#include &lt;libfprint-2/fprint.h&gt;
#include &lt;glib-2.0/glib-unix.h&gt;

void start_device() {
    FpContext *ctx = fp_context_new();
    fp_context_enumerate(ctx);
}

int main() {
    start_device();
}

I'm compiling with gcc using gcc -fsanitize=address and I'm getting memory leaks and I can't figure out why.

Edit: Output

=================================================================
==55025==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 3 byte(s) in 1 object(s) allocated from:
    #0 0x7f14a8aba6af in __interceptor_malloc (/lib64/libasan.so.8+0xba6af)
    #1 0x7f14a56d3017 in __vasprintf_internal (/lib64/libc.so.6+0x80017)
    #2 0x7af10c9e9cbbccff  (&lt;unknown module&gt;)

SUMMARY: AddressSanitizer: 3 byte(s) leaked in 1 allocation(s).

Edit 2: If I delete fp_context_enumerate(ctx); I don't get memory leaks, so I guess there's the problem but I don't know why.

答案1

得分: 2

1 这个函数被标记为transfer full,意味着它将完全的责任转移到调用者身上,关于指针的情况可以参考这里以获取更多细节。

这意味着在完成后,你需要负责释放它。并且,通过释放,我指的是正常的C free函数。返回的指针是一个GObject类型,因此你应该简单地使用g_object_unref释放它,让对象引用计数框架来处理实际的释放。

换句话说,类似于:

#include <libfprint-2/fprint.h>
#include <glib-2.0/glib-unix.h>

FpContext *start_device() {
    FpContext *ctx = fp_context_new();
    fp_context_enumerate(ctx);
    return ctx;
}

void stop_device(FpContext *ctx) {
    g_object_unref(ctx);
}

int main() {
    FpContext *ctx = start_device();

    // 做你的操作。

    stop_device(ctx);
}

但要记住,未释放的内存和内存泄漏并不一定是相同的事情(甚至不一定是坏事),只有当它们随着时间的推移累积时才会引发问题。

例如,启动代码可能会泄漏从未清理的内存,例如,如果首次调用时创建一个4K缓冲区以进行printf,那么它可能会依赖于进程退出来“释放”该内存。

显然,这种分配不会是真正的问题,因为它不会持续分配4K,只会在开始时分配一次。从这个意义上说,它不比为你的进程分配一个堆栈更严重 为什么这个函数会导致内存泄漏?

事实上,根据你的编辑:

如果我删除 fp_context_enumerate(ctx);,我就不会出现内存泄漏,所以我猜问题就在这里,但我不知道为什么。

这正是我上面所说的(由源代码确认)。使用 fp_context_enumerate() 枚举时,第一次为上下文分配一些内存以检测和存储每个指纹扫描设备的详细信息,并且该内存随后保留。

然后,调用 fp_context_get_devices() 将首先调用 fp_enumerate()(如果已经对该上下文进行了处理,则立即返回),然后返回对在枚举过程中设置的内部数据的指针(1)

(1) 这就是为什么它是transfer none操作,它只是为你提供了一个指向内部数据的指针。因为该数据在进程内从未实际释放,所以不需要引用计数。

英文:

That function is tagged with the transfer full label, meaning it's transferring full responsibility to the caller for the pointer. See here for more detail.

That means you're responsible for freeing it when you're done. And, by freeing, I don't mean the normal C free function. The returned pointer is a GObject one, so you should simply g_object_unref it and let the object reference counting framework take care of the actual freeing.

In other words, something like:

include &lt;libfprint-2/fprint.h&gt;
#include &lt;glib-2.0/glib-unix.h&gt;

FpContext *start_device() {
    FpContext *ctx = fp_context_new();
    fp_context_enumerate(ctx);
    return ctx;
}

void stop_device(FpContext *ctx) {
    g_object_unref(ctx);
}

int main() {
    FpContext *ctx = start_device();

    // Work your magic.

    stop_device(ctx);
}

But keep in mind that un-freed memory and memory leaks aren't quite necessarily the same thing (or even a bad thing), it's just when they add up over time that they cause issues.

For example, startup code may leak memory that it never cleans up, such as if a 4K buffer is created once for printf purposes the first time it's called. It may well rely on process exit to "free" that memory.

Obviously, this allocation won't be a real problem because it's not allocating 4K continuously, just once at the start. In that sense, it's no more serious than having a stack allocated for your process 为什么这个函数会导致内存泄漏?

In fact, based on your edit:

> If I delete fp_context_enumerate(ctx);, I don't get memory leaks so I guess there's the problem but I don't know why.

That's exactly what I was stating above (confirmed by the source code). Enumerating with fp_context_enumerate() will, the first time it's called for a context, allocate some memory to detect and store details on each fingerprint scanning device, and that memory is subsequently kept around.

Then a call to fp_context_get_devices() will first call fp_enumerate() (which returns immediately if already done for that context), then return a pointer to internal data set up during enumeration <sup>(1)</sup>.


<sup>(1)</sup> That's why it's a transfer none operation, it just gives you a pointer to internal data. Because that data is never actually freed within the process, there's no reference counting needed for it.

huangapple
  • 本文由 发表于 2023年4月17日 09:42:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76031197.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定