Release the array behind a type annotated global - #100
Open
davidanthoff wants to merge 1 commit into
Open
Conversation
`release_module_globals!` nulls every global of a test item's module once the item
finishes, so that whatever the item bound can be collected — Julia cannot unload a
module, and test processes are pooled, so anything still reachable is held for every
later item and every later run on that worker. A global declared with a type,
`x::Vector{UInt8} = …`, rejects `nothing`: the assignment throws on `convert`, the bare
`catch` swallows it, and the array stays reachable. When such a binding holds an array an
empty array of the same type is assigned instead, which the declared type does accept.
Measured on the identical function in TestItemRunner, ten test items binding 100 MB each
behind such a global took the run from 417 MB to 1303 MB on Julia 1.12 and from 344 MB to
1203 MB on 1.13; it stays flat now.
The docstring also had the `const` case backwards. From Julia 1.12 on a `const` cannot be
released at all, because the previous `Core.BindingPartition` goes on holding the old
value even after a `const` redeclaration; before 1.12 the plain assignment goes through
for some values and throws for others.
`testdata/MemoryPackage` probed only a plain global, which is the case that already
worked, so it now probes all three binding forms.
Ported from julia-testitems/TestItemRunner.jl#145.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ports julia-testitems/TestItemRunner.jl#145 —
TestItemServer.release_module_globals!wasbyte-identical to the pre-fix TestItemRunner version, docstring included.
release_module_globals!sets every global of a test item's module tonothingwhen theitem finishes, because Julia cannot unload the module. A global declared with a type does
not accept
nothing:The assignment throws on
convert, the barecatchswallows it, and the array staysreachable. This matters more here than in TestItemRunner: test processes are pooled, so the
array is held not just for the rest of that run but for every later run on that worker.
Neither existing mitigation helps —
gc_between_testitemsis off by default for asingle-process run and
GC.gc(true)cannot free what a live module global points at, andmemory_thresholdis off by default and recycles the whole process when it does fire.When the binding holds an array — the case where the memory is worth having back — the
teardown now falls back to assigning an empty array of the same type. Everything else is
skipped as before, and nothing needs a
VERSIONguard:isa Array,similarwith aDimstuple and
ntupleall work on Julia 1.0, which the test process still supports.Measured on the identical function in TestItemRunner, ten test items binding 100 MB each
behind such a global:
The docstring also had the
constcase backwards — it said aconstis only skipped"on Julia before 1.12". In fact from 1.12 on a
constcannot be released at all: theassignment is rejected, and redeclaring with
constfrees nothing either, because theprevious
Core.BindingPartitiongoes on holding the old value (verified withgc_live_bytes). Before 1.12 the plain assignment goes through for some values, with aWARNING: redefinition of constant, and throws for others.testdata/MemoryPackageprobed a single plain global — the case that already worked — so itnow probes all three binding forms, keeping the two-item / two-run shape that
test/test_memory_release.jldrives. That test fails with the extended fixture and the oldteardown, and passes with this change.
Full suite green on the host Julia: 1143/1143 in 18m55s.
🤖 Generated with Claude Code