1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
21
package org.jnode.vm.memmgr.generational;
22
23
import java.io.PrintWriter;
24
25
import org.jnode.annotation.Inline;
26
import org.jnode.annotation.MagicPermission;
27
import org.jnode.vm.MemoryBlockManager;
28
import org.jnode.vm.ObjectVisitor;
29
import org.jnode.vm.Unsafe;
30
import org.jnode.vm.VmArchitecture;
31
import org.jnode.vm.VmMagic;
32
import org.jnode.vm.classmgr.ObjectFlags;
33
import org.jnode.vm.classmgr.ObjectLayout;
34
import org.jnode.vm.classmgr.VmClassLoader;
35
import org.jnode.vm.classmgr.VmClassType;
36
import org.jnode.vm.classmgr.VmNormalClass;
37
import org.jnode.vm.classmgr.VmType;
38
import org.jnode.vm.memmgr.GCStatistics;
39
import org.jnode.vm.memmgr.HeapHelper;
40
import org.jnode.vm.memmgr.HeapStatistics;
41
import org.jnode.vm.memmgr.VmHeapManager;
42
import org.jnode.vm.scheduler.Monitor;
43
import org.jnode.vm.scheduler.VmProcessor;
44
import org.vmmagic.unboxed.Address;
45
import org.vmmagic.unboxed.Extent;
46
import org.vmmagic.unboxed.Word;
47
48
/**
49
 * 
50
 * @author ismael
51
 * 
52
 */
