Skip to content

Map each Go package, method and variable to one JavaType - #8970

Merged
jkschneider merged 1 commit into
mainfrom
go-shared-type-mapper
Sep 27, 2026
Merged

jkschneider merged 1 commit into
mainfrom
go-shared-type-mapper

Conversation

@jkschneider

@jkschneider jkschneider commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

The Go type mapper built a new JavaType for a package at every reference to it: the assert in assert.Equal, the declaring type of a package-level function, the owner of a package-level variable. It did the same for go.tuple at every multi-value result, and built a new method or variable type at every use. The RPC ref table interns by identity, so none of those copies was ever a ref hit, and every one of them crossed the wire and stayed interned on both sides.

Parsing milvus-io/milvus (3,635 Go files), 945 files in, the Java side held 488,018 JavaType.Class instances. The most-duplicated names were packages and tuples:

FQN instances
go.tuple 74,802
github.com/stretchr/testify/assert 47,988
context 37,153
github.com/milvus-io/milvus/pkg/v3/mlog 27,438
builtin 18,210

Change

The mapper caches packages by path (which also covers go.tuple and builtin), methods by *types.Func, and variables by *types.Var together with the scope they were read in, since a local's owner is its enclosing function. Nothing mutates a method or variable type after mapSignature builds it, so sharing the instances is safe.

ParseProject also keeps one parser, and so one mapper, per module rather than per package. A module's packages already share its ProjectImporter, which caches each imported *types.Package, so a type imported by many packages is the same object in each and now maps to one JavaType. On its own this barely moved the numbers above; the per-use caching is what matters.

Result

With #8512 keeping refs across files and #8969 dropping each tree once the caller is done with it, milvus-io/milvus parses end to end with a 1 GB heap, which previously ran out of memory. Refs averaged about 1,100 per file instead of about 2,600, ending at 3.94M for the project.

This PR doesn't depend on the other two and is useful on its own: the Go peer builds far fewer objects, and anything that holds the trees holds far fewer types.

The Go type mapper built a new JavaType for a package at every reference to it (the
`assert` in `assert.Equal`, the declaring type of a package-level function, the owner of
a package-level variable), for `go.tuple` at every multi-value result, and a new method
or variable type at every use. The RPC ref table interns by identity, so none of those
was ever a ref hit. Parsing milvus-io/milvus, 945 files in, the Java side held 488,018
JavaType.Class instances, among them 74,802 `go.tuple`, 47,988
`github.com/stretchr/testify/assert` and 37,153 `context`.

The mapper now caches them: packages by path, methods by *types.Func, and variables by
*types.Var and the scope they were read in. ParseProject also keeps one parser, and so
one mapper, per module: its packages share the module's ProjectImporter, which caches
each imported *types.Package, so a type imported by many packages is the same object
in each.
@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Sep 27, 2026
@jkschneider
jkschneider merged commit 8404db0 into main Sep 27, 2026
1 check passed
@jkschneider
jkschneider deleted the go-shared-type-mapper branch September 27, 2026 23:30
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant