Repository: go
Issue: encoding/xml: Decoder allocates (and does not release) memory for every control <?...?> encountered
Labels: NeedsInvestigation

<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://github.com/golang/go/wiki/Questions
-->

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.13.7 linux/amd64
</pre>

### Does this issue reproduce with the latest release?
Yes


### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/jcrawford/.cache/go-build"
GOENV="/home/jcrawford/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/jcrawford/git/gowork"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/jcrawford/opt/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/jcrawford/opt/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/jcrawford/git/djtest/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build917252283=/tmp/go-build -gno-record-gcc-switches"
</pre></details>

### What did you do?

<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
-->

Parse an xml file that includes multiple `<?xml version="1.0" encoding="ISO-8859-1" ?>` control tags in the same file.

For each encountered tag, a new reader will be created and will _not_ be released (see below for details).

It is possible to trigger this with a degenerate xml consisting of _only_ repeated tags. As an example, I created a file of ~400kb in size containing 9030 lines of:
```
$ head bad.xml 
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml version="1.0" encoding="ISO-8859-1" ?>
```

```go
package main

import (
	"encoding/xml"
	"golang.org/x/net/html/charset"
	"io"
	"os"
	"runtime/pprof"
)

func main() {
	path := "bad.xml"

	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}

	dec := xml.NewDecoder(f)
	dec.CharsetReader = charset.NewReaderLabel

	type Unused struct{}

	for {
		var value Unused
		err = dec.Decode(&value)
		if err == io.EOF {
			break
		}
	}

	heapOut, err := os.Create("heap")
	if err != nil {
		panic(err)
	}
	pprof.WriteHeapProfile(heapOut)
}
```

### What did you expect to see?
Memory usage somewhat constant


### What did you see instead?
Memory usage grows for every `<?...?>` tag encountered.

Profiling the heap with pprof, the problem appears to be that for every `<?...?>` tag encountered in `xml.Decoder.rawToken`, a new `*charset.Reader` is created through `charset.NewReaderLabel`. The decoder then switches to this new reader with `xml.Decoder.switchToReader` (and also wraps it in another `*bufio.Reader`). However, it looks like references to the previous readers (created when we encountered previous `<?...?>` tags) are never garbage collected. As a result, parsing a large file with enough `<?...?>` can exhaust the system memory.

As an example, running `go tool pprof heap` on the heap file produced above shows 44 MB of usage for the 9030 line example (which was ~400kb in size) 
```
(pprof) top
Showing nodes accounting for 44.67MB, 100% of 44.67MB total
Showing top 10 nodes out of 12
      flat  flat%   sum%        cum   cum%
   25.10MB 56.18% 56.18%    25.10MB 56.18%  golang.org/x/text/transform.NewReader
   19.58MB 43.82%   100%    19.58MB 43.82%  bufio.NewReaderSize
         0     0%   100%    19.58MB 43.82%  bufio.NewReader
         0     0%   100%    44.67MB   100%  encoding/xml.(*Decoder).Decode
         0     0%   100%    44.67MB   100%  encoding/xml.(*Decoder).DecodeElement
         0     0%   100%    44.67MB   100%  encoding/xml.(*Decoder).Token
         0     0%   100%    44.67MB   100%  encoding/xml.(*Decoder).rawToken
         0     0%   100%    19.58MB 43.82%  encoding/xml.(*Decoder).switchToReader
         0     0%   100%    44.67MB   100%  encoding/xml.(*Decoder).unmarshal
         0     0%   100%    25.10MB 56.18%  golang.org/x/net/html/charset.NewReaderLabel
```