This page is no longer maintained — Please continue to the home page at www.scala-lang.org

vals locking things into memory

20 replies
Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Hi,
Again I've hit an application where values are locked into memory because the're referred to by vals that are modelled as local vars in the bytecode, and the method isn't run more than once so the VM doesn't get a chance to inline that away.
    val relationMatrix = mkRelationMatrix    val symmetrical = relationMatrix add relationMatrix.flip    val rowNormalizedT = symmetrical.flip.normalizeColumns     val rowColNormalized = rowNormalizedT.flip.normalizeColumns
I only want rowColNormalized locked into memory, as I go on to perform a bajillion operations on it. However, the other 3 matrices stick about for the entire lifetime of the program, eating valuable ram. I have had to re-write this with vars and explicit assignment to null.
Please can the compiler generate localvar=null ops at the first possible place that the vals are no longer used? It would make it immeasurably easier to write scala programs with lower memory overhead.
Thanks,
Matthew
--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143
nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: vals locking things into memory
On Thu, Nov 3, 2011 at 9:30 AM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:
Hi,
Again I've hit an application where values are locked into memory because the're referred to by vals that are modelled as local vars in the bytecode, and the method isn't run more than once so the VM doesn't get a chance to inline that away.
    val relationMatrix = mkRelationMatrix    val symmetrical = relationMatrix add relationMatrix.flip    val rowNormalizedT = symmetrical.flip.normalizeColumns     val rowColNormalized = rowNormalizedT.flip.normalizeColumns
I only want rowColNormalized locked into memory, as I go on to perform a bajillion operations on it. However, the other 3 matrices stick about for the entire lifetime of the program, eating valuable ram. I have had to re-write this with vars and explicit assignment to null.

Why rewrite with vars and null? Why not just have a method to construct and hand back rowColNormalized? That way you separate the creation code, and associated temporary vals, from the operating code. Seems cleaner, more reusable, and negates any reason to use vars with manual nulling.  

Please can the compiler generate localvar=null ops at the first possible place that the vals are no longer used? It would make it immeasurably easier to write scala programs with lower memory overhead.
Thanks,
Matthew
--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: vals locking things into memory

Why rewrite with vars and null? Why not just have a method to construct and hand back rowColNormalized? That way you separate the creation code, and associated temporary vals, from the operating code. Seems cleaner, more reusable, and negates any reason to use vars with manual nulling.

These happen to be a run of statements in a main method. They could just as easily be in a run() method of a Runnable, or the body of a future or whatever. I could generate one def for each and every scope, but then the code is unreadable. That would be 4 defs for what is currently 4 vals. Scale that up to 10 vals and you have completely unreadable spaghetti. If scalac decides to inline those defs, as they surely are trivial, we're back to having locals in the bytecode that aren't cleared, but now they are cryptic.
Matthew
 

Please can the compiler generate localvar=null ops at the first possible place that the vals are no longer used? It would make it immeasurably easier to write scala programs with lower memory overhead.
Thanks,
Matthew
--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143




--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143
Ittay Dror 2
Joined: 2010-05-05,
User offline. Last seen 42 years 45 weeks ago.
Re: vals locking things into memory
body p { margin-bottom: 0cm; margin-top: 0pt; }



Matthew Pocock wrote:
CAHQS_exka1ADid83SRDZ187XnrZ0OzTD0xjqHPsVezUDLGk-zg [at] mail [dot] gmail [dot] com" type="cite">Hi,
Again I've hit an application where values are locked into memory because the're referred to by vals that are modelled as local vars in the bytecode, and the method isn't run more than once so the VM doesn't get a chance to inline that away.
    val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     val rowColNormalized = rowNormalizedT.flip.normalizeColumns

Do you mean these vals are defined inside a class?

Maybe you should try:

val rowColNormalized = {
    val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     rowNormalizedT.flip.normalizeColumns
}

or create a helper method

Ittay

