[
https://issues.apache.org/jira/browse/GROOVY-10918?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17689394#comment-17689394
]
Eric Milles edited comment on GROOVY-10918 at 2/15/23 11:09 PM:
----------------------------------------------------------------
I would guess that javac handles variable assignment without a temporary. For
groovyc, a left side variable expression could refer to a dynamic variable,
local variable, field, setter method or something dynamic. In order to deal
with all this, classgen tries to provide a unified approach for multiple
options.
https://github.com/apache/groovy/blob/master/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java#L380
I do think I can skip the temp when the target is a local variable (like a
parameter in this case).
I'm trying that out right now. My experiment produces this, in case you want
to compare to the previous bytecode output:
{code}
public java.lang.String test(java.lang.String s);
0 aload_1 [s]
1 invokevirtual java.lang.String.chars() : java.util.stream.IntStream
[92]
4 new Scratch$_test_closure1 [94]
7 dup
8 aload_0 [this]
9 aload_0 [this]
10 invokespecial Scratch$_test_closure1(java.lang.Object,
java.lang.Object) [97]
13 invokedynamic 2 cast(groovy.lang.Closure) :
java.util.function.IntFunction [100]
18 invokeinterface
java.util.stream.IntStream.mapToObj(java.util.function.IntFunction) :
java.util.stream.Stream [106] [nargs: 2]
23 invokestatic java.util.stream.Collectors.joining() :
java.util.stream.Collector [112]
26 invokeinterface
java.util.stream.Stream.collect(java.util.stream.Collector) : java.lang.Object
[118] [nargs: 2]
31 invokedynamic 2 cast(java.lang.Object) : java.lang.String [61]
36 astore_1 [s]
37 aload_1 [s]
38 pop
39 aload_0 [this]
40 aload_1 [s]
41 iconst_0
42 invokevirtual java.lang.String.charAt(int) : char [122]
45 invokestatic java.lang.Character.valueOf(char) : java.lang.Character
[128]
48 invokevirtual Scratch.println(java.lang.Object) : void [131]
51 aconst_null
52 pop
53 aload_1 [s]
54 invokevirtual java.lang.String.chars() : java.util.stream.IntStream
[92]
57 new Scratch$_test_closure2 [133]
60 dup
61 aload_0 [this]
62 aload_0 [this]
63 invokespecial Scratch$_test_closure2(java.lang.Object,
java.lang.Object) [134]
66 invokedynamic 2 cast(groovy.lang.Closure) :
java.util.function.IntFunction [100]
71 invokeinterface
java.util.stream.IntStream.mapToObj(java.util.function.IntFunction) :
java.util.stream.Stream [106] [nargs: 2]
76 invokestatic java.util.stream.Collectors.joining() :
java.util.stream.Collector [112]
79 invokeinterface
java.util.stream.Stream.collect(java.util.stream.Collector) : java.lang.Object
[118] [nargs: 2]
84 invokedynamic 2 cast(java.lang.Object) : java.lang.String [61]
89 astore_1 [s]
90 aload_1 [s]
91 pop
92 aload_0 [this]
93 aload_1 [s]
94 iconst_0
95 invokevirtual java.lang.String.charAt(int) : char [122]
98 invokestatic java.lang.Character.valueOf(char) : java.lang.Character
[128]
101 invokevirtual Scratch.println(java.lang.Object) : void [131]
104 aconst_null
105 pop
106 aload_1 [s]
107 invokevirtual java.lang.String.chars() : java.util.stream.IntStream
[92]
110 new Scratch$_test_closure3 [136]
113 dup
114 aload_0 [this]
115 aload_0 [this]
116 invokespecial Scratch$_test_closure3(java.lang.Object,
java.lang.Object) [137]
119 invokedynamic 2 cast(groovy.lang.Closure) :
java.util.function.IntFunction [100]
124 invokeinterface
java.util.stream.IntStream.mapToObj(java.util.function.IntFunction) :
java.util.stream.Stream [106] [nargs: 2]
129 invokestatic java.util.stream.Collectors.joining() :
java.util.stream.Collector [112]
132 invokeinterface
java.util.stream.Stream.collect(java.util.stream.Collector) : java.lang.Object
[118] [nargs: 2]
137 invokedynamic 2 cast(java.lang.Object) : java.lang.String [61]
142 astore_1 [s]
143 aload_1 [s]
144 pop
145 aload_0 [this]
146 aload_1 [s]
147 iconst_0
148 invokevirtual java.lang.String.charAt(int) : char [122]
151 invokestatic java.lang.Character.valueOf(char) : java.lang.Character
[128]
154 invokevirtual Scratch.println(java.lang.Object) : void [131]
157 aconst_null
158 pop
159 aload_1 [s]
160 areturn
{code}
was (Author: emilles):
I would guess that javac handles variable assignment without a temporary. For
groovyc, a left side variable expression could refer to a dynamic variable,
local variable, field, setter method or something dynamic. In order to deal
with all this, classgen tries to provide a unified approach for multiple
options.
https://github.com/apache/groovy/blob/master/src/main/java/org/codehaus/groovy/classgen/asm/BinaryExpressionHelper.java#L380
I do think I can skip the temp when the target is a local variable (like a
parameter in this case). I'm trying that out right now.
> Memory leak: local variable values are not discarded
> ----------------------------------------------------
>
> Key: GROOVY-10918
> URL: https://issues.apache.org/jira/browse/GROOVY-10918
> Project: Groovy
> Issue Type: Bug
> Components: groovy-runtime
> Affects Versions: 4.0.9
> Reporter: Andriy Rysin
> Priority: Major
> Labels: MemoryLeak
> Attachments: TestOOM.groovy, TestOOMIcj.java, TestOOMJ.java,
> TestOOM_works.groovy, groovy_oom.png
>
>
> When I run the code below with 3 statements with closures inside the method
> the local var (parameter) values (all 4 of them) are staying in memory.
> I don't see the same problem if I run corresponding Java code with lambdas.
> Run TestOOM.groovy with
> -Xmx600M -XX:+HeapDumpOnOutOfMemoryError
> Notice groovy fails:
> Y
> Z
> java.lang.OutOfMemoryError: Java heap space
> Dumping heap to java_pid147612.hprof ...
> Heap dump file created [497819587 bytes in 0.136 secs]
> Caught: java.lang.OutOfMemoryError: Java heap space
> java.lang.OutOfMemoryError: Java heap space
> at test.TestOOM.test(TestOOM.groovy:31)
> at test.TestOOM.run(TestOOM.groovy:41)
> But Java version does not.
> It looks like all the values of s are still in memory (see screenshot), even
> though previous values should be discarded.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)