Fixing a few bugs in the statistics printout.
[jpf-core.git] / src / main / gov / nasa / jpf / vm / GenericHeap.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18
19 package gov.nasa.jpf.vm;
20
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25
26 import gov.nasa.jpf.Config;
27 import gov.nasa.jpf.JPFException;
28 import gov.nasa.jpf.util.ArrayObjectQueue;
29 import gov.nasa.jpf.util.IntTable;
30 import gov.nasa.jpf.util.IntVector;
31 import gov.nasa.jpf.util.ObjectQueue;
32 import gov.nasa.jpf.util.Processor;
33
34 /**
35  * this is an abstract root for Heap implementations, providing a standard
36  * mark&sweep collector, change attribute management, and generic pinDownList,
37  * weakReference and internString handling
38  * 
39  * The concrete Heap implementors have to provide the ElementInfo collection
40  * and associated getters, allocators and iterators
41  */
42 public abstract class GenericHeap implements Heap, Iterable<ElementInfo> {
43   
44   static abstract class GenericHeapMemento implements Memento<Heap> {
45     // those can be simply copied
46     int attributes;
47     IntVector pinDownList;
48     Map<Integer,IntTable<String>> internStringsMap;
49     
50     protected GenericHeapMemento (GenericHeap heap){
51       // these are copy-on-first-write, so we don't have to clone
52       pinDownList = heap.pinDownList;
53       internStringsMap = heap.internStringsMap;
54       attributes = heap.attributes & ATTR_STORE_MASK;
55       
56       heap.setStored();
57     }
58     
59     @Override
60     public Heap restore (Heap inSitu) {
61       GenericHeap heap = (GenericHeap) inSitu;
62       heap.pinDownList = pinDownList;
63       heap.internStringsMap = internStringsMap;
64       heap.attributes = attributes;
65       heap.liveBitValue = false; // always start with false after a restore
66       return inSitu;
67     }
68   }
69   
70   
71   protected class ElementInfoMarker implements Processor<ElementInfo>{
72     @Override
73         public void process (ElementInfo ei) {
74       ei.markRecursive( GenericHeap.this); // this might in turn call queueMark
75     }
76   }
77   
78   protected VM vm;
79
80   // list of pinned down references (this is only efficient for a small number of objects)
81   // this is copy-on-first-write
82   protected IntVector pinDownList;
83
84   // interned Strings
85   // this is copy-on-first-write, it is created on demand upon adding the first interned string,
86   // and it includes IntTable per process.
87   protected Map<Integer,IntTable<String>> internStringsMap;
88
89   // the usual drill - the lower 2 bytes are sticky, the upper two ones 
90   // hold change status and transient (transition local) flags
91   protected int attributes;
92
93   static final int ATTR_GC            = 0x0001;
94   static final int ATTR_OUT_OF_MEMORY = 0x0002;
95   static final int ATTR_RUN_FINALIZER = 0x0004;
96
97   static final int ATTR_ELEMENTS_CHANGED  = 0x10000;
98   static final int ATTR_PINDOWN_CHANGED   = 0x20000;
99   static final int ATTR_INTERN_CHANGED    = 0x40000;
100   static final int ATTR_ATTRIBUTE_CHANGED = 0x80000;
101
102   // masks and sets
103   static final int ATTR_STORE_MASK = 0x0000ffff;
104   static final int ATTR_ANY_CHANGED = (ATTR_ELEMENTS_CHANGED | ATTR_PINDOWN_CHANGED | ATTR_INTERN_CHANGED | ATTR_ATTRIBUTE_CHANGED);
105
106
107   //--- these objects are only used during gc
108
109   // used to keep track of marked WeakRefs that might have to be updated (no need to restore, only transient use during gc)
110   protected ArrayList<ElementInfo> weakRefs;
111
112   protected ObjectQueue<ElementInfo> markQueue = new ArrayObjectQueue<ElementInfo>();
113
114   // this is set to false upon backtrack/restore
115   protected boolean liveBitValue;
116   
117   protected ElementInfoMarker elementInfoMarker = new ElementInfoMarker();
118   
119   // the number of live objects
120   // <2do> currently only defined after gc
121   protected int nLiveObjects;
122   
123   //--- constructors
124
125   public GenericHeap (Config config, KernelState ks){
126     vm = VM.getVM();
127
128     pinDownList = new IntVector(256);
129     attributes |= ATTR_PINDOWN_CHANGED; // no need to clone on next add
130
131     if (config.getBoolean("vm.finalize", true)){
132       attributes |= ATTR_RUN_FINALIZER;
133     }
134
135     if (config.getBoolean("vm.sweep",true)){
136       attributes |= ATTR_GC;
137     }
138   }
139
140
141   protected DynamicElementInfo createElementInfo (int objref, ClassInfo ci, Fields f, Monitor m, ThreadInfo ti){
142     return new DynamicElementInfo( objref,ci,f,m,ti);
143   }
144   
145   //--- pinDown handling
146   protected void addToPinDownList (int objref){
147     if ((attributes & ATTR_PINDOWN_CHANGED) == 0) {
148       pinDownList = pinDownList.clone();
149       attributes |= ATTR_PINDOWN_CHANGED;
150     }
151     pinDownList.add(objref);
152   }
153   
154   protected void removeFromPinDownList (int objref){
155     if ((attributes & ATTR_PINDOWN_CHANGED) == 0) {
156       pinDownList = pinDownList.clone();
157       attributes |= ATTR_PINDOWN_CHANGED;
158     }
159     pinDownList.removeFirst(objref);    
160   }
161
162   @Override
163   public void registerPinDown(int objref){
164     ElementInfo ei = getModifiable(objref);
165     if (ei != null) {
166       if (ei.incPinDown()){
167         addToPinDownList(objref);
168       }
169     } else {
170       throw new JPFException("pinDown reference not a live object: " + objref);
171     }
172   }
173
174   @Override
175   public void releasePinDown(int objref){
176     ElementInfo ei = getModifiable(objref);
177     if (ei != null) {
178       if (ei.decPinDown()){
179         removeFromPinDownList(objref);
180       }
181     } else {
182       throw new JPFException("pinDown reference not a live object: " + objref);
183     }
184   }  
185   void markPinDownList (){
186     if (pinDownList != null){
187       int len = pinDownList.size();
188       for (int i=0; i<len; i++){
189         int objref = pinDownList.get(i);
190         queueMark(objref);
191       }
192     }
193   }
194   
195   //--- weak reference handling
196   
197   @Override
198   public void registerWeakReference (ElementInfo ei) {
199     if (weakRefs == null) {
200       weakRefs = new ArrayList<ElementInfo>();
201     }
202
203     weakRefs.add(ei);
204   }
205   
206   /**
207    * reset all weak references that now point to collected objects to 'null'
208    * NOTE: this implementation requires our own Reference/WeakReference implementation, to
209    * make sure the 'ref' field is the first one
210    */
211   protected void cleanupWeakRefs () {
212     if (weakRefs != null) {
213       for (ElementInfo ei : weakRefs) {
214         Fields f = ei.getFields();
215         int    ref = f.getIntValue(0); // watch out, the 0 only works with our own WeakReference impl
216         if (ref != MJIEnv.NULL) {
217           ElementInfo refEi = get(ref);
218           if ((refEi == null) || (refEi.isNull())) {
219             ei = ei.getModifiableInstance();
220             // we need to make sure the Fields are properly state managed
221             ei.setReferenceField(ei.getFieldInfo(0), MJIEnv.NULL);
222           }
223         }
224       }
225
226       weakRefs = null;
227     }
228   }
229   
230   // NOTE - this is where to assert if this index isn't occupied yet, since only concrete classes know
231   // if there can be collisions, and how elements are stored
232   
233   protected abstract AllocationContext getSUTAllocationContext (ClassInfo ci, ThreadInfo ti);
234   protected abstract AllocationContext getSystemAllocationContext (ClassInfo ci, ThreadInfo ti, int anchor);
235   
236   /**
237    * this is called for newXX(..) allocations that are SUT thread specific, i.e. in response to
238    * a explicit NEW or xNEWARRAY instruction that should take the allocating thread into account 
239    */
240   protected abstract int getNewElementInfoIndex (AllocationContext ctx);
241   
242   //--- allocators
243     
244   protected ElementInfo createObject (ClassInfo ci, ThreadInfo ti, int objref) {
245     // create the thing itself
246     Fields f = ci.createInstanceFields();
247     Monitor m = new Monitor();
248     ElementInfo ei = createElementInfo( objref, ci, f, m, ti);
249     
250     set(objref, ei);
251
252     attributes |= ATTR_ELEMENTS_CHANGED;
253
254     // and do the default (const) field initialization
255     ci.initializeInstanceData(ei, ti);
256
257     vm.notifyObjectCreated(ti, ei);
258     
259     // note that we don't return -1 if 'outOfMemory' (which is handled in
260     // the NEWxx bytecode) because our allocs are used from within the
261     // exception handling of the resulting OutOfMemoryError (and we would
262     // have to override it, since the VM should guarantee proper exceptions)
263     
264     return ei;    
265   }
266     
267   @Override
268   public ElementInfo newObject(ClassInfo ci, ThreadInfo ti) {
269     AllocationContext ctx = getSUTAllocationContext( ci, ti);
270     int index = getNewElementInfoIndex( ctx);
271     ElementInfo ei = createObject( ci, ti, index);
272
273     return ei;
274   }
275
276   @Override
277   public ElementInfo newSystemObject (ClassInfo ci, ThreadInfo ti, int anchor) {
278     AllocationContext ctx = getSystemAllocationContext( ci, ti, anchor);
279     int index = getNewElementInfoIndex( ctx);
280     ElementInfo ei = createObject( ci, ti, index);
281     return ei;
282   }
283   
284   protected ElementInfo createArray (String elementType, int nElements, ClassInfo ci, ThreadInfo ti, int objref) {
285
286     Fields f = ci.createArrayFields(ci.getName(), nElements, Types.getTypeSize(elementType), Types.isReference(elementType));
287     Monitor m = new Monitor();
288     DynamicElementInfo ei = createElementInfo( objref, ci, f, m, ti);
289
290     set(objref, ei);
291
292     attributes |= ATTR_ELEMENTS_CHANGED;
293
294     vm.notifyObjectCreated(ti, ei);
295
296     return ei;
297   }
298   
299   protected ClassInfo getArrayClassInfo (ThreadInfo ti, String elementType) {
300     String type = "[" + elementType;
301     SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo();
302     ClassInfo ciArray = sysCl.getResolvedClassInfo(type);
303
304     if (!ciArray.isInitialized()) {
305       // we do this explicitly here since there are no clinits for array classes
306       ciArray.registerClass(ti);
307       ciArray.setInitialized();
308     }
309
310     return ciArray;
311   }
312   
313   @Override
314   public ElementInfo newArray(String elementType, int nElements, ThreadInfo ti) {
315     // see newObject for OOM simulation
316     ClassInfo ci = getArrayClassInfo(ti, elementType);
317     AllocationContext ctx = getSUTAllocationContext( ci, ti);
318     
319     int index = getNewElementInfoIndex( ctx);
320     ElementInfo ei = createArray( elementType, nElements, ci, ti, index);
321
322     return ei;
323   }
324
325   @Override
326   public ElementInfo newSystemArray(String elementType, int nElements, ThreadInfo ti, int anchor) {
327     // see newObject for OOM simulation
328     ClassInfo ci = getArrayClassInfo(ti, elementType);
329     AllocationContext ctx = getSystemAllocationContext( ci, ti, anchor);
330     
331     int index = getNewElementInfoIndex( ctx);
332     ElementInfo ei = createArray( elementType, nElements, ci, ti, index);
333
334     return ei;
335   }
336
337   
338   
339   protected ElementInfo initializeStringObject( String str, int index, int vref) {
340     ElementInfo ei = getModifiable(index);
341     ei.setReferenceField("value", vref);
342
343     ElementInfo eVal = getModifiable(vref);
344     CharArrayFields cf = (CharArrayFields)eVal.getFields();
345     cf.setCharValues(str.toCharArray());
346     
347     return ei;
348   }
349   
350   protected ElementInfo newString (ClassInfo ciString, ClassInfo ciChars, String str, ThreadInfo ti, AllocationContext ctx) {
351     
352     //--- the string object itself
353     int sRef = getNewElementInfoIndex( ctx);
354     createObject( ciString, ti, sRef);
355     
356     //--- its char[] array
357     ctx = ctx.extend(ciChars, sRef);
358     int vRef = getNewElementInfoIndex( ctx);
359     createArray( "C", str.length(), ciChars, ti, vRef);
360     
361     ElementInfo ei = initializeStringObject(str, sRef, vRef);      
362     return ei;
363   }
364
365   @Override
366   public ElementInfo newString(String str, ThreadInfo ti){
367     if (str != null) {
368       SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo();
369       ClassInfo ciString = sysCl.getStringClassInfo();
370       ClassInfo ciChars = sysCl.getCharArrayClassInfo();
371       
372       AllocationContext ctx = getSUTAllocationContext( ciString, ti);
373       return newString( ciString, ciChars, str, ti, ctx);
374       
375     } else {
376       return null;
377     }
378   }
379   
380   @Override
381   public ElementInfo newSystemString (String str, ThreadInfo ti, int anchor) {
382     if (str != null) {
383       SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo();
384       ClassInfo ciString = sysCl.getStringClassInfo();
385       ClassInfo ciChars = sysCl.getCharArrayClassInfo();
386       
387       AllocationContext ctx = getSystemAllocationContext( ciString, ti, anchor);
388       return newString( ciString, ciChars, str, ti, ctx);
389       
390     } else {
391       return null;
392     }    
393   }
394
395   @Override
396   public ElementInfo newInternString (String str, ThreadInfo ti) {
397     if(internStringsMap==null) {
398       internStringsMap = vm.getInitialInternStringsMap();
399     }
400     
401     int prcId = ti.getApplicationContext().getId();
402     IntTable.Entry<String> e = internStringsMap.get(prcId).get(str);
403     
404     if (e == null){
405       if (str != null) {
406         ElementInfo ei = newString( str, ti);
407         int index = ei.getObjectRef();
408         
409         // new interned Strings are always pinned down
410         ei.incPinDown();
411         addToPinDownList(index);
412         addToInternStrings(str, index, prcId);
413
414         return ei;
415       
416       } else {
417         return null;
418       }
419
420     } else {
421       return get(e.val);
422     }
423   }
424
425   protected void addToInternStrings (String str, int objref, int prcId) {
426     if ((attributes & ATTR_INTERN_CHANGED) == 0){
427       // shallow copy all interned strings tables
428       internStringsMap = new HashMap<Integer,IntTable<String>>(internStringsMap);
429       
430       // only clone the interned strings table of the current process
431       internStringsMap.put(prcId, internStringsMap.get(prcId).clone());
432       
433       // just cloned, no need to clone on the next add
434       attributes |= ATTR_INTERN_CHANGED;
435     }
436     internStringsMap.get(prcId).add(str, objref);
437   }
438   
439   
440   @Override
441   public ElementInfo newSystemThrowable (ClassInfo ciThrowable, String details, int[] stackSnapshot, int causeRef,
442                                  ThreadInfo ti, int anchor) {
443     SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo(); 
444     ClassInfo ciString = sysCl.getStringClassInfo();
445     ClassInfo ciChars = sysCl.getCharArrayClassInfo();
446     
447     //--- the Throwable object itself
448     AllocationContext ctx = getSystemAllocationContext( ciThrowable, ti, anchor);
449     int xRef = getNewElementInfoIndex( ctx);
450     ElementInfo eiThrowable = createObject( ciThrowable, ti, xRef);
451     
452     //--- the detailMsg field
453     if (details != null) {
454       AllocationContext ctxString = ctx.extend( ciString, xRef);
455       ElementInfo eiMsg = newString( ciString, ciChars, details, ti, ctxString);
456       eiThrowable.setReferenceField("detailMessage", eiMsg.getObjectRef());
457     }
458
459     //--- the stack snapshot field
460     ClassInfo ciSnap = getArrayClassInfo(ti, "I");
461     AllocationContext ctxSnap = ctx.extend(ciSnap, xRef);
462     int snapRef = getNewElementInfoIndex( ctxSnap);
463     ElementInfo eiSnap = createArray( "I", stackSnapshot.length, ciSnap, ti, snapRef);
464     int[] snap = eiSnap.asIntArray();
465     System.arraycopy( stackSnapshot, 0, snap, 0, stackSnapshot.length);
466     eiThrowable.setReferenceField("snapshot", snapRef);
467
468     //--- the cause field
469     eiThrowable.setReferenceField("cause", (causeRef != MJIEnv.NULL)? causeRef : xRef);
470
471     return eiThrowable;
472   }
473
474   
475   //--- abstract accessors
476
477   /*
478    * these methods abstract away the container type used in GenericHeap subclasses
479    */
480   
481   /**
482    * internal setter used during allocation
483    * @param index
484    * @param ei
485    */  
486   protected abstract void set (int index, ElementInfo ei);
487
488   /**
489    * public getter to access but not change ElementInfos
490    */
491   @Override
492   public abstract ElementInfo get (int ref);
493   
494   
495   /**
496    * public getter to access modifiable ElementInfos;
497    */
498   @Override
499   public abstract ElementInfo getModifiable (int ref);
500   
501   
502   /**
503    * internal remover used by generic sweep
504    */
505   protected abstract void remove (int ref);
506
507   
508   //--- iterators
509   
510   /**
511    * return Iterator for all non-null ElementInfo entries
512    */
513   @Override
514   public abstract Iterator<ElementInfo> iterator();
515   
516   @Override
517   public abstract Iterable<ElementInfo> liveObjects();
518   
519   
520   //--- garbage collection
521   
522   public boolean isGcEnabled (){
523     return (attributes & ATTR_GC) != 0;
524   }
525
526   public void setGcEnabled (boolean doGC) {
527     if (doGC != isGcEnabled()) {
528       if (doGC) {
529         attributes |= ATTR_GC;
530       } else {
531         attributes &= ~ATTR_GC;
532       }
533       attributes |= ATTR_ATTRIBUTE_CHANGED;
534     }
535   }
536   
537   @Override
538   public void unmarkAll(){
539     for (ElementInfo ei : liveObjects()){
540       ei.setUnmarked();
541     }
542   }
543   
544   /**
545    * add a non-null, not yet marked reference to the markQueue
546    *  
547    * called from ElementInfo.markRecursive(). We don't want to expose the
548    * markQueue since a copying collector might not have it
549    */
550   @Override
551   public void queueMark (int objref){
552     if (objref == MJIEnv.NULL) {
553       return;
554     }
555
556     ElementInfo ei = get(objref);
557     if (!ei.isMarked()){ // only add objects once
558       ei.setMarked();
559       markQueue.add(ei);
560     }
561   }
562   
563   /**
564    * called during non-recursive phase1 marking of all objects reachable
565    * from static fields
566    * @aspects: gc
567    */
568   @Override
569   public void markStaticRoot (int objref) {
570     if (objref != MJIEnv.NULL) {
571       queueMark(objref);
572     }
573   }
574
575   /**
576    * called during non-recursive phase1 marking of all objects reachable
577    * from Thread roots
578    * @aspects: gc
579    */
580   @Override
581   public void markThreadRoot (int objref, int tid) {
582     if (objref != MJIEnv.NULL) {
583       queueMark(objref);
584     }
585   }
586   
587   /**
588    * this implementation uses a generic ElementInfo iterator, it can be replaced
589    * with a more efficient container specific version
590    */
591   protected void sweep () {
592     ThreadInfo ti = vm.getCurrentThread();
593     int tid = ti.getId();
594     boolean isThreadTermination = ti.isTerminated();
595     int n = 0;
596     
597     if(vm.finalizersEnabled()) {
598       markFinalizableObjects();
599     }
600     
601     // now go over all objects, purge the ones that are not live and reset attrs for rest
602     for (ElementInfo ei : this){
603       
604       if (ei.isMarked()){ // live object, prepare for next transition & gc cycle
605         ei.setUnmarked();
606         ei.setAlive(liveBitValue);
607         
608         ei.cleanUp(this, isThreadTermination, tid);
609         n++;
610         
611       } else {
612         ei.processReleaseActions();
613         
614         vm.notifyObjectReleased(ti, ei);
615         remove(ei.getObjectRef());
616       }
617     }
618     
619     nLiveObjects = n;
620   }
621   
622   protected void markFinalizableObjects () {
623     FinalizerThreadInfo tiFinalizer = vm.getFinalizerThread();
624     
625     if (tiFinalizer != null){
626       for (ElementInfo ei : this) {
627         if (!ei.isMarked() && ei.hasFinalizer() && !ei.isFinalized()) {
628           ei = tiFinalizer.getFinalizerQueuedInstance(ei);
629           ei.setMarked(); // make sure it's not collected before the finalizerQueue has been processed
630           ei.markRecursive(this);
631         }
632       }
633     }
634   }
635   
636   protected void mark () {
637     markQueue.clear();
638     
639     //--- mark everything in our root set
640     markPinDownList();
641     vm.getThreadList().markRoots(this);      // mark thread stacks
642     vm.getClassLoaderList().markRoots(this); // mark all static references
643
644     //--- trace all entries - this gets recursive
645     markQueue.process(elementInfoMarker);    
646   }
647   
648   @Override
649   public void gc() {
650     vm.notifyGCBegin();
651
652     weakRefs = null;
653     liveBitValue = !liveBitValue;
654
655     mark();
656     
657     // at this point all live objects are marked
658     sweep();
659
660     cleanupWeakRefs(); // for potential nullification
661
662     vm.processPostGcActions();
663     vm.notifyGCEnd();
664   }
665
666   /**
667    * clean up reference values that are stored outside of reference fields 
668    * called from KernelState to process live ElementInfos after GC has finished
669    * and only live objects remain in the heap.
670    * 
671    * <2do> full heap enumeration is BAD - check if this can be moved into the sweep loop
672    */
673   @Override
674   public void cleanUpDanglingReferences() {
675     ThreadInfo ti = ThreadInfo.getCurrentThread();
676     int tid = ti.getId();
677     boolean isThreadTermination = ti.isTerminated();
678     
679     for (ElementInfo e : this) {
680       if (e != null) {
681         e.cleanUp(this, isThreadTermination, tid);
682       }
683     }
684   }
685   
686   /**
687    * check if object is alive. This is here and not in ElementInfo
688    * because we might own the liveness bit. In fact, the generic
689    * implementation uses bit-toggle to avoid iteration over all live
690    * objects at the end of GC
691    */
692   @Override
693   public boolean isAlive (ElementInfo ei){
694     return (ei == null || ei.isMarkedOrAlive(liveBitValue));
695   }
696   
697   //--- state management
698   
699   // since we can't provide generic implementations, we force concrete subclasses to
700   // handle volatile information
701   
702   @Override
703   public abstract void resetVolatiles();
704
705   @Override
706   public abstract void restoreVolatiles();
707   
708   @Override
709   public boolean hasChanged() {
710     return (attributes & ATTR_ANY_CHANGED) != 0;
711   }
712   
713   @Override
714   public void markChanged(int objref) {
715     attributes |= ATTR_ELEMENTS_CHANGED;
716   }
717
718   public void setStored() {
719     attributes &= ~ATTR_ANY_CHANGED;
720   }
721   
722   @Override
723   public abstract Memento<Heap> getMemento(MementoFactory factory);
724
725   @Override
726   public abstract Memento<Heap> getMemento();
727
728   
729   //--- out of memory simulation
730   
731   @Override
732   public boolean isOutOfMemory() {
733     return (attributes & ATTR_OUT_OF_MEMORY) != 0;
734   }
735
736   @Override
737   public void setOutOfMemory(boolean isOutOfMemory) {
738     if (isOutOfMemory != isOutOfMemory()) {
739       if (isOutOfMemory) {
740         attributes |= ATTR_OUT_OF_MEMORY;
741       } else {
742         attributes &= ~ATTR_OUT_OF_MEMORY;
743       }
744       attributes |= ATTR_ATTRIBUTE_CHANGED;
745     }
746   }
747
748
749   
750   //--- debugging
751
752   @Override
753   public void checkConsistency(boolean isStateStore) {
754     for (ElementInfo ei : this){
755       ei.checkConsistency();
756     }
757   }
758 }