CAHQS_exka1ADid83SRDZ187XnrZ0OzTD0xjqHPsVezUDLGk-zg [at] mail [dot] gmail [dot] com" type="cite">
I only want rowColNormalized locked into memory, as I go on to perform a bajillion operations on it. However, the other 3 matrices stick about for the entire lifetime of the program, eating valuable ram. I have had to re-write this with vars and explicit assignment to null.
Please can the compiler generate localvar=null ops at the first possible place that the vals are no longer used? It would make it immeasurably easier to write scala programs with lower memory overhead.
Thanks,
Matthew
--
Dr Matthew Pocock Integrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster [at] gmail [dot] com" target="_blank" rel="nofollow">turingatemyhamster@gmail.com gchat: turingatemyhamster [at] gmail [dot] com" target="_blank" rel="nofollow">turingatemyhamster@gmail.com msn: matthew_pocock [at] yahoo [dot] co [dot] uk" target="_blank" rel="nofollow">matthew_pocock@yahoo.co.uk irc.freenode.net: drdozer skype: matthew.pocock tel: (0191) 2566550 mob: +447535664143
nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: vals locking things into memory
On Thu, Nov 3, 2011 at 9:44 AM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:

Why rewrite with vars and null? Why not just have a method to construct and hand back rowColNormalized? That way you separate the creation code, and associated temporary vals, from the operating code. Seems cleaner, more reusable, and negates any reason to use vars with manual nulling.

These happen to be a run of statements in a main method. They could just as easily be in a run() method of a Runnable, or the body of a future or whatever. I could generate one def for each and every scope, but then the code is unreadable. That would be 4 defs for what is currently 4 vals.

No, you're only working on the result, so you'd do this:
def newRowColNormalized() = {     val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     rowNormalizedT.flip.normalizeColumns }val rowColNormalized = newRowColNormalized() // do work Or do it like Ittay suggested.
Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: vals locking things into memory
On 3 November 2011 14:49, Nils Kilden-Pedersen <nilskp@gmail.com> wrote:
No, you're only working on the result, so you'd do this:
def newRowColNormalized() = {     val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     rowNormalizedT.flip.normalizeColumns }val rowColNormalized = newRowColNormalized() // do work Or do it like Ittay suggested.

What prevents scalac from inlining newRowColNormalized? If it gets inlined we still have the same problem. Within newRowColNormalized, we still are not freeing these matrices at the earliest opportunity e.g. relationMatrix is sticking about until the end of the def.

Matthew --
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143
H-star Development
Joined: 2010-04-14,
User offline. Last seen 2 years 26 weeks ago.
Re: vals locking things into memory
i don't understand why you care about this. and even if you set everything to null explicitly - the gc doesn't care. it cleans up your stuff when it pleases to. you are solving a non existent problem

Am 03.11.2011 15:59, schrieb Matthew Pocock:
mkPXxTDJgHLHx-PWOPVA [at] mail [dot] gmail [dot] com" type="cite"> On 3 November 2011 14:49, Nils Kilden-Pedersen <nilskp [at] gmail [dot] com" rel="nofollow">nilskp@gmail.com> wrote:
No, you're only working on the result, so you'd do this:
def newRowColNormalized() = {     val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     rowNormalizedT.flip.normalizeColumns } val rowColNormalized = newRowColNormalized() // do work   Or do it like Ittay suggested.

What prevents scalac from inlining newRowColNormalized? If it gets inlined we still have the same problem. Within newRowColNormalized, we still are not freeing these matrices at the earliest opportunity e.g. relationMatrix is sticking about until the end of the def.

Matthew   --
Dr Matthew Pocock Integrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster [at] gmail [dot] com" target="_blank" rel="nofollow">turingatemyhamster@gmail.com gchat: turingatemyhamster [at] gmail [dot] com" target="_blank" rel="nofollow">turingatemyhamster@gmail.com msn: matthew_pocock [at] yahoo [dot] co [dot] uk" target="_blank" rel="nofollow">matthew_pocock@yahoo.co.uk irc.freenode.net: drdozer skype: matthew.pocock tel: (0191) 2566550 mob: +447535664143

nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: vals locking things into memory
On Thu, Nov 3, 2011 at 10:53 AM, HamsterofDeath <h-star@gmx.de> wrote:
i don't understand why you care about this. and even if you set everything to null explicitly - the gc doesn't care. it cleans up your stuff when it pleases to. you are solving a non existent problem

The GC does care if he nulls the references in this particular example, but it's a rather odd way to deal with a simple problem. 

