[
https://issues.apache.org/jira/browse/GROOVY-10657?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18138776#comment-18138776
]
ASF GitHub Bot commented on GROOVY-10657:
-----------------------------------------
daniellansun opened a new pull request, #3226:
URL: https://github.com/apache/groovy/pull/3226
## GROOVY-10657: release subscript assignment RHS after putAt
https://issues.apache.org/jira/browse/GROOVY-10657
`obj[index] = rhs` is compiled as a `putAt` call, but class generation first
stored the right-hand value in a `$rhs` temporary. `CompileStack.removeVar`
only dropped that slot from the compiler's temporary list; it did not clear the
local, so the value stayed strongly reachable until the enclosing method
returned. A direct `putAt` call does not keep that temporary.
`putAt` consumes the receiver, the index, and the value in that order. Plain
subscript assignment already evaluates the receiver and the index into slots
before the right-hand side, so when the assignment value is discarded the RHS
is evaluated as the last argument and no `VariableSlotLoader` is created for
it. When the assignment expression's value is used, or the RHS must run before
the receiver (safe subscript, and synthetic assignments whose RHS re-reads the
subscript), the temporary remains and a reference slot is cleared after the
write. The expression value is still the RHS, not the value returned by `putAt`.
Receiver and index reference temporaries are cleared the same way.
`src/test/groovy/bugs/Groovy10657.groovy` assigns a large array through
subscript syntax and checks that a `WeakReference` is cleared while the
assigning frame is still live.
### Testing
- `bugs.Groovy10657`
- static compilation coverage in `StaticCompilationTest`
> Generated code for overloaded assignment operator prevents garbage collection
> -----------------------------------------------------------------------------
>
> Key: GROOVY-10657
> URL: https://issues.apache.org/jira/browse/GROOVY-10657
> Project: Groovy
> Issue Type: Bug
> Components: Compiler
> Affects Versions: 4.0.3
> Reporter: Jonathan Baxter
> Priority: Major
>
> The groovy code
> {code:java}
> obj["lhs"] = "rhs"{code}
> ostensibly converts to
> {code:java}
> obj.putAt("lhs","rhs")
> {code}
> if {{obj}} has the appropriate {{putAt}} method.
> However , in the process of tracking down a memory leak, I discovered that
> assignment is not exactly equivalent to invocation of the {{putAt}} method.
> Specifically, the code generated by the compiler is equivalent to creating an
> additional temporary variable to hold the rhs of the assignment, and then
> invoking the {{putAt}} method with that temporary variable as its second
> argument:
> {code:java}
> String string = "rhs"
> obj.putAt("lhs",string){code}
> The problem with this is that since {{"rhs"}} now has a live reference
> ({{{}string{}}}), it is not eligible for garbage collection.
> In my particular use case, {{"rhs"}} is a very large object that I would like
> to be automatically garbage collected after the assignment is executed, but
> before the script has finished. But that means I have to use the underlying
> {{putAt}} method rather than the assignment operator, which rather negates
> the benefits of overloading the assignment operator.
> One potential fix would be to simply null out any temporary variables
> constructed by the compiler, so that
> {code:java}
> obj["lhs"] = "rhs"
> {code}
> becomes
> {code:java}
> String string = "rhs"
> obj.putAt("lhs",string)
> string = null <----- "rhs" is now eligible for garbage-collection{code}
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)