#How to fix memory leak?

9 messages · Page 1 of 1 (latest)

lethal galleon
#
package main

import (
    "fmt"
    "runtime"
    "runtime/debug"
    "strings"
    "unsafe"
)

func main() {
    r := gen()
    println("starting GC")
    debug.SetGCPercent(100)
    debug.SetMemoryLimit(100_000_000)
    runtime.GC()
    other := strings.Repeat("E", 100_000_000)
    a := uintptr(unsafe.Pointer(unsafe.StringData(r)))
    b := uintptr(unsafe.Pointer(unsafe.StringData(other)))
    fmt.Printf("%v\n", max(a, b)-min(a, b) > uintptr(len(other))) // true
    _ = other
}

func gen() string {
    foo := strings.Repeat("F", 100_000_000)
    runtime.SetFinalizer(&foo, func(s *string) {
        println("finalized!")
    })

    return strings.Split(foo, "")[0]
}

This is the code that allocates a string and then uses a substring. However, even though the remaining part of the string is not accessible, it remains in memory. Is there a way to make the GC clean up the remaining part of the string without copying?

tropic smelt
#

to my knowledge, no, because the substring still holds onto a ref of the underlying string, so releasing it would be undefined behavior

that's my understanding at least, i could be wrong

golden dagger
#

it's same as slice, you have to free the base string by your self

#
func copyString(str string)(string){
  buf := make([]byte, len(str))
  copy(buf, str)
  return (string)(buf)
}
lethal galleon
#

It's a pity that it's not possible without copying. However, not all allocators, of course, allow partial memory release.

golden dagger
#

if for some reason you have to save a part of a large string, then it's not possible without copy, so what are you trying to reach?

#

I don't know what's your case that cause you have to save a part of large string

lethal galleon
#

I found a solution:

package main

import (
    "fmt"
    "runtime"
    "runtime/debug"
    "strings"
    "unsafe"
)

func main() {
    r := gen()
    println("starting GC")
    debug.SetGCPercent(100)
    debug.SetMemoryLimit(1000)
    runtime.GC()
    other := copyToStack(strings.Repeat("E", 10_000))
    a := uintptr(unsafe.Pointer(unsafe.StringData(r)))
    b := uintptr(unsafe.Pointer(unsafe.StringData(other)))
    fmt.Printf("%v %v %v\n", a, b, max(a, b)-min(a, b) >= uintptr(len(other)))
    _ = other
}

//go:nosplit
func gen() string {
    foo := copyToStack(strings.Repeat("F", 10_000))
    runtime.SetFinalizer(&foo, func(s *string) {
        println("finalized!")
    })

    return strings.Split(foo, "")[0]
}

//go:linkname morestack_noctxt runtime.morestack_noctxt
func morestack_noctxt(n int)

//go:nosplit
func copyToStack(s string) string {
  sp := byte(0)
  morestack_noctxt(len(s)-1)
    
  dst := unsafe.Slice(&sp, len(s))
  copy(dst, s)

  return unsafe.String(&sp, len(s))
}

Of course, here I also need to copy the string, but only once. (for example if I want to use more substrings and substrings of substrings).

lethal galleon