Am 03.11.2011 15:59, schrieb Matthew Pocock:
On 3 November 2011 14:49, Nils Kilden-Pedersen <nilskp@gmail.com> wrote:
No, you're only working on the result, so you'd do this:
def newRowColNormalized() = {     val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     rowNormalizedT.flip.normalizeColumns } val rowColNormalized = newRowColNormalized() // do work   Or do it like Ittay suggested.

What prevents scalac from inlining newRowColNormalized? If it gets inlined we still have the same problem. Within newRowColNormalized, we still are not freeing these matrices at the earliest opportunity e.g. relationMatrix is sticking about until the end of the def.

Matthew   --
Dr Matthew Pocock Integrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.com msn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozer skype: matthew.pocock tel: (0191) 2566550 mob: +447535664143


H-star Development
Joined: 2010-04-14,
User offline. Last seen 2 years 26 weeks ago.
Re: vals locking things into memory
we are talking about a *local* val, right? there will only be a difference if the gc kicks in between "tmp = null" and the end of the method. what are the odds?

Am 03.11.2011 17:09, schrieb Nils Kilden-Pedersen:
CABDULvV7ymjzyorP2cqWNuLqS0_eK6Qfo1-NOnBJxdg9JPN4mw [at] mail [dot] gmail [dot] com" type="cite"> On Thu, Nov 3, 2011 at 10:53 AM, HamsterofDeath <h-star [at] gmx [dot] de" rel="nofollow">h-star@gmx.de> wrote:
i don't understand why you care about this. and even if you set everything to null explicitly - the gc doesn't care. it cleans up your stuff when it pleases to. you are solving a non existent problem

The GC does care if he nulls the references in this particular example, but it's a rather odd way to deal with a simple problem.  

Am 03.11.2011 15:59, schrieb Matthew Pocock:
On 3 November 2011 14:49, Nils Kilden-Pedersen <nilskp [at] gmail [dot] com" target="_blank" rel="nofollow">nilskp@gmail.com> wrote:
No, you're only working on the result, so you'd do this:
def newRowColNormalized() = {     val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     rowNormalizedT.flip.normalizeColumns } val rowColNormalized = newRowColNormalized() // do work   Or do it like Ittay suggested.

What prevents scalac from inlining newRowColNormalized? If it gets inlined we still have the same problem. Within newRowColNormalized, we still are not freeing these matrices at the earliest opportunity e.g. relationMatrix is sticking about until the end of the def.

Matthew   --
Dr Matthew Pocock Integrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster [at] gmail [dot] com" target="_blank" rel="nofollow">turingatemyhamster@gmail.com gchat: turingatemyhamster [at] gmail [dot] com" target="_blank" rel="nofollow">turingatemyhamster@gmail.com msn: matthew_pocock [at] yahoo [dot] co [dot] uk" target="_blank" rel="nofollow">matthew_pocock@yahoo.co.uk irc.freenode.net: drdozer skype: matthew.pocock tel: (0191) 2566550 mob: +447535664143



nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: vals locking things into memory
On Thu, Nov 3, 2011 at 9:59 AM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:
What prevents scalac from inlining newRowColNormalized? If it gets inlined we still have the same problem.

Now that's an interesting question. If it's inlined and in such a way that references are retained, I would consider that a bug.
Hopefully someone with intimate knowledge of the compiler can elaborate on this?
Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: vals locking things into memory


On 3 November 2011 16:26, HamsterofDeath <h-star@gmx.de> wrote:
we are talking about a *local* val, right? there will only be a difference if the gc kicks in between "tmp = null" and the end of the method. what are the odds?

Just about absolutely guaranteed if the local is in main, or in run()/future that does significant work. Really, I'm not inventing a non-existent problem here.
Matthew 
--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143
nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: vals locking things into memory
On Thu, Nov 3, 2011 at 11:26 AM, HamsterofDeath <h-star@gmx.de> wrote:
we are talking about a *local* val, right? there will only be a difference if the gc kicks in between "tmp = null" and the end of the method. what are the odds?

Apparently very high in the example presented.
ScottC 2
Joined: 2011-01-30,
User offline. Last seen 42 years 45 weeks ago.
Re: vals locking things into memory

On Nov 3, 7:59 am, Matthew Pocock
wrote:
> On 3 November 2011 14:49, Nils Kilden-Pedersen wrote:
>
> What prevents scalac from inlining newRowColNormalized? If it gets inlined
> we still have the same problem. Within newRowColNormalized, we still are
> not freeing these matrices at the earliest opportunity e.g. relationMatrix
> is sticking about until the end of the def.
>
> Matthew
>

