001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.mmtk.policy;
014    
015    import org.mmtk.plan.Plan;
016    import org.mmtk.plan.TransitiveClosure;
017    import org.mmtk.utility.heap.Map;
018    import org.mmtk.utility.heap.Mmapper;
019    import org.mmtk.utility.heap.PageResource;
020    import org.mmtk.utility.heap.SpaceDescriptor;
021    import org.mmtk.utility.heap.VMRequest;
022    import org.mmtk.utility.options.Options;
023    import org.mmtk.utility.Log;
024    import org.mmtk.utility.Constants;
025    
026    import org.mmtk.vm.VM;
027    
028    import org.vmmagic.pragma.*;
029    import org.vmmagic.unboxed.*;
030    
031    /**
032     * This class defines and manages spaces.  Each policy is an instance
033     * of a space.  A space is a region of virtual memory (contiguous or
034     * discontigous) which is subject to the same memory management
035     * regime.  Multiple spaces (instances of this class or its
036     * descendants) may have the same policy (eg there could be numerous
037     * instances of CopySpace, each with different roles). Spaces are
038     * defined in terms of a unique region of virtual memory, so no two
039     * space instances ever share any virtual memory.<p>
040     *
041     * In addition to tracking virtual memory use and the mapping to
042     * policy, spaces also manage memory consumption (<i>used</i> virtual
043     * memory).<p>
044     *
045     */
046    @Uninterruptible
047    public abstract class Space implements Constants {
048    
049      /****************************************************************************
050       *
051       * Class variables
052       */
053    
054      /**
055       *
056       */
057      private static boolean DEBUG = false;
058    
059      // the following is somewhat arbitrary for the 64 bit system at this stage
060      public static final int LOG_ADDRESS_SPACE = (BYTES_IN_ADDRESS == 4) ? 32 : 40;
061      public static final int LOG_BYTES_IN_CHUNK = 22;
062      public static final int BYTES_IN_CHUNK = 1 << LOG_BYTES_IN_CHUNK;
063      public static final int PAGES_IN_CHUNK = 1 << (LOG_BYTES_IN_CHUNK - LOG_BYTES_IN_PAGE);
064      private static final int LOG_MAX_CHUNKS = LOG_ADDRESS_SPACE - LOG_BYTES_IN_CHUNK;
065      public static final int MAX_CHUNKS = 1 << LOG_MAX_CHUNKS;
066      public static final int MAX_SPACES = 20; // quite arbitrary
067    
068      public static final Address HEAP_START = chunkAlign(VM.HEAP_START, true);
069      public static final Address AVAILABLE_START = chunkAlign(VM.AVAILABLE_START, false);
070      public static final Address AVAILABLE_END = chunkAlign(VM.AVAILABLE_END, true);
071      public static final Extent AVAILABLE_BYTES = AVAILABLE_END.toWord().minus(AVAILABLE_START.toWord()).toExtent();
072      public static final int AVAILABLE_PAGES = AVAILABLE_BYTES.toWord().rshl(LOG_BYTES_IN_PAGE).toInt();
073      public static final Address HEAP_END = chunkAlign(VM.HEAP_END, false);
074    
075      private static final boolean FORCE_SLOW_MAP_LOOKUP = false;
076    
077      private static final int PAGES = 0;
078      private static final int MB = 1;
079      private static final int PAGES_MB = 2;
080      private static final int MB_PAGES = 3;
081    
082      private static int spaceCount = 0;
083      private static Space[] spaces = new Space[MAX_SPACES];
084      private static Address heapCursor = HEAP_START;
085      private static Address heapLimit = HEAP_END;
086    
087      /****************************************************************************
088       *
089       * Instance variables
090       */
091    
092      /**
093       *
094       */
095      private final String name;
096      private final int nameLength;
097      protected final int descriptor;
098      private final int index;
099      private final VMRequest vmRequest;
100    
101      protected final boolean immortal;
102      protected final boolean movable;
103      protected final boolean contiguous;
104      protected final boolean zeroed;
105    
106      protected PageResource pr;
107      protected final Address start;
108      protected final Extent extent;
109      protected Address headDiscontiguousRegion;
110    
111      /****************************************************************************
112       *
113       * Initialization
114       */
115    
116      {
117        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(PAGES_IN_CHUNK > 1);
118      }
119    
120      /**
121       * This is the base constructor for <i>all</i> spaces.<p>
122       *
123       * @param name The name of this space (used when printing error messages etc)
124       * @param movable Are objects in this space movable?
125       * @param immortal Are objects in this space immortal (uncollected)?
126       * @param zeroed if it is {@code true}, allocated memory is zeroed.
127       * @param vmRequest An object describing the virtual memory requested.
128       */
129      protected Space(String name, boolean movable, boolean immortal, boolean zeroed, VMRequest vmRequest) {
130        this.name = name;
131        this.nameLength = name.length();  // necessary to avoid calling length() in uninterruptible code
132        this.movable = movable;
133        this.immortal = immortal;
134        this.zeroed = zeroed;
135        this.vmRequest = vmRequest;
136        this.index = spaceCount++;
137        spaces[index] = this;
138    
139        if (vmRequest.type == VMRequest.REQUEST_DISCONTIGUOUS) {
140          this.contiguous = false;
141          this.descriptor = SpaceDescriptor.createDescriptor();
142          this.start = Address.zero();
143          this.extent = Extent.zero();
144          this.headDiscontiguousRegion = Address.zero();
145          VM.memory.setHeapRange(index, HEAP_START, HEAP_END); // this should really be refined!  Once we have a code space, we can be a lot more specific about what is a valid code heap area
146          return;
147        }
148    
149        Address start;
150        Extent extent;
151    
152        if (vmRequest.type == VMRequest.REQUEST_FRACTION) {
153          extent = getFracAvailable(vmRequest.frac);
154        } else {
155          extent = vmRequest.extent;
156        }
157    
158        if (extent.NE(chunkAlign(extent, false))) {
159          VM.assertions.fail(name + " requested non-aligned extent: " + extent.toLong() + " bytes");
160        }
161    
162        if (vmRequest.type == VMRequest.REQUEST_FIXED) {
163          start = vmRequest.start;
164          if (start.NE(chunkAlign(start, false))) {
165            VM.assertions.fail(name + " starting on non-aligned boundary: " + start.toLong() + " bytes");
166          }
167        } else if (vmRequest.top) {
168          heapLimit = heapLimit.minus(extent);
169          start = heapLimit;
170        } else {
171          start = heapCursor;
172          heapCursor = heapCursor.plus(extent);
173        }
174    
175        if (heapCursor.GT(heapLimit)) {
176          Log.write("Out of virtual address space allocating \"");
177          Log.write(name); Log.write("\" at ");
178          Log.write(heapCursor.minus(extent)); Log.write(" (");
179          Log.write(heapCursor); Log.write(" > ");
180          Log.write(heapLimit); Log.writeln(")");
181          VM.assertions.fail("exiting");
182        }
183    
184        this.contiguous = true;
185        this.start = start;
186        this.extent = extent;
187        this.descriptor = SpaceDescriptor.createDescriptor(start, start.plus(extent));
188    
189        VM.memory.setHeapRange(index, start, start.plus(extent));
190        Map.insert(start, extent, descriptor, this);
191    
192        if (DEBUG) {
193          Log.write(name); Log.write(" ");
194          Log.write(start); Log.write(" ");
195          Log.write(start.plus(extent)); Log.write(" ");
196          Log.writeln(extent.toWord());
197        }
198      }
199    
200      /****************************************************************************
201       *
202       * Accessor methods
203       */
204    
205      /** Start of discontig getter @return The start of the discontiguous space */
206      public static Address getDiscontigStart() { return heapCursor; }
207    
208      /** End of discontig getter @return The end of the discontiguous space */
209      public static Address getDiscontigEnd() { return heapLimit.minus(1); }
210    
211      /** Name getter @return The name of this space */
212      public final String getName() { return name; }
213    
214      /** Start getter @return The start address of this space */
215      public final Address getStart() { return start; }
216    
217      /** Extent getter @return The size (extent) of this space */
218      public final Extent getExtent() { return extent; }
219    
220      /** Descriptor method @return The integer descriptor for this space */
221      public final int getDescriptor() { return descriptor; }
222    
223      /** Index getter @return The index (ordinal number) of this space */
224      public final int getIndex() { return index; }
225    
226      /** Immortal getter @return {@code true} if this space is never collected */
227      public final boolean isImmortal() { return immortal; }
228    
229      /** Movable getter @return {@code true} if objects in this space may move */
230      public boolean isMovable() { return movable; }
231    
232      /** ReservedPages getter @return The number of reserved pages */
233      public final int reservedPages() { return pr.reservedPages(); }
234    
235      /** CommittedPages getter @return The number of committed pages */
236      public final int committedPages() { return pr.committedPages(); }
237    
238      /** AvailablePages getter @return The number of pages available for allocation */
239      public final int availablePhysicalPages() { return pr.getAvailablePhysicalPages(); }
240    
241      /** Cumulative committed pages getter @return Cumulative committed pages. */
242      public static long cumulativeCommittedPages() {
243        return PageResource.cumulativeCommittedPages();
244      }
245    
246      /****************************************************************************
247       *
248       * Object and address tests / accessors
249       */
250    
251      /**
252       * Return {@code true} if the given object is in an immortal (uncollected) space.
253       *
254       * @param object The object in question
255       * @return {@code true} if the given object is in an immortal (uncollected) space.
256       */
257      public static boolean isImmortal(ObjectReference object) {
258        Space space = getSpaceForObject(object);
259        if (space == null)
260          return true;
261        else
262          return space.isImmortal();
263      }
264    
265      /**
266       * Return {@code true} if the given object is in space that moves objects.
267       *
268       * @param object The object in question
269       * @return {@code true} if the given object is in space that moves objects.
270       */
271      @Inline
272      public static boolean isMovable(ObjectReference object) {
273        Space space = getSpaceForObject(object);
274        if (space == null)
275          return true;
276        else
277          return space.isMovable();
278      }
279    
280      /**
281       * Return {@code true} if the given object is in a space managed by MMTk.
282       *
283       * @param object The object in question
284       * @return {@code true} if the given object is in a space managed by MMTk.
285       */
286      @Inline
287      public static boolean isMappedObject(ObjectReference object) {
288        return !object.isNull() && (getSpaceForObject(object) != null) && Mmapper.objectIsMapped(object);
289      }
290    
291      /**
292       * Return {@code true} if the given address is in a space managed by MMTk.
293       *
294       * @param address The address in question
295       * @return {@code true} if the given address is in a space managed by MMTk.
296       */
297      @Inline
298      public static boolean isMappedAddress(Address address) {
299        return Map.getSpaceForAddress(address) != null && Mmapper.addressIsMapped(address);
300      }
301    
302      /**
303       * Return {@code true} if the given object is the space associated with the
304       * given descriptor.
305       *
306       * @param descriptor The descriptor for a space
307       * @param object The object in question
308       * @return {@code true} if the given object is in the space associated with
309       * the descriptor.
310       */
311      @Inline
312      public static boolean isInSpace(int descriptor, ObjectReference object) {
313        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!object.isNull());
314        return isInSpace(descriptor, VM.objectModel.refToAddress(object));
315      }
316    
317      /**
318       * Return {@code true} if the given address is the space associated with the
319       * given descriptor.
320       *
321       * @param descriptor The descriptor for a space
322       * @param address The address in question.
323       * @return {@code true} if the given address is in the space associated with
324       * the descriptor.
325       */
326      @Inline
327      public static boolean isInSpace(int descriptor, Address address) {
328        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!address.isZero());
329        if (FORCE_SLOW_MAP_LOOKUP || !SpaceDescriptor.isContiguous(descriptor)) {
330          return Map.getDescriptorForAddress(address) == descriptor;
331        } else {
332          Address start = SpaceDescriptor.getStart(descriptor);
333          if (!VM.VERIFY_ASSERTIONS &&
334              SpaceDescriptor.isContiguousHi(descriptor))
335            return address.GE(start);
336          else {
337            Extent size = Word.fromIntSignExtend(SpaceDescriptor.getChunks(descriptor)).lsh(LOG_BYTES_IN_CHUNK).toExtent();
338            Address end = start.plus(size);
339            return address.GE(start) && address.LT(end);
340          }
341        }
342      }
343    
344      /**
345       * Return the space for a given object
346       *
347       * @param object The object in question
348       * @return The space containing the object
349       */
350      @Inline
351      public static Space getSpaceForObject(ObjectReference object) {
352        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!object.isNull());
353        return Map.getSpaceForAddress(VM.objectModel.refToAddress(object));
354      }
355    
356      /**
357       * Return the space for a given address, not necessarily the
358       * start address of an object.
359       *
360       * @param addr The address in question
361       * @return The space containing the address
362       */
363      public static Space getSpaceForAddress(Address addr) {
364        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!addr.isZero());
365        return Map.getSpaceForAddress(addr);
366      }
367    
368      /****************************************************************************
369       *
370       * Page management
371       */
372    
373      /**
374       * Update the zeroing approach for this space.
375       */
376      @Interruptible
377      public void setZeroingApproach(boolean useNT, boolean concurrent) {
378        pr.updateZeroingApproach(useNT, concurrent);
379      }
380    
381      /**
382       * Skip concurrent zeroing (fall back to bulk zeroing).
383       */
384      public void skipConcurrentZeroing() {
385        pr.skipConcurrentZeroing();
386      }
387    
388      /**
389       * Trigger concurrent zeroing.
390       */
391      public void triggerConcurrentZeroing() {
392        pr.triggerConcurrentZeroing();
393      }
394    
395      /**
396       * Acquire a number of pages from the page resource, returning
397       * either the address of the first page, or zero on failure.<p>
398       *
399       * This may trigger a GC if necessary.<p>
400       *
401       * First the page budget is checked to see whether polling the GC is
402       * necessary.  If so, the GC is polled.  If a GC is required then the
403       * request fails and zero is returned.<p>
404       *
405       * If the check of the page budget does not lead to GC being
406       * triggered, then a request is made for specific pages in virtual
407       * memory.  If the page manager cannot satisify this request, then
408       * the request fails, a GC is forced, and zero is returned.
409       * Otherwise the address of the first page is returned.<p>
410       *
411       * @param pages The number of pages requested
412       * @return The start of the first page if successful, zero on
413       * failure.
414       */
415      @LogicallyUninterruptible
416      public final Address acquire(int pages) {
417        boolean allowPoll = VM.activePlan.isMutator() && Plan.isInitialized();
418    
419        /* Check page budget */
420        int pagesReserved = pr.reservePages(pages);
421    
422        /* Poll, either fixing budget or requiring GC */
423        if (allowPoll && VM.activePlan.global().poll(false, this)) {
424          pr.clearRequest(pagesReserved);
425          VM.collection.blockForGC();
426          return Address.zero(); // GC required, return failure
427        }
428    
429        /* Page budget is ok, try to acquire virtual memory */
430        Address rtn = pr.getNewPages(pagesReserved, pages, zeroed);
431        if (rtn.isZero()) {
432          /* Failed, so force a GC */
433          if (!allowPoll) VM.assertions.fail("Physical allocation failed when polling not allowed!");
434          boolean gcPerformed = VM.activePlan.global().poll(true, this);
435          if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(gcPerformed, "GC not performed when forced.");
436          pr.clearRequest(pagesReserved);
437          VM.collection.blockForGC();
438          return Address.zero();
439        }
440    
441        return rtn;
442      }
443    
444      /**
445       * Extend the virtual memory associated with a particular discontiguous
446       * space.  This simply involves requesting a suitable number of chunks
447       * from the pool of chunks available to discontiguous spaces.
448       *
449       * @param chunks The number of chunks by which the space needs to be extended
450       * @return The address of the new discontiguous space.
451       */
452      public Address growDiscontiguousSpace(int chunks) {
453        Address newHead = Map.allocateContiguousChunks(descriptor, this, chunks, headDiscontiguousRegion);
454        if (newHead.isZero()) {
455          return Address.zero();
456        }
457        return headDiscontiguousRegion = newHead;
458      }
459    
460      /**
461       * Return the number of chunks required to satisfy a request for a certain number of pages
462       *
463       * @param pages The number of pages desired
464       * @return The number of chunks needed to satisfy the request
465       */
466      public static int requiredChunks(int pages) {
467        Extent extent = chunkAlign(Extent.fromIntZeroExtend(pages<<LOG_BYTES_IN_PAGE), false);
468        return extent.toWord().rshl(LOG_BYTES_IN_CHUNK).toInt();
469      }
470    
471      /**
472       * This hook is called by page resources each time a space grows.  The space may
473       * tap into the hook to monitor heap growth.  The call is made from within the
474       * page resources' critical region, immediately before yielding the lock.
475       *
476       * @param start The start of the newly allocated space
477       * @param bytes The size of the newly allocated space
478       * @param newChunk {@code true} if the new space encroached upon or started a new chunk or chunks.
479       */
480      public void growSpace(Address start, Extent bytes, boolean newChunk) {}
481    
482      /**
483       * Release one or more contiguous chunks associated with a discontiguous
484       * space.
485       *
486       * @param chunk The address of the start of the contiguous chunk or chunks
487       * @return The number of chunks freed
488       */
489      public int releaseDiscontiguousChunks(Address chunk) {
490        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(chunk.EQ(chunkAlign(chunk, true)));
491        if (chunk.EQ(headDiscontiguousRegion)) {
492          headDiscontiguousRegion = Map.getNextContiguousRegion(chunk);
493        }
494        return Map.freeContiguousChunks(chunk);
495      }
496    
497      /**
498       * @return The address of the head of the discontiguous chunk map.
499       */
500      public Address getHeadDiscontiguousRegion() {
501        return headDiscontiguousRegion;
502      }
503    
504      public void releaseAllChunks() {
505        Map.freeAllChunks(headDiscontiguousRegion);
506        headDiscontiguousRegion = Address.zero();
507      }
508    
509      /**
510       * Release a unit of allocation (a page or pages)
511       *
512       * @param start The address of the start of the region to be released
513       */
514      public abstract void release(Address start);
515    
516      /**
517       * Get the total number of pages reserved by all of the spaces
518       *
519       * @return the total number of pages reserved by all of the spaces
520       */
521      private static int getPagesReserved() {
522        int pages = 0;
523        for (int i = 0; i < spaceCount; i++) {
524          pages += spaces[i].reservedPages();
525        }
526        return pages;
527      }
528    
529      /****************************************************************************
530       *
531       * Debugging / printing
532       */
533    
534      /**
535       * Print out the memory used by all spaces, in megabytes
536       */
537      public static void printUsageMB() { printUsage(MB); }
538    
539      /**
540       * Print out the memory used by all spaces, in megabytes
541       */
542      public static void printUsagePages() { printUsage(PAGES); }
543    
544      /**
545       * Print out a map of virtual memory useage by all spaces
546       */
547      public static void printVMMap() {
548        Log.writeln("Key: (I)mmortal (N)onmoving (D)iscontiguous (E)xtent (F)raction");
549        Log.write("     HEAP_START "); Log.writeln(HEAP_START);
550        Log.write("AVAILABLE_START "); Log.writeln(AVAILABLE_START);
551        for (int i = 0; i < spaceCount; i++) {
552          Space space = spaces[i];
553    
554          for (int s = 0; s < 11 - space.nameLength; s++)
555            Log.write(" ");
556          Log.write(space.name); Log.write(" ");
557          Log.write(space.immortal ? "I" : " ");
558          Log.write(space.movable ? " " : "N");
559    
560          if (space.contiguous) {
561            Log.write("  ");
562            Log.write(space.start); Log.write("->");
563            Log.write(space.start.plus(space.extent.minus(1)));
564            if (space.vmRequest.type == VMRequest.REQUEST_EXTENT) {
565              Log.write(" E "); Log.write(space.vmRequest.extent);
566            } else if (space.vmRequest.type == VMRequest.REQUEST_FRACTION) {
567              Log.write(" F "); Log.write(space.vmRequest.frac);
568            }
569            Log.writeln();
570          } else {
571            Log.write("D [");
572            for(Address a = space.headDiscontiguousRegion; !a.isZero(); a = Map.getNextContiguousRegion(a)) {
573              Log.write(a); Log.write("->");
574              Log.write(a.plus(Map.getContiguousRegionSize(a).minus(1)));
575              if (Map.getNextContiguousRegion(a) != Address.zero())
576                Log.write(", ");
577            }
578            Log.writeln("]");
579          }
580        }
581        Log.write("  AVAILABLE_END "); Log.writeln(AVAILABLE_END);
582        Log.write("       HEAP_END "); Log.writeln(HEAP_END);
583      }
584    
585      /**
586       * Interface to use to implement the Visitor Pattern for Spaces.
587       */
588      public static interface SpaceVisitor {
589        void visit(Space s);
590      }
591    
592      /**
593       * Implement the Visitor Pattern for Spaces.
594       * @param v The visitor to perform on each Space instance
595       */
596      @Interruptible
597      public static void visitSpaces(SpaceVisitor v) {
598        for (int i = 0; i < spaceCount; i++) {
599          v.visit(spaces[i]);
600        }
601      }
602    
603    
604      /**
605       * Ensure that all MMTk spaces (all spaces aside from the VM space)
606       * are mapped. Demand zero map all of them if they are not already
607       * mapped.
608       */
609      @Interruptible
610      public static void eagerlyMmapMMTkSpaces() {
611        eagerlyMmapMMTkContiguousSpaces();
612        eagerlyMmapMMTkDiscontiguousSpaces();
613      }
614    
615    
616      /**
617       * Ensure that all contiguous MMTk spaces are mapped. Demand zero map
618       * all of them if they are not already mapped.
619       */
620      @Interruptible
621      public static void eagerlyMmapMMTkContiguousSpaces() {
622        for (int i = 0; i < spaceCount; i++) {
623          Space space = spaces[i];
624          if (space != VM.memory.getVMSpace()) {
625            if (Options.verbose.getValue() > 2) {
626              Log.write("Mapping ");
627              Log.write(space.name);
628              Log.write(" ");
629              Log.write(space.start);
630              Log.write("->");
631              Log.writeln(space.start.plus(space.extent.minus(1)));
632            }
633            Mmapper.ensureMapped(space.start, space.extent.toInt()>>LOG_BYTES_IN_PAGE);
634          }
635        }
636      }
637    
638      /**
639       * Ensure that all discontiguous MMTk spaces are mapped. Demand zero map
640       * all of them if they are not already mapped.
641       */
642      @Interruptible
643      public static void eagerlyMmapMMTkDiscontiguousSpaces() {
644        Address regionStart = Space.getDiscontigStart();
645        Address regionEnd = Space.getDiscontigEnd();
646        int pages = regionEnd.diff(regionStart).toInt()>>LOG_BYTES_IN_PAGE;
647        Log.write("Mapping discontiguous spaces ");
648        Log.write(regionStart);
649        Log.write("->");
650        Log.writeln(regionEnd.minus(1));
651        Mmapper.ensureMapped(getDiscontigStart(), pages);
652      }
653    
654      /**
655       * Print out the memory used by all spaces in either megabytes or
656       * pages.
657       *
658       * @param mode An enumeration type that specifies the format for the
659       * prining (PAGES, MB, PAGES_MB, or MB_PAGES).
660       */
661      private static void printUsage(int mode) {
662        Log.write("used = ");
663        printPages(getPagesReserved(), mode);
664        boolean first = true;
665        for (int i = 0; i < spaceCount; i++) {
666          Space space = spaces[i];
667          Log.write(first ? " = " : " + ");
668          first = false;
669          Log.write(space.name); Log.write(" ");
670          printPages(space.reservedPages(), mode);
671        }
672        Log.writeln();
673      }
674    
675      /**
676       * Print out the number of pages and or megabytes, depending on the mode.
677       *
678       * @param pages The number of pages
679       * @param mode An enumeration type that specifies the format for the
680       * printing (PAGES, MB, PAGES_MB, or MB_PAGES).
681       */
682      private static void printPages(int pages, int mode) {
683        double mb = (double) (pages << LOG_BYTES_IN_PAGE) / (double) (1 << 20);
684        switch (mode) {
685        case PAGES: Log.write(pages); Log.write(" pgs"); break;
686        case MB:    Log.write(mb); Log.write(" Mb"); break;
687        case PAGES_MB: Log.write(pages); Log.write(" pgs ("); Log.write(mb); Log.write(" Mb)"); break;
688        case MB_PAGES: Log.write(mb); Log.write(" Mb ("); Log.write(pages); Log.write(" pgs)"); break;
689          default: VM.assertions.fail("writePages passed illegal printing mode");
690        }
691      }
692    
693      /****************************************************************************
694       *
695       * Miscellaneous
696       */
697    
698      /**
699       * Trace an object as part of a collection and return the object,
700       * which may have been forwarded (if a copying collector).
701       *
702       * @param trace The trace being conducted.
703       * @param object The object to trace
704       * @return The object, forwarded, if appropriate
705       */
706      public abstract ObjectReference traceObject(TransitiveClosure trace, ObjectReference object);
707    
708    
709      /**
710       * Has the object in this space been reached during the current collection.
711       * This is used for GC Tracing.
712       *
713       * @param object The object reference.
714       * @return {@code true} if the object is reachable.
715       */
716      public boolean isReachable(ObjectReference object) {
717        return isLive(object);
718      }
719    
720    
721      /**
722       * Is the object in this space alive?
723       *
724       * @param object The object reference.
725       * @return {@code true} if the object is live.
726       */
727      public abstract boolean isLive(ObjectReference object);
728    
729      /**
730       * Align an address to a space chunk
731       *
732       * @param addr The address to be aligned
733       * @param down If {@code true} the address will be rounded down, otherwise
734       * it will rounded up.
735       * @return The chunk-aligned address
736       */
737      public static Address chunkAlign(Address addr, boolean down) {
738        if (!down) addr = addr.plus(BYTES_IN_CHUNK - 1);
739        return addr.toWord().rshl(LOG_BYTES_IN_CHUNK).lsh(LOG_BYTES_IN_CHUNK).toAddress();
740      }
741    
742      /**
743       * Align an extent to a space chunk
744       *
745       * @param bytes The extent to be aligned
746       * @param down If {@code true} the extent will be rounded down, otherwise
747       * it will rounded up.
748       * @return The chunk-aligned extent
749       */
750      public static Extent chunkAlign(Extent bytes, boolean down) {
751        if (!down) bytes = bytes.plus(BYTES_IN_CHUNK - 1);
752        return bytes.toWord().rshl(LOG_BYTES_IN_CHUNK).lsh(LOG_BYTES_IN_CHUNK).toExtent();
753      }
754    
755      /**
756       * Convert a fraction into a number of bytes according to the
757       * fraction of available bytes.
758       *
759       * @param frac The fraction of available virtual memory desired
760       * @return The corresponding number of bytes, chunk-aligned.
761       */
762      public static Extent getFracAvailable(float frac) {
763        long bytes = (long) (frac * AVAILABLE_BYTES.toLong());
764        Word mb = Word.fromIntSignExtend((int) (bytes >> LOG_BYTES_IN_MBYTE));
765        Extent rtn = mb.lsh(LOG_BYTES_IN_MBYTE).toExtent();
766        return chunkAlign(rtn, false);
767      }
768    }