This discussion is archived
11 Replies Latest reply: Mar 30, 2009 2:46 PM by 807557 RSS

ScopedMemory backing store overflow

807557 Newbie
Currently Being Moderated
Hi,



my latest problem is the following:



First ScopeMemory seems to work fine. When the reference count of a ScopedMemory object falls to zero, the memory the object took seems to be freed again without any occurrence of the garbage collector.

But what happens in the backing store of ScopedMemory?? To me it seems that every new ScopedMemory area that is allocated by an application, is taking new space in the backing store. The ScopedMemory areas that should have been freed, seem to last in the backing store of ScopedMemory.



I noticed this problem as follows:

During initialization of the VM I assigned about 1 Gigabyte Memory to ScopedMemory:
... -XX:ScopedSize=1000000000... 
.

In my application the main thread is running in ImmortalMemory allocating space in the ScopedMemory. The allocations happen a few times and every time the reference count of a ScopeMemory object falls to zero - and even the block where the ScopedMemory object is declared is left (so every ScopedMemory is really left and should free all its memory) - the ScopedMemory object seems to free its memory.

I know that since this ScopedMemory objects are declared in ImmortalMemory they are never garbage collected - but this just takes some bytes every time, furthermore ImmortalMemory is big enough and is not the problem.

But when the sum of all allocated ScopedMemory areas reaches about the dedicated 1 Gigabyte, the ScopedMemory overflow exception appears. Although all the memory allocated in the ScopedMemory areas should have been freed, an overflow happens in the backing store of the ScopedMemory. The biggest allocation size in my application at a moment is about 200 MB. So at one moment there is never allocated more ScopedMemory than about 200 MB. How can i avoid this overflow??



