【问题标题】:CGO: how to free memory allocated in C using malloc from go to avoid memory leakCGO:如何从 go 中使用 malloc 释放在 C 中分配的内存以避免内存泄漏
【发布时间】:2016-02-19 14:18:45
【问题描述】:

我正在尝试使用 CGO 从 golang 调用复杂算法的优化 C++ CPU 绑定实现。基本上,它将一个字符串传递给 c++ 函数并返回一个字符串。代码的简化版本如下:

//algo.go
package main

//#cgo LDFLAGS:
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//char* echo(char* s);
import "C"
import "unsafe"

func main() {
    cs := C.CString("Hello from stdio\n")
    defer C.free(unsafe.Pointer(cs))
    var echoOut *C.char = C.echo(cs)
    //defer C.free(unsafe.Pointer(echoOut)); -> using this will crash the code
    fmt.Println(C.GoString(echoOut));
}


//algo.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>

using namespace std;

extern "C" {
    char* echo(char* o) {
        int len = sizeof(o) / sizeof(char);
        char* out = (char*)malloc(len * sizeof(char));
        strcpy(out, o);
        return out;
    }
}    

在此链接中,ppl 提到 C++ 代码应自行调用“free”以释放分配的内存:http://grokbase.com/t/gg/golang-nuts/149hxezftf/go-nuts-cgo-is-it-safe-to-malloc-and-free-in-seperate-c-functions。但是这非常棘手,因为我的 c++ 函数返回一个分配的指针,以便 golang 可以得到结果。我不能在 C++ 代码中调用 free 吗?处理这个问题的正确方法应该是什么?我有一个网络服务器,每个请求都会调用 c++ 代码,并希望确保它不会引入任何内存泄漏。

谢谢。

【问题讨论】:

  • 您说调用C.free 会使代码崩溃。有什么错误?堆栈跟踪是什么样的?在 Go 中调用 C.free 与在 C 中调用它完全一样。您必须知道谁负责释放内存,以及何时可以安全释放。
  • 您好,谢谢。我的代码崩溃了,因为我在 C++ 中错误地分配了内存。但是我只是注意到即使在修复了分配的内存错误之后,我的代码仍然存在内存泄漏问题。看来我必须在 c++ 代码中释放内存或将 go 指针传递给 c++ 指针(避免使用 malloc)将字符串从 c++ 代码传回 gocode 的正确方法是什么?
  • 似乎内存泄漏是因为我的代码的其他部分。谢谢。
  • @auxdx 我也面临同样的问题。内存泄漏。一旦从 c++ 传递到释放就不再起作用了。

标签: memory-management go memory-leaks malloc cgo


【解决方案1】:

修复 echo 函数中的内存分配错误。例如,

algo.go:

//algo.go
package main

//#cgo LDFLAGS:
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//char* echo(char* s);
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    cs := C.CString("Hello from stdio\n")
    defer C.free(unsafe.Pointer(cs))
    var echoOut *C.char = C.echo(cs)
    defer C.free(unsafe.Pointer(echoOut))
    fmt.Println(C.GoString(echoOut))
}

algo.cpp:

//algo.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>

using namespace std;

extern "C" {
    char* echo(char* o) {
        char* out = (char*)malloc(strlen(o)+1);
        strcpy(out, o);
        return out;
    }
}

输出:

$ cd algo
$ go build && ./algo
Hello from stdio

$ 

【讨论】:

  • 谢谢。有用。但我仍然想知道我是否应该使用 defer C.free(unsafe.Pointer(echoOut)) 释放内存,还是必须在 c++ 代码中的某个地方?
  • 顺便说一句,我刚刚意识到我的代码正在泄漏内存。似乎我必须释放 c++ malloc'ed 内存或将 go 指针传递给 c++ 代码。有什么建议吗?
【解决方案2】:

我正在使用以下 go 版本 go version go1.8 linux/amd64,在取消注释您的延迟 C.free 后运行您的代码没有问题。

我添加了一个循环以允许我通过 htop 跟踪内存泄漏。如果没有延迟免费,它确实会泄漏,但取消注释它可以解决问题。

代码如下。

//algo.go
package main

//#cgo LDFLAGS:
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//char* echo(char* s);
import "C"
import "unsafe"

func main() {
    for i := 0; i < 1000000000; i++ {
        allocateAndDeallocate()
    }
}

func allocateAndDeallocate() {
    cs := C.CString("Hello from stdio\n")
    defer C.free(unsafe.Pointer(cs))
    var echoOut *C.char = C.echo(cs)
    defer C.free(unsafe.Pointer(echoOut)) // no crash here
}

//algo.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>

using namespace std;

extern "C" {

    char* echo(char* o) {
        int len = sizeof(o) / sizeof(char);
        char* out = (char*)malloc(len * sizeof(char));
        strcpy(out, o);
        return out;
    }

}    

【讨论】:

    猜你喜欢
    • 2014-08-26
    • 2010-12-04
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多