[ 
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)

Reply via email to