Большой объем памяти при демаршалинге большого фрагмента строк - PullRequest
0 голосов
/ 25 мая 2020

Мы наблюдаем большой объем памяти в нашей программе, когда она демаршаллирует JSON ответ, содержащий большой фрагмент (например, один миллион UUID).

Я попытался смоделировать это с помощью некоторого простого теста, программа вызовет API-интерфейс на какой-то фиктивный сервер, который вернет 1M UUID в качестве ответа, а затем демаршалирует ответ в структуру. Фрагмент кода:

type response struct {
    ItemID []string
}

func main() {

    benchRes := testing.Benchmark(func(b *testing.B) {
        for n := 0; n < b.N; n++ {
            resp, err := http.Get("http://localhost:8080")
            if err != nil {
                fmt.Println(err)
            }

            body, _ := ioutil.ReadAll(resp.Body)

            res := response{}
            err = json.Unmarshal(body, &res)
            if err != nil {
                fmt.Println(err)
            }
            resp.Body.Close()
        }
    })
    fmt.Println(benchRes)
    fmt.Println(benchRes.MemString())
}

Результат:

      2         532327650 ns/op
232710540 B/op   1000147 allocs/op

Что более или менее подтверждает наше предыдущее наблюдение, то есть большой объем памяти (~ 200 МБ) только для обработки 1M UUID, который должен быть около 40 МБ (?)

Одно предположение, которое я могу придумать, связано с несколькими выделениями памяти для увеличения емкости среза (?).

Проблема усугубляется тем фактом, что память не похоже, со временем восстанавливается ОС, т.е. RSS продолжает увеличиваться с течением времени, и программа через некоторое время убивает OOM. Мы понимаем, что это может быть связано с тем, как работает Go G C в сочетании с используемым флагом MADV_FREE.

Поэтому теперь мы пытаемся выяснить, есть ли способ хотя бы уменьшить объем памяти Go JSON Unmarsal для нашего варианта использования. Спасибо!

Обновление: Пытался декодировать токен за токеном, memAllo c уменьшено с 238 МБ до 176 МБ

65.50MB 41.52% 41.52%    80.50MB 51.03%  encoding/json.(*scanner).error
      46MB 29.16% 70.68%       46MB 29.16%  encoding/json.(*decodeState).literalStore
   31.27MB 19.82% 90.49%   157.77MB   100%  main.main.func2
      15MB  9.51%   100%       15MB  9.51%  encoding/json.quoteChar

Похоже на https://github.com/golang/go/issues/10335

...