1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
|
/*
Title: memmgr.h Memory segment manager
Copyright (c) 2006-8, 2010-12 David C. J. Matthews
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MEMMGR_H
#define MEMMGR_H
#include "bitmap.h"
#include "locking.h"
// utility conversion macros
#define Words_to_K(w) (w*sizeof(PolyWord))/1024
#define Words_to_M(w) (w*sizeof(PolyWord))/(1<<20)
#define B_to_M(b) (b/(1<<20))
class ScanAddress;
class GCTaskId;
class TaskData;
typedef enum {
ST_IO, // The io area forms an interface with the RTS
ST_PERMANENT, // Permanent areas are part of the object code
// Also loaded saved state.
ST_LOCAL, // Local heaps contain volatile data
ST_EXPORT, // Temporary export area
ST_STACK // ML Stack for a thread
} SpaceType;
// B-tree used in SpaceForAddress. Leaves are MemSpaces.
class SpaceTree
{
public:
SpaceTree(bool is): isSpace(is) { }
virtual ~SpaceTree() {}
bool isSpace;
};
// A non-leaf node in the B-tree
class SpaceTreeTree: public SpaceTree
{
public:
SpaceTreeTree();
virtual ~SpaceTreeTree();
SpaceTree *tree[256];
};
// Base class for the various memory spaces.
class MemSpace: public SpaceTree
{
protected:
MemSpace();
virtual ~MemSpace();
public:
SpaceType spaceType;
bool isMutable;
bool isOwnSpace; // True if this has been allocated.
PolyWord *bottom; // Bottom of area
PolyWord *top; // Top of area.
POLYUNSIGNED spaceSize(void)const { return top-bottom; } // No of words
// These next two are used in the GC to limit scanning for
// weak refs.
PolyWord *lowestWeak, *highestWeak;
// Used when printing debugging info
virtual const char *spaceTypeString() { return isMutable ? "mutable" : "immutable"; }
friend class MemMgr;
};
// Permanent memory space. Either linked into the executable program or
// loaded from a saved state file.
class PermanentMemSpace: public MemSpace
{
protected:
PermanentMemSpace(): index(0), hierarchy(0), noOverwrite(false),
byteOnly(false), topPointer(0) {}
public:
unsigned index; // An identifier for the space. Used when saving and loading.
unsigned hierarchy; // The hierarchy number: 0=from executable, 1=top level saved state, ...
bool noOverwrite; // Don't save this in deeper hierarchies.
bool byteOnly; // Only contains byte data - no need to scan for addresses.
// When exporting or saving state we copy data into a new area.
// This area grows upwards unlike the local areas that grow down.
PolyWord *topPointer;
Bitmap shareBitmap; // Used in sharedata
friend class MemMgr;
};
#define NSTARTS 10
// Local areas can be garbage collected.
class LocalMemSpace: public MemSpace
{
protected:
LocalMemSpace();
virtual ~LocalMemSpace() {}
bool InitSpace(POLYUNSIGNED size, bool mut);
public:
// Allocation. The minor GC allocates at the bottom of the areas while the
// major GC and initial allocations are made at the top. The reason for this
// is that it's only possible to scan objects from the bottom up and the minor
// GC combines scanning with allocation whereas the major GC compacts from the
// bottom into the top of an area.
PolyWord *upperAllocPtr; // Allocation pointer. Objects are allocated AFTER this.
PolyWord *lowerAllocPtr; // Allocation pointer. Objects are allocated BEFORE this.
PolyWord *fullGCLowerLimit;// Lowest object in area before copying.
PolyWord *fullGCRescanStart; // Upper and lower limits for rescan during mark phase.
PolyWord *fullGCRescanEnd;
PolyWord *partialGCTop; // Value of upperAllocPtr before the current partial GC.
PolyWord *partialGCScan; // Scan pointer used in minor GC
PolyWord *partialGCRootBase; // Start of the root objects.
PolyWord *partialGCRootTop;// Value of lowerAllocPtr after the roots have been copied.
PLock spaceLock; // Lock used to protect forwarding pointers
GCTaskId *spaceOwner; // The thread that "owns" this space during a GC.
Bitmap bitmap; /* bitmap with one bit for each word in the GC area. */
bool allocationSpace; // True if this is (mutable) space for initial allocation
POLYUNSIGNED start[NSTARTS]; /* starting points for bit searches. */
unsigned start_index; /* last index used to index start array */
POLYUNSIGNED i_marked; /* count of immutable words marked. */
POLYUNSIGNED m_marked; /* count of mutable words marked. */
POLYUNSIGNED updated; /* count of words updated. */
POLYUNSIGNED allocatedSpace(void)const // Words allocated
{ return (top-upperAllocPtr) + (lowerAllocPtr-bottom); }
POLYUNSIGNED freeSpace(void)const // Words free
{ return upperAllocPtr-lowerAllocPtr; }
virtual const char *spaceTypeString()
{ return allocationSpace ? "allocation" : MemSpace::spaceTypeString(); }
// Used when converting to and from bit positions in the bitmap
POLYUNSIGNED wordNo(PolyWord *pt) { return pt - bottom; }
PolyWord *wordAddr(POLYUNSIGNED bitno) { return bottom + bitno; }
friend class MemMgr;
};
class StackObject; // Abstract - Architecture specific
// Stack spaces. These are managed by the thread module
class StackSpace: public MemSpace
{
public:
StackSpace() { isOwnSpace = true; }
StackObject *stack()const { return (StackObject *)bottom; }
};
class MemMgr
{
public:
MemMgr();
~MemMgr();
// Create a local space for initial allocation.
LocalMemSpace *CreateAllocationSpace(POLYUNSIGNED size);
// Create and initialise a new local space and add it to the table.
LocalMemSpace *NewLocalSpace(POLYUNSIGNED size, bool mut);
// Create an entry for a permanent space.
PermanentMemSpace *NewPermanentSpace(PolyWord *base, POLYUNSIGNED words,
unsigned flags, unsigned index, unsigned hierarchy = 0);
// Create an entry for the IO space.
MemSpace *InitIOSpace(PolyWord *base, POLYUNSIGNED words);
// Delete a local space
bool DeleteLocalSpace(LocalMemSpace *sp);
// Allocate an area of the heap of at least minWords and at most maxWords.
// This is used both when allocating single objects (when minWords and maxWords
// are the same) and when allocating heap segments. If there is insufficient
// space to satisfy the minimum it will return 0. Updates "maxWords" with
// the space actually allocated
PolyWord *AllocHeapSpace(POLYUNSIGNED minWords, POLYUNSIGNED &maxWords, bool doAllocation = true);
PolyWord *AllocHeapSpace(POLYUNSIGNED words)
{ POLYUNSIGNED allocated = words; return AllocHeapSpace(words, allocated); }
// Check that a subsequent allocation will succeed. Called from the GC to ensure
bool CheckForAllocation(POLYUNSIGNED words);
// If an allocation space has a lot of data left in it, particularly a single
// large object we should turn it into a local area.
void ConvertAllocationSpaceToLocal(LocalMemSpace *space);
// Allocate space for the initial stack for a thread. The caller must
// initialise the new stack. Returns 0 if allocation fails.
StackSpace *NewStackSpace(POLYUNSIGNED size);
// Adjust the space for a stack. Returns true if it succeeded. If it failed
// it leaves the stack untouched.
bool GrowOrShrinkStack(TaskData *taskData, POLYUNSIGNED newSize);
// Delete a stack when a thread has finished.
bool DeleteStackSpace(StackSpace *space);
// Create and delete export spaces
PermanentMemSpace *NewExportSpace(POLYUNSIGNED size, bool mut, bool noOv);
void DeleteExportSpaces(void);
bool PromoteExportSpaces(unsigned hierarchy); // Turn export spaces into permanent spaces.
bool DemoteImportSpaces(void); // Turn previously imported spaces into local.
PermanentMemSpace *SpaceForIndex(unsigned index) const; // Return the space for a given index
// As a debugging check, write protect the immutable areas apart from during the GC.
void ProtectImmutable(bool on);
// Find a space that contains a given address. This is called for every cell
// during a GC so needs to be fast.,
MemSpace *SpaceForAddress(const void *pt) const
{
uintptr_t t = (uintptr_t)pt;
SpaceTree *tr = spaceTree;
// Each level of the tree is either a leaf or a vector of trees.
unsigned j = sizeof(void *)*8;
for (;;)
{
if (tr == 0 || tr->isSpace)
return (MemSpace*)tr;
j -= 8;
tr = ((SpaceTreeTree*)tr)->tree[(t >> j) & 0xff];
}
return 0;
}
// Find a local address for a space.
LocalMemSpace *LocalSpaceForAddress(const void *pt) const
{
MemSpace *s = SpaceForAddress(pt);
if (s != 0 && s->spaceType == ST_LOCAL)
return (LocalMemSpace*)s;
else return 0;
}
bool IsIOPointer(const void *pt) const { return pt >= ioSpace->bottom && pt < ioSpace->top; }
bool IsLocalMutable(const void *pt) const
{ LocalMemSpace *space = LocalSpaceForAddress(pt); return space != 0 && space->isMutable; }
void SetReservation(POLYUNSIGNED words) { reservedSpace = words; }
// In several places we assume that segments are filled with valid
// objects. This fills unused memory with one or more "byte" objects.
void FillUnusedSpace(PolyWord *base, POLYUNSIGNED words);
// Return number of words of free space for stats.
POLYUNSIGNED GetFreeAllocSpace();
// Remove unused local areas.
void RemoveEmptyLocals();
// Remove unused allocation areas to reduce the space below the limit.
void RemoveExcessAllocation(POLYUNSIGNED words);
void RemoveExcessAllocation() { RemoveExcessAllocation(spaceBeforeMinorGC); }
MemSpace *IoSpace() { return ioSpace; } // Return pointer to the IO space.
MemSpace *ioSpace; // The IO space
// Table for permanent spaces
PermanentMemSpace **pSpaces;
unsigned npSpaces;
// Table for local spaces
LocalMemSpace **lSpaces;
unsigned nlSpaces;
// Table for export spaces
PermanentMemSpace **eSpaces;
unsigned neSpaces;
// Table for stack spaces
StackSpace **sSpaces;
unsigned nsSpaces;
PLock stackSpaceLock;
// Storage manager lock.
PLock allocLock;
unsigned nextIndex; // Used when allocating new permanent spaces.
POLYUNSIGNED SpaceBeforeMinorGC() const { return spaceBeforeMinorGC; }
POLYUNSIGNED SpaceForHeap() const { return spaceForHeap; }
void SetSpaceBeforeMinorGC(POLYUNSIGNED minorSize) { spaceBeforeMinorGC = minorSize; }
void SetSpaceForHeap(POLYUNSIGNED heapSize) { spaceForHeap = heapSize; }
POLYUNSIGNED CurrentAllocSpace() { return currentAllocSpace; }
POLYUNSIGNED AllocatedInAlloc();
POLYUNSIGNED CurrentHeapSize() { return currentHeapSize; }
POLYUNSIGNED DefaultSpaceSize() const { return defaultSpaceSize; }
void ReportHeapSizes(const char *phase);
private:
bool AddLocalSpace(LocalMemSpace *space);
POLYUNSIGNED reservedSpace;
unsigned nextAllocator;
// The default size in words when creating new segments.
POLYUNSIGNED defaultSpaceSize;
// The number of words that can be used for initial allocation.
POLYUNSIGNED spaceBeforeMinorGC;
// The number of words that can be used for the heap
POLYUNSIGNED spaceForHeap;
// The current sizes of the allocation space and the total heap size.
POLYUNSIGNED currentAllocSpace, currentHeapSize;
// LocalSpaceForAddress is a hot-spot so we use a B-tree to convert addresses;
SpaceTree *spaceTree;
PLock spaceTreeLock;
void AddTree(MemSpace *space) { AddTree(space, space->bottom, space->top); }
void RemoveTree(MemSpace *space) { RemoveTree(space, space->bottom, space->top); }
void AddTree(MemSpace *space, PolyWord *startS, PolyWord *endS);
void RemoveTree(MemSpace *space, PolyWord *startS, PolyWord *endS);
void AddTreeRange(SpaceTree **t, MemSpace *space, uintptr_t startS, uintptr_t endS);
void RemoveTreeRange(SpaceTree **t, MemSpace *space, uintptr_t startS, uintptr_t endS);
};
extern MemMgr gMem;
#endif
|