If scalac were to do such an inline, it would be a bug if it did not
leave the scoping and visibility the same after the inline.
javac avoids inlining because the JVM does more detailed analysis and
profiling of small methods than it does large ones, and inlining
increases the size of methods, reducing optimization opportunities.
When the JVM inlines, the local variables of the inlined method are
not in scope afterward (are off the bytecode stack) -- If it was Java,
it is as if the inlined method contents were surrounded by curly
brackets in the calling method.

I'm not sure what Scala's compiler does, but I suppose it could push
values off of the stack as early as possible (this is not the same as
setting them to null). The JVM does this sort of analysis on methods
that are compiled. Have you tried configuring the JVM to compile more
aggressively? Try -XX:+TieredCompilation, which will do very basic
compilation earlier than -server or -client will by default. I'm not
sure if it will do the visibility analysis on a main method.

> --
> Dr Matthew Pocock
> Integrative Bioinformatics Group, School of Computing Science, Newcastle
> University
> mailto: turingatemyhams...@gmail.com
> gchat: turingatemyhams...@gmail.com
> msn: matthew_poc...@yahoo.co.uk
> irc.freenode.net: drdozer
> skype: matthew.pocock
> tel: (0191) 2566550
> mob: +447535664143

Stephan Houben
Joined: 2010-10-29,
User offline. Last seen 42 years 45 weeks ago.
Re: vals locking things into memory

Hi Matthew,

On which Java runtime implementation did you observe that nulling it out actually matters?

Most JITs do sufficient analysis that they would immediately observe that relationMatrix
etc. are dead after their first use and not include them in the stack maps which are
generated for the benefit of the GC (and most likely recycle the register/stack slot
for something else).

For that same reason, statements like
relationMatrix = null;
when relationMatrix is never used again would get optimized away so it
makes little sense for the scala compiler to emit them.

Only on an interpreted implementation I would expect any effect of explicit nulling.

Stephan

On 11/03/2011 03:59 PM, Matthew Pocock wrote:
> On 3 November 2011 14:49, Nils Kilden-Pedersen > wrote:
>
> No, you're only working on the result, so you'd do this:
>
> def newRowColNormalized() = {
> val relationMatrix = mkRelationMatrix
> val symmetrical = relationMatrix add relationMatrix.flip
> val rowNormalizedT = symmetrical.flip.normalizeColumns
> rowNormalizedT.flip.normalizeColumns
> }
> val rowColNormalized = newRowColNormalized()
> // do work
> Or do it like Ittay suggested.
>
>
> What prevents scalac from inlining newRowColNormalized? If it gets inlined we still have the same problem. Within newRowColNormalized, we still are not freeing these
> matrices at the earliest opportunity e.g. relationMatrix is sticking about until the end of the def.

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: vals locking things into memory
HI,

On 3 November 2011 16:13, Stephan Houben <stephanh@planet.nl> wrote:
Hi Matthew,

On which Java runtime implementation did you observe that nulling it out actually matters?

Both latest 6 and 7. 

Most JITs do sufficient analysis that they would immediately observe that relationMatrix
etc. are dead after their first use and not include them in the stack maps which are
generated for the benefit of the GC (and  most likely recycle the register/stack slot
for something else).

Well, this doesn't seem to be kicking in for me - perhaps main() is being interpreted, as no CPU time is actually spent in that method - all the time is in things it calls.
Telling me the problem is impossible or not happening is a bit frustrating for me, as I can see it happening and I can see nulling out things fixing it.
Matthew 

Stephan

On 11/03/2011 03:59 PM, Matthew Pocock wrote:
On 3 November 2011 14:49, Nils Kilden-Pedersen <nilskp@gmail.com <mailto:nilskp@gmail.com>> wrote:

   No, you're only working on the result, so you'd do this:

   def newRowColNormalized() = {
        val relationMatrix = mkRelationMatrix
        val symmetrical = relationMatrix add relationMatrix.flip
        val rowNormalizedT = symmetrical.flip.normalizeColumns
        rowNormalizedT.flip.normalizeColumns
   }
   val rowColNormalized = newRowColNormalized()
   // do work
   Or do it like Ittay suggested.


