[
https://issues.apache.org/jira/browse/GROOVY-10657?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17554050#comment-17554050
]
Jonathan Baxter commented on GROOVY-10657:
------------------------------------------
I am not familiar with byte code, but in case it is useful, I diagnosed by
compiling a test groovy script and then decompiling to java (using CFR).
{{PutAtTest.groovy:}}
{code:java}
class Employees {
Map names;
void putAt(String key, String name) {
names.put(key,name)
}
}
def employees = new Employees()
employees["1"] = "Bob"
employees.putAt("0", "Alice"){code}
Decompiled java ({{{}groovyc PutAtTest.groovy; java -jar cfr-0.152.jar
PutAtTest.class){}}}:
{code:java}
/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* groovy.lang.Binding
* groovy.lang.MetaClass
* groovy.lang.Script
* org.codehaus.groovy.reflection.ClassInfo
* org.codehaus.groovy.runtime.InvokerHelper
* org.codehaus.groovy.runtime.ScriptBytecodeAdapter
* org.codehaus.groovy.vmplugin.v8.IndyInterface
*/
import groovy.lang.Binding;
import groovy.lang.MetaClass;
import groovy.lang.Script;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import org.codehaus.groovy.reflection.ClassInfo;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.vmplugin.v8.IndyInterface;
public class PutAtTest extends Script {
// ... only showing the generated run method for clarity
public Object run() {
CallSite employees = IndyInterface.bootstrap("init", "<init>", 0,
Employees.class);
String string = "Bob"; // <---- the offending extraneous variable
IndyInterface.bootstrap("invoke", "putAt", 0, employees, "1", string);
return IndyInterface.bootstrap("invoke", "putAt", 0, employees, "0",
"Alice"); // <----- no extraneous variable
}
}
{code}
> 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
(v8.20.7#820007)