My last idea is, that the unused ScopedMemory references that remain in the ImmortalMemory - created by the main thread - may inhibit the allocated Objects inside the ScopedMemories to be freed. If this is the case, a solution would be, to run my main thread in another MemoryArea.
  • 1. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Gordon_Realtime wrote:
    I know that since this ScopedMemory objects are declared in ImmortalMemory they are never garbage collected - but this just takes some bytes every time, furthermore ImmortalMemory is big enough and is not the problem.
    On the contrary it is precisely the problem. The backing store for a ScopedMemory object is only reclaimable when the ScopedMemory instance itself could be finalized (as per the RTSJ). If your scope instances are in immortal then they are never finalizable and the backing store is never reclaimable.

    Either don't allocate your short-lived scopes in immortal, or else recycle them via an "object pooling" mechanism.

    David Holmes
  • 2. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Either don't allocate your short-lived scopes in immortal, or else recycle them via an "object pooling" mechanism.
    Well still there are two things that bother me:

    1. If the short-lived allocations of new ScopedMemory will take place in another ScopedMemory (instead of ImmortalMemory), then this problem will stay unsolved: All the references to the short-lived ScopedMemory objects will stay, as long as the ScopedMemory they are created in, is living. So again all the short-lived Scoped objects will not be deleted until I leave the parent ScopedMemory!?

    2. If I allocate the short-lived ScopedMemory objects from Heap, the Garbage Collector will be unintentionally triggered.

    3. With object pooling I had the following problem:
    ...
    ScopedMemory scope = new LTMemory(someSize);
    ...
    while(someCondition) {
      ...
      // --> XX
      scope.enter(someRunnable);
      ...
    }
    From the second run in this while-loop on, the scope object already has the memory allocated from the run before. Although the reference counter of the object scope falls to zero after finishing the scope.enter() method, the memory allocated inside the scope.enter() method remains. When I'm allocating in the second run of this while-loop the same amount of memory inside this enter() method, the consumed memory of the scope object doesn't sum up, but stays the same.
    The only possibility I found to get scope.memoryConsumed() to zero, is the following:
    scope = new LTMemory(someSize);
    But this is not what I understand by object pooling. If I would pool the scope object, I would get an object, which already has some amount of memory consumed. Maybe there is some workaround for this?


    And Guys: Thanks for your help! It really boosts the time I'm spending with fault analysis. Furthermore it helps germany not to fall behind with RTSJ :). Thanks a lot!
  • 3. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Searching for the best workaround.
  • 4. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Gordon_Realtime wrote:
    1. If the short-lived allocations of new ScopedMemory will take place in another ScopedMemory (instead of ImmortalMemory), then this problem will stay unsolved: All the references to the short-lived ScopedMemory objects will stay, as long as the ScopedMemory they are created in, is living. So again all the short-lived Scoped objects will not be deleted until I leave the parent ScopedMemory!?
    No. A scope is cleared when it is no longer in-use (reference count goes to zero). When that happens all finalizable objects in the scope are finalized. For a ScopedMemory object allocated within a ScopedMemory that is the point where it's backing store can be freed. If that does not happen then there is a bug - though note that this reclaimation is not instantaneous in JRTS
    2. If I allocate the short-lived ScopedMemory objects from Heap, the Garbage Collector will be unintentionally triggered.
    If you allocate enough of them yes. Whether this is an issue depends on many, many things. The RTGC will run in preference to non-critical threads but won't disrupt critical threads. Of course a NHRT can't use a heap-allocated ScopedMemory object.
    3. With object pooling I had the following problem:
    ...
    ScopedMemory scope = new LTMemory(someSize);
    ...
    while(someCondition) {
    ...
    // --> XX
    scope.enter(someRunnable);
    ...
    }
    From the second run in this while-loop on, the scope object already has the memory allocated from the run before. Although the reference counter of the object scope falls to zero after finishing the scope.enter() method, the memory allocated inside the scope.enter() method remains. When I'm allocating in the second run of this while-loop the same amount of memory inside this enter() method, the consumed memory of the scope object doesn't sum up, but stays the same.
    The only possibility I found to get scope.memoryConsumed() to zero, is the following:
    scope = new LTMemory(someSize);
    I do not understand this. Each time you leave scope.enter(runnable) if the scope is no longer in use then the scope's backing store will be cleared so that the scope has its full allocation available for the next enter. Of course the backing store itself will not be freed, but then you don't need it to be. The change in the two code fragments is insignificant from a scoped-memory usage perspective.

    Do you have a simple test case to demonstrate the problem?

    David Holmes
  • 5. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Here's a litle test case, which demonstrates, that scope memory consumed inside an enter() method remains consumed.

    Code:
    ...
    for ( someChangingAllocationSize ) {
                ScopedMemory scope = new LTMemory(
                        someChangingAllocationSize );
    
                /*
                 * Allocate the current allocation size (i) noLoops times.
                 */
                for (int j=0; j<noLoops; j++) {
                    // DEBUG
                    System.out.println(">> Scope consumed before entering: " +
                            scope.memoryConsumed());
    
                    scope.enter(runner);
    
                    // DEBUG
                    System.out.println(">> Scope consumed after entering: " +
                            scope.memoryConsumed());
                    System.out.println(">>--------------------------------");
    
                    ...
                }
    }
    Printed Results:
    Scope consumed before entering: 0
    Scope consumed after entering: 32
    --------------------------------
    Scope consumed before entering: 32
    Scope consumed after entering: 32
    --------------------------------
    Scope consumed before entering: 32
    Scope consumed after entering: 32
    --------------------------------
    Scope consumed before entering: 0
    Scope consumed after entering: 64
    --------------------------------
    Scope consumed before entering: 64
    Scope consumed after entering: 64
    --------------------------------
    Scope consumed before entering: 64
    Scope consumed after entering: 64
    --------------------------------
    Scope consumed before entering: 0
    Scope consumed after entering: 128
    --------------------------------
    Scope consumed before entering: 128
    Scope consumed after entering: 128
    --------------------------------
    Scope consumed before entering: 128
    Scope consumed after entering: 128
    --------------------------------
    Scope consumed before entering: 0
    Scope consumed after entering: 256
    --------------------------------
    Scope consumed before entering: 256
    Scope consumed after entering: 256
    --------------------------------
    Scope consumed before entering: 256
    Scope consumed after entering: 256
    --------------------------------
    Scope consumed before entering: 0
    Scope consumed after entering: 512
    --------------------------------
    Scope consumed before entering: 512
    Scope consumed after entering: 512
    --------------------------------
    Scope consumed before entering: 512
    Scope consumed after entering: 512
    Here one can see, that the memory consumed inside the scope.enter() method in the first run, is in the second run already consumed before entering scope.enter(). After entering scope.enter() again and consuming the same amount of memory again, the consumed memory of scope doesn't sum up. The scope.memoryConsumed() seems to stay at the highes amount of memory that was allocated inside the scope object.

    Only when allocating a new scope object (outer loop) the consumed memory is reset to zero.

    With this behavior object pooling would be a little bit strange - but one needs getting used to.

    Dominik
  • 6. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    <sigh> you've found a bug. Looks like we leak the first allocation in a scope.

    Thanks for the report. I'll file a bug.

    Edited to add: I still don't understand the change that you made that apparently made this work ok. Can you clarify which version of the code showed the scope being reclaimed correctly. Thanks.

    David Holmes

    Edited by: davidholmes on Mar 31, 2009 6:52 PM
  • 7. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Just realized the bug is not what it seems. Logically a scope is cleared when the reference count goes to zero - and that's when finalizers are executed. But in practice it is easier to clear the scope upon the next entry. The problem we have here is that the memory counters should be being reset when the reference count goes to zero, but they aren't being reset until the scope is actually cleared on the next entry.

    Try running this test:
    import javax.realtime.*;
    
    public class ConsumedTest3 {
      public static void main(String[] args) {
        Thread t = new RealtimeThread() {
           ScopedMemory mem = new VTMemory(1024,1024);
           long size = -1;
           long consumed = -1;
           long remaining = -1;
    
           public void run() {
              for( int i = 0; i < 8; i++) {
                info();
                mem.enter(runner);
              }
           }
    
           Runnable runner = new Runnable() {
             public void run() {
               size = mem.size();
               consumed = mem.memoryConsumed();
               remaining = mem.memoryRemaining();
               Object t = new Object[100];
             }
           };
    
           void info() {
               System.out.printf("size: %d, consumed: %d, remaining: %d%n",
               size, consumed, remaining);
           }
        };
        t.start();
      }
    }
  • 8. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Yes this really works fine. All counters are set to zero when entering the scopeMemory again.

    So the secret is that the memory counters of a ScopedMemory object (like memoryRemains, or memoryConsumed) are set to zero, when the scoped object is entered. After leaving the object the counters are still set to their last value, until the scoped object is entered again.
  • 9. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Gordon_Realtime wrote:
    So the secret is that the memory counters of a ScopedMemory object (like memoryRemains, or memoryConsumed) are set to zero, when the scoped object is entered. After leaving the object the counters are still set to their last value, until the scoped object is entered again.
    Yes, but that behaviour is a bug in JRTS ... and I already have a fix for JRTS 2.2

    David
  • 10. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    The backing store for a ScopedMemory object is only reclaimable when the ScopedMemory instance itself could be finalized (as per the RTSJ). If your scope instances are in immortal then they are never finalizable and the backing store is never reclaimable.
    So in this case:
    ...
    // in ImmortalMemory
    ...
    LTMemory scope;
    scope = new LTMemory(20000); // object A
    scope = new LTMemory(3000); // object B
    ...
    the object A remains in the backing store of ScopedMemory, although the variable scope is set to a new value.

    Is this behavior out of tune with the RTSJ? To me it seems, that the programmer can check via scope.getReferenceCout() if any reference to a scope object exists. And if this is not the case, the scope object is finalized. But in the upper example the programmer has no chance to check during runtime, if the object A exists or not. The reference counter is zero, too. That's why one should think, that the object is finalized.

    So is this behavior a bug or is it in the responsibility of the programmer to check for such missbehavior?

    Thanks,
    Gordon
  • 11. Re: ScopedMemory backing store overflow
    807557 Newbie
    Currently Being Moderated
    Gordon_Realtime wrote:
    So in this case:
    ...
    // in ImmortalMemory
    ...
    LTMemory scope;
    scope = new LTMemory(20000); // object A
    scope = new LTMemory(3000); // object B
    ...
    the object A remains in the backing store of ScopedMemory, although the variable scope is set to a new value.

    ...
    So is this behavior a bug or is it in the responsibility of the programmer to check for such missbehavior?
    This is a consequence of using immortal memory.

    The backing store for a scope is cleared when the scope is no longer in-use: i.e. the reference count goes to zero.
    The backing store for a scope is freed (returned to the internal VM pool) when the scope object itself is finalizable.
    No object allocated in immortal is ever finalizable because immortal is not GC'ed and no reachability of immortal objects is ever carried out.

    David