What prevents scalac from inlining newRowColNormalized? If it gets inlined we still have the same problem. Within newRowColNormalized, we still are not freeing these
matrices at the earliest opportunity e.g. relationMatrix is sticking about until the end of the def.






--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143
Jason Zaugg
Joined: 2009-05-18,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: vals locking things into memory
On Thu, Nov 3, 2011 at 10:17 PM, Matthew Pocock <turingatemyhamster@gmail.com> wrote:
HI,

On 3 November 2011 16:13, Stephan Houben <stephanh@planet.nl> wrote:
Hi Matthew,

On which Java runtime implementation did you observe that nulling it out actually matters?

Both latest 6 and 7. 

Most JITs do sufficient analysis that they would immediately observe that relationMatrix
etc. are dead after their first use and not include them in the stack maps which are
generated for the benefit of the GC (and  most likely recycle the register/stack slot
for something else).

Well, this doesn't seem to be kicking in for me - perhaps main() is being interpreted, as no CPU time is actually spent in that method - all the time is in things it calls.
Telling me the problem is impossible or not happening is a bit frustrating for me, as I can see it happening and I can see nulling out things fixing it.

Clojure nulls out locals after they have reached end-of-life, motivated by the common problem of inadvertently retaining the head of a stream [1].
  scala> def foo = { val s = Stream.from(0); s.drop(100000000).take(1) }  foo: scala.collection.immutable.Stream[Int]
  scala> foo  java.lang.OutOfMemoryError: Java heap space
-jason
[1] http://groups.google.com/group/clojure/msg/9b4e268b85c20cd6
Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: vals locking things into memory
On 3 November 2011 18:25, ScottC <scott.carey@gmail.com> wrote:

I've tested some little programs and can't get scalac to inline things that are annotated with @inline, even with -optimize, so possibly this isn't a problem in reality with 1.9.1. 
If scalac were to do such an inline, it would be a bug if it did not
leave the scoping and visibility the same after the inline.

Unless scalac is explicitly clearing values that its stored away using astore at the boundary matching the termination of the inlined function, the inlined version will be holding onto references longer than the non-inlined one. However, since I can't seem to convince scalac to do any inlining, I can't show you the bytecode that would demonstrate this one way or the other.  
 
When the JVM inlines, the local variables of the inlined method are
not in scope afterward (are off the bytecode stack)

Oh? That would mean that a JVM 'inline' is pushing and popping the stack, which is exactly the operation that inlining is intended to avoid. I suspect that what actually happens is that it transforms the inlined code into a block, which allows the re-uses of astore indexes. However, I've never taken a modern, optimizing JVM apart so can't say for sure.  
-- If it was Java,
it is as if the inlined method contents were surrounded by curly
brackets in the calling method.

Curly brackets scope the visibility of the variable in the Java source. These boundaries don't exist at all in the bytecode. In particular, any objects you store in a local variable in a curly-bracket block are not removed from the astore'd local var at the end of the curlies. What can happen is that the same astore location is re-used after the curly-bracket block, which de-facto removes the reference to that resource.
Try compiling this: 
// java codepublic class TestScope {    public void testNoScoping() {      Object a = new Object();      System.out.println("Hello");       Object b = new Object();    }
    public void testWithScoping() {      {        Object a = new Object();       }      System.out.println("Hello");      Object b = new Object();    }}
Compile this and run scalap - you will see that the two generate identical bytecode, except that in the 1st case b is saved with an astore_2 and in the 2nd case, b is saved with astore_1. However, the value in local var 1 is still pointing to 'a' up until after the curlies have been exited and Hello has been printed. So, in the 2nd case, 'a' can't be GC'ed until after Hello is printed even though it is out of scope from the point of view of java source visibility.

I'm not sure what Scala's compiler does, but I suppose it could push
values off of the stack as early as possible (this is not the same as
setting them to null).

We're not talking about the stack, we're talking about things in the local var table that where put there by an astore. Assuming hotspot doesn't chew on this code, you /must/ either pop the stack or replace the astore'd ref with null to allow those objects to be GC'ed. Otherwise, that ref in the local var table prevents it being GC'ed.  Matthew

