CGO: how to free memory allocated in C using malloc from go to avoid memory leak

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

CGO: how to free memory allocated in C using malloc from go to avoid memory leak

问题

我正在尝试使用CGO从golang调用一个经过优化的C++ CPU密集型算法实现。基本上,它会将一个字符串传递给C++函数,并获得一个字符串作为返回值。下面是代码的简化版本:

algo.go

  1. package main
  2. //#cgo LDFLAGS:
  3. //#include <stdio.h>
  4. //#include <stdlib.h>
  5. //#include <string.h>
  6. //char* echo(char* s);
  7. import "C"
  8. import "unsafe"
  9. func main() {
  10. cs := C.CString("Hello from stdio\n")
  11. defer C.free(unsafe.Pointer(cs))
  12. var echoOut *C.char = C.echo(cs)
  13. //defer C.free(unsafe.Pointer(echoOut)); -> using this will crash the code
  14. fmt.Println(C.GoString(echoOut))
  15. }

algo.cpp

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <iostream>
  5. #include <math.h>
  6. using namespace std;
  7. extern "C" {
  8. char* echo(char* o) {
  9. int len = sizeof(o) / sizeof(char);
  10. char* out = (char*)malloc(len * sizeof(char));
  11. strcpy(out, o);
  12. return out;
  13. }
  14. }

在这个链接中,有人提到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++代码,并希望确保它不会引入任何内存泄漏。

谢谢。

英文:

I am trying to use CGO to call an optimized C++ CPU-bound implementation of a complex algorithms from golang. Basically, it will pass a string into c++ function and get a string back. A simplified version of the code can be seen in the below:

algo.go

<!-- language: lang-go -->

  1. package main
  2. //#cgo LDFLAGS:
  3. //#include &lt;stdio.h&gt;
  4. //#include &lt;stdlib.h&gt;
  5. //#include &lt;string.h&gt;
  6. //char* echo(char* s);
  7. import &quot;C&quot;
  8. import &quot;unsafe&quot;
  9. func main() {
  10. cs := C.CString(&quot;Hello from stdio\n&quot;)
  11. defer C.free(unsafe.Pointer(cs))
  12. var echoOut *C.char = C.echo(cs)
  13. //defer C.free(unsafe.Pointer(echoOut)); -&gt; using this will crash the code
  14. fmt.Println(C.GoString(echoOut));
  15. }

algo.cpp

<!-- language: lang-c++ -->

  1. #include &lt;stdlib.h&gt;
  2. #include &lt;stdio.h&gt;
  3. #include &lt;string.h&gt;
  4. #include &lt;iostream&gt;
  5. #include &lt;math.h&gt;
  6. using namespace std;
  7. extern &quot;C&quot; {
  8. char* echo(char* o) {
  9. int len = sizeof(o) / sizeof(char);
  10. char* out = (char*)malloc(len * sizeof(char));
  11. strcpy(out, o);
  12. return out;
  13. }
  14. }

In this link, ppl mentions that C++ code should call "free" by itself to free the allocated memory: http://grokbase.com/t/gg/golang-nuts/149hxezftf/go-nuts-cgo-is-it-safe-to-malloc-and-free-in-seperate-c-functions. But then it's very tricky because my c++ function return an allocated pointer so that golang can get the result. I cannot call free in the c++ code? What should be the correct way to handle this? I have a webserver will call the c++ code per each request and want to make sure it doesn't introduce any memory leak.

Thanks.

答案1

得分: 2

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

algo.go:

  1. //algo.go
  2. package main
  3. //#cgo LDFLAGS:
  4. //#include <stdio.h>
  5. //#include <stdlib.h>
  6. //#include <string.h>
  7. //char* echo(char* s);
  8. import "C"
  9. import (
  10. "fmt"
  11. "unsafe"
  12. )
  13. func main() {
  14. cs := C.CString("Hello from stdio\n")
  15. defer C.free(unsafe.Pointer(cs))
  16. var echoOut *C.char = C.echo(cs)
  17. defer C.free(unsafe.Pointer(echoOut))
  18. fmt.Println(C.GoString(echoOut))
  19. }

algo.cpp:

  1. //algo.cpp
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <iostream>
  6. #include <math.h>
  7. using namespace std;
  8. extern "C" {
  9. char* echo(char* o) {
  10. char* out = (char*)malloc(strlen(o)+1);
  11. strcpy(out, o);
  12. return out;
  13. }
  14. }

输出:

  1. $ cd algo
  2. $ go build && ./algo
  3. Hello from stdio
  4. $
英文:

Fix the memory allocation bug in your echo function. For example,

