http.Response.Body复制导致大量内存使用增加

So I'm trying to get the response body out of http.Response, to do some manipulation, and then set it back. This is my first attempt at getting it out without draining the http.Response:

bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
    // err
} else {
    cachedBody := string(bodyBytes)

    // Restore the io.ReadCloser to its original state
    // This is causing huge increases in memory usage.
    resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
}

If I send 500 requests through, with a 1mb response size (a large piece of JSON, but it could be any format, not only JSON), the server memory usage goes up to ~400mb, and doesn't come back down. Is this normal? Is there something wrong with the above code? Am I missing something to release the memory?

defer resp.Body.Close() has no effect.

Thanks!


Edit 1

Here's a gist with a simple version of the proxy, including the suggested close call. If the server at localhost:3000 (where it's proxying to) returns a large response, memory usage will quickly increase. In my case, I'm returning a 1mb json file, and sending 500 requests through the go proxy increases memory usage to around 400mb.

The go proxy:
https://gist.github.com/marbemac/300c4c376171cbd27cc3

A simple node server that returns a file in the same directory called large_response.json.
https://gist.github.com/marbemac/55aaa78f5858484d33f6

The Body must be closed after use. Refer to this

Notice that the defer resp.Body.Close() is invoked after you reassign the resp.Body with new value.

ioutil.NopCloser makes Close() do nothing. You're never actually closing it if you defer the close. Close right after reading instead.

bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
    // err
} 
resp.Body.Close()
cachedBody := string(bodyBytes)
// Close() does nothing after this
resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

Also in Go you're typically able to ignore that else because you should probably be returning the error or handling it in the if above it.

Things like this are times when it would be nice to have a sync.Pool as well, since you keep recycling huge buffers, check how it is used in the net/http package for an example.