CGO:如何在Go中释放使用malloc分配的C内存,以避免内存泄漏。

3

我希望使用CGO调用一个优化的C++ CPU密集型算法实现,从golang中传递一个字符串到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;
    }
}    

在这个链接中,人们提到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吗?正确处理这种情况的方法是什么?我有一个Web服务器会在每个请求中调用C++代码,并希望确保它不会引入任何内存泄漏。

谢谢。


你说调用 C.free 会使代码崩溃。错误是什么?堆栈跟踪是什么样子的?在 Go 中调用 C.free 就像在 C 中调用它一样。你必须知道谁负责释放内存,以及什么时候安全地进行释放。 - JimB
嗨,谢谢。我的代码因为C++中错误地分配内存而崩溃了。但是我注意到即使修复了分配内存的错误,我的代码仍然面临着内存泄漏问题。看来我需要在C++代码中释放内存或者传递go指针到C++指针(避免使用malloc)。那么从C++代码返回字符串到gocode的正确方法是什么? - auxdx
看起来内存泄漏是由于我的代码的其他部分导致的。谢谢。 - auxdx
1
@auxdx 我也遇到了同样的问题。出现了内存泄漏。一旦从C++传递到Go,释放就不再起作用了。 - UmNyobe
2个回答

2

修复你的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)) 在 Go 中释放内存,还是必须在 C++ 代码中的某个地方? - auxdx
顺便说一下,我刚刚意识到我的代码存在内存泄漏问题。看起来我需要释放C++ malloc分配的内存或者将Go指针传递给C++代码。有什么建议吗? - auxdx

1
我正在使用以下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;
    }

}    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接