go-json
go-json copied to clipboard
High Mem & GC when compiling large cyclic data structure
go.mod
:
module gojsontest
go 1.21
require (
github.com/goccy/go-json v0.10.3
github.com/stripe/stripe-go/v79 v79.2.0
)
main.go
:
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"github.com/goccy/go-json"
"github.com/stripe/stripe-go/v79"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
bytes, err := json.Marshal(&stripe.CheckoutSession{})
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}
The above code stuck in func (c *Compiler) compile(typeptr uintptr)
phase. Plenty of memory was allocated and then GC contributed most of the CPU time.
A pprof sample is here. pprof.samples.cpu.001.pb.gz
And I used the following script to check how many cycle definition:
package main
import (
"fmt"
"reflect"
"github.com/stripe/stripe-go/v79"
)
func findTypeCycle(t reflect.Type, visited map[reflect.Type]bool, cycleCount *int) {
if visited[t] {
*cycleCount++
return
}
visited[t] = true
defer delete(visited, t)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fieldType := field.Type
if fieldType.Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
if fieldType.Kind() == reflect.Struct {
findTypeCycle(fieldType, visited, cycleCount)
}
}
}
func main() {
cycleCount := 0
findTypeCycle(reflect.TypeOf(stripe.CheckoutSession{}), make(map[reflect.Type]bool), &cycleCount)
fmt.Printf("Total number of cycles detected: %d\n", cycleCount)
}
And the output is:
Total number of cycles detected: 1083720