algo.go:

<!-- language: lang-go -->

  1. //algo.go
  2. package main
  3. //#cgo LDFLAGS:
  4. //#include &lt;stdio.h&gt;
  5. //#include &lt;stdlib.h&gt;
  6. //#include &lt;string.h&gt;
  7. //char* echo(char* s);
  8. import &quot;C&quot;
  9. import (
  10. &quot;fmt&quot;
  11. &quot;unsafe&quot;
  12. )
  13. func main() {
  14. cs := C.CString(&quot;Hello from stdio\n&quot;)
  15. defer C.free(unsafe.Pointer(cs))
  16. var echoOut *C.char = C.echo(cs)
  17. defer C.free(unsafe.Pointer(echoOut))
  18. fmt.Println(C.GoString(echoOut))
  19. }

algo.cpp:

<!-- language: lang-c++ -->

  1. //algo.cpp
  2. #include &lt;stdlib.h&gt;
  3. #include &lt;stdio.h&gt;
  4. #include &lt;string.h&gt;
  5. #include &lt;iostream&gt;
  6. #include &lt;math.h&gt;
  7. using namespace std;
  8. extern &quot;C&quot; {
  9. char* echo(char* o) {
  10. char* out = (char*)malloc(strlen(o)+1);
  11. strcpy(out, o);
  12. return out;
  13. }
  14. }

Output:

  1. $ cd algo
  2. $ go build &amp;&amp; ./algo
  3. Hello from stdio
  4. $

答案2

得分: 1

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

我添加了一个循环,以便通过htop跟踪内存泄漏。如果不取消注释,它确实会泄漏,但取消注释后问题就解决了。

以下是代码。

  1. //algo.go
  2. package main
  3. //#cgo LDFLAGS:
  4. //#include <stdio.h>
  5. //#include <stdlib.h>
  6. //#include <string.h>
  7. //char* echo(char* s);
  8. import "C"
  9. import "unsafe"
  10. func main() {
  11. for i := 0; i < 1000000000; i++ {
  12. allocateAndDeallocate()
  13. }
  14. }
  15. func allocateAndDeallocate() {
  16. cs := C.CString("Hello from stdio\n")
  17. defer C.free(unsafe.Pointer(cs))
  18. var echoOut *C.char = C.echo(cs)
  19. defer C.free(unsafe.Pointer(echoOut)) // no crash here
  20. }
  1. //algo.cpp
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <iostream>
  6. #include <math.h>
  7. using namespace std;
  8. extern "C" {
  9. char* echo(char* o) {
  10. int len = sizeof(o) / sizeof(char);
  11. char* out = (char*)malloc(len * sizeof(char));
  12. strcpy(out, o);
  13. return out;
  14. }
  15. }

希望对你有帮助!

英文:

I'm using the following go version go version go1.8 linux/amd64 and I have no problems running your code after uncommenting your deferred C.free.

I added a loop to allow me to track memory leaks via htop. Without the deferred free it does leak, but uncommenting it fixes the problem.

The code is below.

<!-- language: lang-go -->

  1. //algo.go
  2. package main
  3. //#cgo LDFLAGS:
  4. //#include &lt;stdio.h&gt;
  5. //#include &lt;stdlib.h&gt;
  6. //#include &lt;string.h&gt;
  7. //char* echo(char* s);
  8. import &quot;C&quot;
  9. import &quot;unsafe&quot;
  10. func main() {
  11. for i := 0; i &lt; 1000000000; i++ {
  12. allocateAndDeallocate()
  13. }
  14. }
  15. func allocateAndDeallocate() {
  16. cs := C.CString(&quot;Hello from stdio\n&quot;)
  17. defer C.free(unsafe.Pointer(cs))
  18. var echoOut *C.char = C.echo(cs)
  19. defer C.free(unsafe.Pointer(echoOut)) // no crash here
  20. }

<!-- language: lang-c++ -->

  1. //algo.cpp
  2. #include &lt;stdlib.h&gt;
  3. #include &lt;stdio.h&gt;
  4. #include &lt;string.h&gt;
  5. #include &lt;iostream&gt;
  6. #include &lt;math.h&gt;
  7. using namespace std;
  8. extern &quot;C&quot; {
  9. char* echo(char* o) {
  10. int len = sizeof(o) / sizeof(char);
  11. char* out = (char*)malloc(len * sizeof(char));
  12. strcpy(out, o);
  13. return out;
  14. }
  15. }

huangapple
  • 本文由 发表于 2016年2月19日 22:18:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/35507467.html
匿名

发表评论

匿名网友

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

确定