> --
> Dr Matthew Pocock
> Integrative Bioinformatics Group, School of Computing Science, Newcastle
> University
> mailto: turingatemyhams...@gmail.com
> gchat: turingatemyhams...@gmail.com
> msn: matthew_poc...@yahoo.co.uk
> irc.freenode.net: drdozer
> skype: matthew.pocock
> tel: (0191) 2566550
> mob: +447535664143



--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster@gmail.comgchat: turingatemyhamster@gmail.com msn: matthew_pocock@yahoo.co.ukirc.freenode.net: drdozerskype: matthew.pocock tel: (0191) 2566550mob: +447535664143
ScottC 2
Joined: 2011-01-30,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: vals locking things into memory

On Thu, Nov 3, 2011 at 2:46 PM, Matthew Pocock
wrote:
> On 3 November 2011 18:25, ScottC wrote:
>
> I've tested some little programs and can't get scalac to inline things that
> are annotated with @inline, even with -optimize, so possibly this isn't a
> problem in reality with 1.9.1.
>
>>
>> If scalac were to do such an inline, it would be a bug if it did not
>> leave the scoping and visibility the same after the inline.
>
> Unless scalac is explicitly clearing values that its stored away using
> astore at the boundary matching the termination of the inlined function, the
> inlined version will be holding onto references longer than the non-inlined
> one. However, since I can't seem to convince scalac to do any inlining, I
> can't show you the bytecode that would demonstrate this one way or the
> other.
>

I suspect that this is related to why javac doesn't do any inlining.

>>
>>
>>
>> When the JVM inlines, the local variables of the inlined method are
>> not in scope afterward (are off the bytecode stack)
>
> Oh? That would mean that a JVM 'inline' is pushing and popping the stack,
> which is exactly the operation that inlining is intended to avoid. I suspect
> that what actually happens is that it transforms the inlined code into a
> block, which allows the re-uses of astore indexes. However, I've never taken
> a modern, optimizing JVM apart so can't say for sure.
>

I am mistaken, I should have gone back and looked instead of recalling
from memory.
The JVM however doesn't inline at the bytecode level anyway, it does
so in compiled native code where it can avoid function call overhead
but still maintain stack trace info and potentially deal with this
local variable visibility properly during GCs.

>>

Matthew Pocock 3
Joined: 2010-07-30,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: vals locking things into memory
The JVM however doesn't inline at the bytecode level anyway, it does
so in compiled native code where it can avoid function call overhead
but still maintain stack trace info and potentially deal with this
local variable visibility properly during GCs.

Good point. 
If Java doesn't optimize local variable visibility, should Scala?

Clojure does. It is a relatively minor change to scalac to achieve this and it would fix show-stopper memory issues that I've now fallen over in two different real-world applications. Scala does many things that Java does not, which contribute to it being a superior language. Let's add this one and make it just that little bit better.  
What does the JVM do with the extra instructions to remove visibility
earlier?

It is just an `astore_<?> NULL` for each Object var (not needed in all cases, if you have more clever logic).  
 Clearly, interpreted mode will be slower, but does this help
or hinder hotspot?  javac developers have probably already
experimented with this.

Well, we have a large body of bytecode generated by Clojure that does this already. Clearly they have come to the judgement that having more dependable (to the coder) and timely (at run-time) memory management is a net win here.
Over all, if you are optimizing for local var lifetimes, there are lots of opportunities for making the bytecode quite a lot smaller. Consider:
  val a = 42  val b = a * 2   println(b)
This will generate something like:
  i_const 1 #42  istore_1 #a  iload_1 #a  i_const 2 #2  imul  i_store_2 #b   <push the println and box methods onto the stack>  i_load_2 #b   <invoke box, invoke println>
If you're looking at the lifetimes of the vals, then when -optimize is thrown you can do all of this on the stack:
  <push the println and box methods onto the stack>  i_const 1 #42  i_const 2 #2  imul  <invoke box, invoke println>