53
@MagicPermission
54
public final class GenHeapManager extends VmHeapManager {
55
56
    /**
57
     * The GC thread
58
     */
59
    private GenGCThread gcThread;
60
61
    /**
62
     * The GC free thread
63
     */
64
    private GenGCSweepThread gcSweepThread;
65
66
    private Monitor heapMonitor;
67
68
    private GenGCManager gcManager;
69
70
    private final GenGCYellowObjectManager yellowObjectManager;
71
72
    private final GenGCTriggerManager triggerManager = new GenGCTriggerManager();
73
74
    /**
75
     * Default size in bytes of a new heap
76
     */
77
    public static final int DEFAULT_HEAP_SIZE = 16 * 1024 * 1024;
78
79
    /**
80
     * The boot heap
81
     */
82
    private final VmBootHeap bootHeap;
83
84
    /**
85
     * Are we low on memory
86
     */
87
    private boolean lowOnMemory;
88
89
    /**
90
     * Linked list of all heaps.
91
     */
92
    private VmDefaultHeap heapList;
93
94
    /**
95
     * The first heap.
96
     */
97
    private final VmDefaultHeap firstNormalHeap;
98
99
    /**
100
     * The heap currently used for allocation
101
     */
102
    private VmDefaultHeap currentHeap;
103
104
    /**
105
     * The heap used for allocations during a GC
106
     */
107
    private VmDefaultHeap gcHeap;
108
109
    /**
110
     * The class of the default heap type. Set by initialize
111
     */
112
    private final VmNormalClass<VmDefaultHeap> defaultHeapClass;
113
114
    private boolean gcActive;
115
    
116
    private boolean gcManagerLoaded = false;
117
118
    /**
119
     * Make this private, so we cannot be instantiated
120
     */
121
    @SuppressWarnings("unchecked")
122
    public GenHeapManager(VmClassLoader loader, HeapHelper helper) throws ClassNotFoundException {
123
        super(helper);
124
        this.bootHeap = new VmBootHeap(getHelper());
125
        setWriteBarrier(null);
126
        this.firstNormalHeap = new VmDefaultHeap(getHelper());
127
        this.currentHeap = firstNormalHeap;
128
        this.heapList = firstNormalHeap;
129
        this.defaultHeapClass = (VmNormalClass<VmDefaultHeap>) loader.loadClass(VmDefaultHeap.class
130
                .getName(), true);
131
        this.yellowObjectManager = new GenGCYellowObjectManager(getHelper());
132
    }
133
134
    @Override
135
    public void start() {
136
        Unsafe.debug("GenHeapManager.start()\n");
137
        final VmArchitecture arch = VmProcessor.current().getArchitecture();
138
139
        heapMonitor = new Monitor();
140
141
        gcManager = new GenGCManager(this, arch);
142
        gcManagerLoaded = true;
143
        gcThread = new GenGCThread(gcManager, heapMonitor);
144
        gcSweepThread = new GenGCSweepThread(gcManager, heapMonitor);
145
146
        Unsafe.debug("GenHeapManager.start(create write barrier)\n");
147
        setWriteBarrier(new GenWriteBarrier(getHelper(), firstNormalHeap.nbRefOffset));
148
        
149
        Unsafe.debug("GenHeapManager.start(start threads)\n");
150
151
        gcThread.start();
152
        gcSweepThread.start();
153
        Unsafe.debug("GenHeapManager.start(gcSweepThread started)\n");
154
155
        final boolean traceTriggers = (heapFlags & TRACE_TRIGGER) != 0;
156
        triggerManager.initialize(gcThread, gcSweepThread, traceTriggers);
157
        triggerManager.enableGCTriggers();
158
        gcManagerLoaded = true;
159
        Unsafe.debug("GenHeapManager.start(ended)\n");
160
    }
161
162
    /**
163
     * Is the given address the address of an allocated object on this heap?
164
     * 
165
     * @param ptr
166
     *            The address to examine.
167
     * @return True if the given address if a valid starting address of an
168
     *         object, false otherwise.
169
     */
170
    @Override
171
    @Inline
172
    public final boolean isObject(Address ptr) {
173
        if (!ptr.toWord().and(Word.fromIntZeroExtend(ObjectLayout.OBJECT_ALIGN - 1)).isZero()) {
174
            // The object is not at an object aligned boundary
175
            return false;
176
        }
177
        if (bootHeap.isObject(ptr)) {
178
            return true;
179
        }
180
        VmDefaultHeap heap = heapList;
181
        while (heap != null) {
182
            if (heap.isObject(ptr)) {
183
                return true;
184
            }
185
            heap = heap.getNext();
186
        }
187
        return false;
188
    }
189
190
    /**
191
     * Is the system low on memory?
192
     * 
193
     * @return boolean
194
     */
195
    @Override
196
    public boolean isLowOnMemory() {
197
        return lowOnMemory;
198
    }
199
200
    /**
201
     * Start a garbage collection process
202
     */
203
    @Override
204
    public void gc() {
205
        gcThread.trigger(false);
206
    }
207
208
    /**
209
     * Gets the size of free memory in bytes.
210
     * 
211
     * @return long
212
     */
213
    @Override
214
    public long getFreeMemory() {
215
        Extent size = Extent.zero();
216
        VmDefaultHeap h = firstNormalHeap;
217
        while (h != null) {
218
            size = size.add(h.getFreeSize());
219
            h = h.getNext();
220
        }
221
        // size += (Unsafe.addressToLong(heapEnd) -
222
        // Unsafe.addressToLong(nextHeapPtr));
223
        size = size.add(Extent.fromLong(MemoryBlockManager.getFreeMemory()));
224
        return size.toLong();
225
    }
226
227
    /**
228
     * Gets the size of all memory in bytes.
229
     * 
230
     * @return the size of all memory in bytes
231
     */
232
    @Override
233
    public long getTotalMemory() {
234
        long size = bootHeap.getSize();
235
        VmDefaultHeap h = firstNormalHeap;
236
        while (h != null) {
237
            size += h.getSize();
238
            h = h.getNext();
239
        }
240
        // size += (Unsafe.addressToLong(heapEnd) -
241
        // Unsafe.addressToLong(nextHeapPtr));
242
        size += MemoryBlockManager.getFreeMemory();
243
        return size;
244
    }
245
246
    /**
247
     * Gets the first heap. All other heaps can be iterated through the
248
     * <code>getNext()</code> method.
249
     * 
250
     * @return the first heap
251
     */
252
    public final VmDefaultHeap getHeapList() {
253
        return heapList;
254
    }
255
256
    /**
257
     * @see org.jnode.vm.memmgr.VmHeapManager#createProcessorHeapData(org.jnode.vm.scheduler.VmProcessor)
258
     */
259
    @Override
260
    public Object createProcessorHeapData(VmProcessor cpu) {
261
        // No need here, so return null.
262
        return null;
263
    }
264
265
    /**
266
     * @see org.jnode.vm.memmgr.VmHeapManager#notifyClassResolved(org.jnode.vm.classmgr.VmType)
267
     */
268
    @Override
269
    public void notifyClassResolved(VmType<?> vmType) {
270
        // Do nothing
271
    }
272
273
    /**
274
     * @see org.jnode.vm.memmgr.VmHeapManager#loadClasses(org.jnode.vm.classmgr.VmClassLoader)
275
     */
276
    @Override
277
    public void loadClasses(VmClassLoader loader) throws ClassNotFoundException {
278
        loader.loadClass("org.jnode.vm.memmgr.generational.VmBootHeap", true);
279
        loader.loadClass("org.jnode.vm.memmgr.generational.VmDefaultHeap", true);
280
    }
281
282
    /**
283
     * Print the statics on this object on out.
284
     */
285
    @Override
286
    public void dumpStatistics(PrintWriter out) {
287
        out.println("WriteBarrier: " + getWriteBarrier());
288
    }
289
290
    @Override
291
    public GCStatistics getStatistics() {
292
        return gcManager.getStatistics();
293
    }
294
295
    @Override
296
    public HeapStatistics getHeapStatistics() {
297
        final GenHeapStatistics heapStatistics = new GenHeapStatistics();
298
        final GenHeapStatisticsVisitor heapStatisticsVisitor = new GenHeapStatisticsVisitor(
299
                heapStatistics);
300
301
        accept(heapStatisticsVisitor, false);
302
303
        return heapStatistics;
304
    }
305
306
    public final void doMinorGC() {
307
        heapMonitor.enter();
308
        gcThread.triggerMinor(true);
309
        heapMonitor.exit();
310
    }
311
312
    public final void doMajorGC() {
313
        gcThread.triggerMajor(true);
314
    }
315
316
    public final void doSweepAndCleanup() {
317
        heapMonitor.enter();
318
        gcSweepThread.trigger(true);
319
        heapMonitor.exit();
320
    }
321
322
    final GenGCYellowObjectManager getYellowObjectManager() {
323
        return yellowObjectManager;
324
    }
325
326
    /**
327
     * @return Returns the bootHeap.
328
     */
329
    final VmBootHeap getBootHeap() {
330
        return this.bootHeap;
331
    }
332
333
    /**
334
     * @param gcActive
335
     *            The gcActive to set.
336
     */
337
    final void setGcActive(boolean gcActive) {
338
        this.gcActive = gcActive;
339
    }
340
341
    /**
342
     * Sets the currentHeap to the first heap.
343
     */
344
    final void resetCurrentHeap() {
345
        this.currentHeap = this.firstNormalHeap;
346
        // Recalculate the trigger size
347
        triggerManager.setFreeMemory(getFreeMemory());
348
    }
349
350
    // ------------------------------------------
351
    // Private natives
352
    // ------------------------------------------
353
354
    @Override
355
    protected void initialize() {
356
        Unsafe.debug("GenHeapManager.initialize()\n");
357
        // Set the basic fields
358
        helper.bootArchitecture(false);
359
        Unsafe.debug("GenHeapManager.initialize(load architecture)\n");
360
        final VmArchitecture arch = VmProcessor.current().getArchitecture();
361
        Unsafe.debug("GenHeapManager.initialize(load slot size)\n");
362
        final int slotSize = arch.getReferenceSize();
363
        VmDefaultHeap.initializeSizeOffset(slotSize);
364
365
        // Initialize the boot heap.
366
        Unsafe.debug("GenHeapManager.initialize(boot heap)\n");
367
        bootHeap.initialize(helper.getBootHeapStart(), helper.getBootHeapEnd(), slotSize);
368
369
        // Initialize the first normal heap
370
        Unsafe.debug("GenHeapManager.initialize(first normal heap)\n");
371
        final Address ptr = helper.allocateBlock(Extent.fromIntZeroExtend(DEFAULT_HEAP_SIZE));
372
        firstNormalHeap.initialize(ptr, ptr.add(DEFAULT_HEAP_SIZE), slotSize);
373
374
        // Initialize the GC heap
375
        Unsafe.debug("GenHeapManager.initialize(gc heap)\n");
376
        gcHeap = allocHeap(Extent.fromIntZeroExtend(DEFAULT_HEAP_SIZE), false);
377
        gcHeap.append(firstNormalHeap);
378
379
        // Initialize the total heap list.
380
        heapList = gcHeap;
381
        Unsafe.debug("GenHeapManager.initialize(ended)\n");
382
    }
383
384
    /**
385
     * Allocate a new instance for the given class. Not that this method cannot
386
     * be synchronized, since obtaining a monitor might require creating one,
387
     * which in turn needs this method.
388
     * 
389
     * @param vmClass
390
     * @param size
391
     * @return Object
392
     */
393
    @Override
394
    protected Object allocObject(VmClassType<?> vmClass, int size) {
395
        // Make sure the class is initialized
396
        vmClass.initialize();
397
398
        final int alignedSize = ObjectLayout.objectAlign(size);
399
        // final Monitor mon = heapMonitor;
400
401
        VmDefaultHeap heap = currentHeap;
402
        Object result = null;
403
        int oomCount = 0;
404
405
        final Monitor m = heapMonitor;
406
        // final Monitor m = null;
407
        if (m != null) {
408
            m.enter();
409
        }
410
        try {
411
            if (gcActive) {
412
                if ((heapFlags & TRACE_ALLOC) != 0) {
413
                    debug("Using GC Heap type ");
414
                    debug(vmClass.getName());
415
                }
416
                result = gcHeap.alloc(vmClass, alignedSize);
417
                if (result == null) {
418
                    helper.die("Out of GC heap memory.");
419
                }
420
            } else {
421
                while (result == null) {
422
                    // The current heap is full
423
                    if (heap == null) {
424
                        // Unsafe.debug("allocHeap in allocObject(");
425
                        // Unsafe.debug(alignedSize);
426
                        // Unsafe.debug(") ");
427
                        int newHeapSize = DEFAULT_HEAP_SIZE;
428
                        if (size > newHeapSize) {
429
                            // this is a BIG object, try to allocate a new
430
                            // heap
431
                            // only for it
432
                            newHeapSize = size;
433
                        }
434
                        if ((heap = allocHeap(Extent.fromIntZeroExtend(newHeapSize), true)) == null) {
435
                            lowOnMemory = true;
436
                            // It was not possible to allocate another heap.
437
                            // First try to GC, if we've done that before
438
                            // in this allocation, then we're in real panic.
439
                            if (oomCount == 0) {
440
                                oomCount++;
441
                                if ((heapFlags & TRACE_OOM) != 0) {
442
                                    debug("<oom/>");
443
                                }
444
                                gcThread.trigger(true);
445
                                heap = firstNormalHeap;
446
                                currentHeap = firstNormalHeap;
447
                            } else {
448
                                if ((heapFlags & TRACE_OOM) != 0) {
449
                                    debug("Out of memory in allocObject(");
450
                                    debug(size);
451
                                    debug(")");
452
                                }
453
                                throw OOME;
454
                                // Unsafe.die();
455
                            }
456
                        } else {
457
                            // Unsafe.debug("AO.G");
458
                            // We successfully allocated a new heap, set it
459
                            // to current, so we'll use it for the following
460
                            // allocations.
461
                            currentHeap = heap;
462
                        }
463
                    }
464
465
                    result = heap.alloc(vmClass, alignedSize);
466
                    if (result == null) {
467
                        heap = (VmDefaultHeap) heap.getNext();
468
                    }
469
                }
470
                lowOnMemory = false;
471
472
                triggerManager.incrementAllocatedSizeSinceLastGCBy(alignedSize);
473
                //triggerManager.check();
474
            }
475
            vmClass.incInstanceCount();
476
            // Allocated objects are initially black.
477
            VmMagic.setObjectFlags(result, Word.fromIntZeroExtend(ObjectFlags.GC_DEFAULT_COLOR));
478
            //add object to young objects
479
            if (gcManagerLoaded) {
480
                // Unsafe.debug("GenHeapManager.allocObject(young)\n");
481
                gcManager.addToYoungObjects(result);
482
            } else {
483
                // Unsafe.debug("GenHeapManager.allocObject(old)\n");
484
                helper.setOld(result);
485
            }
486
            // Unsafe.debug("GenHeapManager.allocObject(done)\n");
487
            
488
        } finally {
489
            if (m != null) {
490
                m.exit();
491
            }
492
        }
493
494
        return result;
495
    }
496
497
    /**
498
     * Allocate a new heap with a given size. The heap object itself is
499
     * allocated on the new heap, so this method can be called even if all other
500
     * heaps are full.
501
     * 
502
     * @param size
503
     * @return The heap
504
     */
505
    private VmDefaultHeap allocHeap(Extent size, boolean addToHeapList) {
506
        // Unsafe.debug("[begin] allocHeap\n");
507
        final Address start = helper.allocateBlock(size);
508
        // final Address start = MemoryBlockManager.allocateBlock(size);
509
        if (start == null) {
510
            return null;
511
        }
512
        final Address end = start.add(size);
513
        final int slotSize = VmProcessor.current().getArchitecture().getReferenceSize();
514
        final VmDefaultHeap heap = VmDefaultHeap.setupHeap(helper, start, defaultHeapClass,
515
                slotSize);
516
        heap.initialize(start, end, slotSize);
517
518
        if (addToHeapList) {
519
            heapList.append(heap);
520
        }
521
        // Unsafe.debug("[end successful] allocHeap\n");
522
        return heap;
523
    }
524
525
    private void accept(ObjectVisitor visitor, boolean locking) {
526
        VmDefaultHeap heap = firstNormalHeap;
527
        final Word zero = Word.zero();
528
529
        while (heap != null) {
530
            heap.walk(visitor, locking, zero, zero);
531
            heap = heap.getNext();
532
        }
533
    }
534
}