You loose the ability to track the values a,b from a debugger, but you get much-reduced bytecode. There are several other trivial optimizations that can be applied - not nulling out local vars that are trampled on, using stack duplication ops instead of astore/aload/aload, and on. The book-keeping needed to spot last-use of a local var for clearing covers what you need for a range of bytecode size optimizations, so I think in practice that there will be minimal bytecode bloat in the naive case and bytecode reduction in the optimizing case. I don't know how this will interact with JVM speed, but I do know for certain that it will do wonders for ensuring that big objects are not locked into memory beyond where you want them.
Matthew
--
Dr Matthew PocockIntegrative Bioinformatics Group, School of Computing Science, Newcastle Universitymailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.commsn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozerskype: matthew.pococktel: (0191) 2566550mob: +447535664143
ScottC 2
Joined: 2011-01-30,
User offline. Last seen 42 years 45 weeks ago.
Re: vals locking things into memory

On Nov 4, 2:28 am, Matthew Pocock
wrote:
>
> Over all, if you are optimizing for local var lifetimes, there are lots of
> opportunities for making the bytecode quite a lot smaller. Consider:
>
>   val a = 42
>   val b = a * 2
>   println(b)
>
> This will generate something like:
>
>   i_const 1 #42
>   istore_1 #a
>   iload_1 #a
>   i_const 2 #2
>   imul
>   i_store_2 #b
>  
>   i_load_2 #b
>  
>
> If you're looking at the lifetimes of the vals, then when -optimize is
> thrown you can do all of this on the stack:
>
>  
>   i_const 1 #42
>   i_const 2 #2
>   imul
>  
>
> You loose the ability to track the values a,b from a debugger, but you get
> much-reduced bytecode. There are several other trivial optimizations that
> can be applied - not nulling out local vars that are trampled on, using
> stack duplication ops instead of astore/aload/aload, and on. The
> book-keeping needed to spot last-use of a local var for clearing covers
> what you need for a range of bytecode size optimizations, so I think in
> practice that there will be minimal bytecode bloat in the naive case and
> bytecode reduction in the optimizing case. I don't know how this will
> interact with JVM speed, but I do know for certain that it will do wonders
> for ensuring that big objects are not locked into memory beyond where you
> want them.

That sounds like a strong argument to me that at minimum some of this
should be done (any case of less bytecode is a good thing, other than
the debugger related issues). The example that motivated change in
Clojure is also compelling. There is some danger in cases that
increase the bytecode, but I'm unconvinced that danger is any worse
than the danger of holding on to values in the local var table.

>
> Matthew
>
> --
> Dr Matthew Pocock
> Integrative Bioinformatics Group, School of Computing Science, Newcastle
> University
> mailto: turingatemyhams...@gmail.com
> gchat: turingatemyhams...@gmail.com
> msn: matthew_poc...@yahoo.co.uk
> irc.freenode.net: drdozer
> skype: matthew.pocock
> tel: (0191) 2566550
> mob: +447535664143

vpatryshev
Joined: 2009-02-16,
User offline. Last seen 1 year 24 weeks ago.
Re: vals locking things into memory

Thanks,
-Vlad


On Thu, Nov 3, 2011 at 7:34 AM, Ittay Dror <ittay.dror@gmail.com> wrote:



Matthew Pocock wrote:
Hi,
Again I've hit an application where values are locked into memory because the're referred to by vals that are modelled as local vars in the bytecode, and the method isn't run more than once so the VM doesn't get a chance to inline that away.
    val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     val rowColNormalized = rowNormalizedT.flip.normalizeColumns

Do you mean these vals are defined inside a class?

Maybe you should try:

val rowColNormalized = {
    val relationMatrix = mkRelationMatrix     val symmetrical = relationMatrix add relationMatrix.flip     val rowNormalizedT = symmetrical.flip.normalizeColumns     rowNormalizedT.flip.normalizeColumns
}


And this is the trick that has been known in JavaScript forever.
 
or create a helper method

Ittay


I only want rowColNormalized locked into memory, as I go on to perform a bajillion operations on it. However, the other 3 matrices stick about for the entire lifetime of the program, eating valuable ram. I have had to re-write this with vars and explicit assignment to null.
Please can the compiler generate localvar=null ops at the first possible place that the vals are no longer used? It would make it immeasurably easier to write scala programs with lower memory overhead.
Thanks,
Matthew
--
Dr Matthew Pocock Integrative Bioinformatics Group, School of Computing Science, Newcastle University mailto: turingatemyhamster@gmail.com gchat: turingatemyhamster@gmail.com msn: matthew_pocock@yahoo.co.uk irc.freenode.net: drdozer skype: matthew.pocock tel: (0191) 2566550 mob: +447535664143

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland