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 org.jnode.annotation.MagicPermission;
24
import org.jnode.vm.Unsafe;
25
import org.jnode.vm.Vm;
26
import org.jnode.vm.VmArchitecture;
27
import org.jnode.vm.VmMagic;
28
import org.jnode.vm.VmSystemObject;
29
import org.jnode.vm.classmgr.VmNormalClass;
30
import org.jnode.vm.classmgr.VmType;
31
import org.jnode.vm.memmgr.GCStatistics;
32
import org.jnode.vm.memmgr.HeapHelper;
33
import org.jnode.vm.memmgr.VmHeapManager;
34
import org.vmmagic.pragma.Uninterruptible;
35
import org.vmmagic.unboxed.Address;
36
import org.vmmagic.unboxed.ObjectReference;
37
import org.vmmagic.unboxed.Offset;
38
import org.vmmagic.unboxed.Word;
39
40
/**
41
 * 
42
 * @author ismael
43
 * 
44
 */
45
@MagicPermission
46
final class GenGCManager extends VmSystemObject implements Uninterruptible {
47
48
    private static final int MAX_AGE_FOR_YOUNG = 5;
49
50
    /**
51
     * The heap manager
52
     */
53
    private GenHeapManager heapManager;
54
55
    /**
56
     * The mark stack
57
     */
58
    private final GenGCStack markStack;
59
60
    /**
61
     * An object visitor used for marking
62
     */
63
    private final GenGCMarkVisitor markVisitor;
64
65
    /**
66
     * An object visitor used for sweeping
67
     */
68
    private final GenGCMarkForSweepVisitor markForSweepVisitor;
69
70
    /**
71
     * An object visitor used for sweeping
72
     */
73
    private final GenGCSweepVisitor sweepVisitor;
74
75
    /**
76
     * An object visitor used for setting objects to GC colour white
77
     */
78
    private final GenGCSetWhiteVisitor setWhiteVisitor;
79
80
    /**
81
     * The object visitor that verifies the correctness of the object tree
82
     */
83
    private final GenGCVerifyVisitor verifyVisitor;
84
85
    /**
86
     * My statistics
87
     */
88
    private final GenGCStatistics stats;
89
90
    /**
91
     * The low level helper
92
     */
93
    private HeapHelper helper;
94
95
    /**
96
     * The write barrier
97
     */
98
    private GenWriteBarrier writeBarrier;
99
100
    /**
101
     * Debug mode?
102
     */
103
    private final boolean debug;
104
105
    private GenGCYellowObjectManager yellowObjectManager;
106
107
    //private List<Object> youngObj = new ArrayList<Object>(4096);
108
    private GenGCStack youngObj = new GenGCStack();
109
    private GenGCStack youngTemp = new GenGCStack();
110
111
    public GenGCManager(GenHeapManager genHeapManager, VmArchitecture arch) {
112
        debug = true || Vm.getVm().isDebugMode();
113
        heapManager = genHeapManager;
114
        helper = heapManager.getHelper();
115
        yellowObjectManager = heapManager.getYellowObjectManager();
116
        writeBarrier = (GenWriteBarrier) heapManager.getWriteBarrier();
117
        markStack = new GenGCStack();
118
        markVisitor = new GenGCMarkVisitor(markStack, heapManager, arch);
119
        setWhiteVisitor = new GenGCSetWhiteVisitor(helper);
120
        verifyVisitor = new GenGCVerifyVisitor(heapManager, arch);
121
        markForSweepVisitor = new GenGCMarkForSweepVisitor(yellowObjectManager);
122
        sweepVisitor = new GenGCSweepVisitor(yellowObjectManager);
123
        stats = new GenGCStatistics();
124
        yellowObjectManager.setStats(stats);
125
    }
126
127
    /**
128
     * Do a garbage collection cycle for young objects or a full collection
129
     */
130
    final void gc() {
131
        Unsafe.debug("\n[Enter] GenGCManager.gc()\n");
132
        majorGC();
133
        Unsafe.debug("\n[Leave] GenGCManager.gc()\n");
134
    }
135
136
    /**
137
     * Do a garbage collection cycle for young objects
138
     */
139
    final void minorGC() {
140
        final long beginTime = GenGCStatistics.currentMillis();
141
        Unsafe.debug("\n[Enter] GenGCManager.minorGC()\n");
142
        // Prepare
143
        // final VmBootHeap bootHeap = heapManager.getBootHeap();
144
        final VmDefaultHeap firstHeap = heapManager.getHeapList();
145
146
        // final boolean locking = (writeBarrier != null);
147
        final boolean verbose = (heapManager.getHeapFlags() & VmHeapManager.TRACE_BASIC) != 0;
148
149
        helper.stopThreadsAtSafePoint();
150
        // heapManager.setGcActive(true);
151
        try {
152
            // Pre-GC verification
153
            if (debug) {
154
                if (false) {
155
                    // Turn back to true in case of problems in the GC.
156
                    if (verbose) {
157
                        heapManager.debug("<preverify/>");
158
                    }
159
                    // verify(bootHeap, firstHeap);
160
                }
161
            }
162
163
            // Analyse young objects
164
            if (verbose) {
165
                heapManager.debug("<collectYoungObjects/>");
166
            }
167
            collectYoungObjects(firstHeap);
168
169
            // Verification
170
            if (debug) {
171
                if (false) {
172
                    // Turn back to true in case of problems in the GC.
173
                    if (verbose) {
174
                        heapManager.debug("<verify/>");
175
                    }
176
                    // verify(bootHeap, firstHeap);
177
                }
178
            }
179
        } finally {
180
            // heapManager.setGcActive(false);
181
            // heapManager.resetCurrentHeap();
182
            final long duration = GenGCStatistics.getDuration(beginTime);
183
            stats.addMinorDuration(duration);
184
            helper.restartThreads();
185
        }
186
187
    }
188
189
    /**
190
     * Do a major garbage collection cycle (clean the full memory with
191
     * mark&sweep).
192
     */
193
    final void majorGC() {
194
        final long beginTime = GenGCStatistics.currentMillis();
195
        Unsafe.debug("\n[Enter] GenGCManager.majorGC()\n");
196
        // Prepare
197
        final VmBootHeap bootHeap = heapManager.getBootHeap();
198
        final VmDefaultHeap firstHeap = heapManager.getHeapList();
199
200
        final boolean locking = (writeBarrier != null);
201
        final boolean verbose = (heapManager.getHeapFlags() & VmHeapManager.TRACE_BASIC) != 0;
202
        helper.stopThreadsAtSafePoint();
203
        heapManager.setGcActive(true);
204
        try {
205
            // Pre-GC verification
206
            if (debug) {
207
                if (false) {
208
                    // Turn back to true in case of problems in the GC.
209
                    if (verbose) {
210
                        heapManager.debug("<preverify/>");
211
                    }
212
                    verify(bootHeap, firstHeap);
213
                }
214
            }
215
216
            // Mark
217
            // helper.stopThreadsAtSafePoint();
218
            // heapManager.setGcActive(true);
219
            try {
220
                if (verbose) {
221
                    heapManager.debug("<mark/>");
222
                }
223
                markHeap(bootHeap, firstHeap, locking);
224
            } finally {
225
                // heapManager.setGcActive(false);
226
                // helper.restartThreads();
227
            }
228
229
            // Mark for Sweep
230
            if (verbose) {
231
                heapManager.debug("<markForSweep/>");
232
            }
233
            markForSweep(firstHeap);
234
235
            // Verification
236
            if (debug) {
237
                if (false) {
238
                    // Turn back to true in case of problems in the GC.
239
                    if (verbose) {
240
                        heapManager.debug("<verify/>");
241
                    }
242
                    verify(bootHeap, firstHeap);
243
                }
244
            }
245
        } finally {
246
            heapManager.setGcActive(false);
247
            heapManager.resetCurrentHeap();
248
            final long duration = GenGCStatistics.getDuration(beginTime);
249
            stats.addMajorDuration(duration);
250
            helper.restartThreads();
251
        }
252
253
        // Start the finalization process
254
        // heapManager.triggerFinalization();
255
    }
256
257
    void cleanup() {
258
        final VmBootHeap bootHeap = heapManager.getBootHeap();
259
        final VmDefaultHeap firstHeap = heapManager.getHeapList();
260
        cleanup(bootHeap, firstHeap);
261
    }
262
263
    void sweep() {
264
        // Unsafe.debug("GenGCManager.sweep()\n");
265
        final long beginTime = GenGCStatistics.currentMillis();
266
        VmDefaultHeap heap = heapManager.getHeapList();
267
        sweepVisitor.resetMarkedObjectsSize();
268
        while (heap != null && yellowObjectManager.areObjectsYellow()) {
269
            heap.lock();
270
            sweepVisitor.setCurrentHeap(heap);
271
            heap.walk(sweepVisitor, false, Word.zero(), Word.zero());
272
            heap.unlock();
273
            heap = heap.getNext();
274
        }
275
        final long duration = GenGCStatistics.getDuration(beginTime);
276
        stats.addSweepDuration(duration);
277
        stats.lastFreedBytes = sweepVisitor.getMarkedObjectsSize();
278
    }
279
280
    // Young collection
281
    private final void collectYoungObjects(VmDefaultHeap firstHeap) {
282
        Object obj = null;
283
        boolean removed = false;
284
        long markedObjects = 0L;
285
        long markedObjectsSize = 0L;
286
        youngTemp.reset();
287
        //for (int i = 0, size = youngObj.size(); i < size; i++) {
288
        while(!youngObj.isEmpty()) {
289
            removed = false;
290
            obj = youngObj.pop();//get(i);
291
            if(helper.getNbRefs(obj) == 0) {
292
                walkObject(obj);
293
                yellowObjectManager.markObjectAsYellow(obj);
294
                markedObjectsSize += VmDefaultHeap.getObjectSize(obj);
295
                markedObjects++;
296
                removed = true;
297
            }
298
            else {
299
                int newAge = helper.incrementAge(obj);
300
                if (newAge > MAX_AGE_FOR_YOUNG) {
301
                    helper.setOld(obj);
302
                    removed = true;
303
                }
304
            }
305
            if(!removed) {
306
                youngTemp.push(obj);
307
            }
308
           /* if (removed) {
309
                youngObj.remove(i);
310
                i--;
311
                size = youngObj.size();
312
                removed = false;
313
            }*/
314
        }
315
        //swap young objects stacks
316
        GenGCStack s = youngObj;
317
        youngObj = youngTemp;
318
        youngTemp = s;
319
        //
320
        stats.lastMinorMarkedObjects = markedObjects;
321
        stats.lastMinorMarkedObjectsSize = markedObjectsSize;
322
    }
323
324
    private final boolean walkObject(Object object) {
325
        VmType<?> vmClass = null;
326
        try {
327
            vmClass = VmMagic.getObjectType(object);
328
        } catch (NullPointerException ex) {
329
            //
330
        }
331
        if (vmClass == null) {
332
            Unsafe.debug("Oops vmClass == null");
333
            helper.die("vmClass == null in walkObject()");
334
        } else if (vmClass.isArray()) {
335
            if (!vmClass.isPrimitiveArray()) {
336
                decrementRefCountForArrayElems(object);
337
            }
338
        } else {
339
            return decrementRefCountForFields(object, (VmNormalClass<?>) vmClass);
340
        }
341
        return false;
342
    }
343
    
344
    private void decrementRefCountForArrayElems(Object object) {
345
        try {
346
            final Object[] arr = (Object[]) object;
347
            final int length = arr.length;
348
            for (int i = 0; i < length; i++) {
349
                final Object child = arr[i];
350
                if (child != null) {
351
                    helper.decRef(child);                    
352
                }
353
            }
354
        } catch (ClassCastException ex) {
355
            throw ex;
356
        }
357
    }
358
    
359
    private final boolean decrementRefCountForFields(Object object, VmNormalClass<?> vmClass) {
360
        final int[] referenceOffsets = vmClass.getReferenceOffsets();
361
        final int cnt = referenceOffsets.length;
362
        if (cnt == 0) {
363
            return false;
364
        }
365
366
        boolean res = false;
367
        final int size = vmClass.getObjectSize();
368
        final Address objAddr = ObjectReference.fromObject(object).toAddress();
369
        for (int i = 0; i < cnt; i++) {
370
            final int offset = referenceOffsets[i];
371
            if ((offset < 0) || (offset >= size)) {
372
                Unsafe.debug("reference offset out of range!");
373
                Unsafe.debug(vmClass.getName());
374
                helper.die("Class internal error");
375
            } else {
376
                final ObjectReference child = objAddr.loadObjectReference(Offset
377
                        .fromIntZeroExtend(offset));
378
                if (child != null) {
379
                    helper.decRef(child);
380
                    res = true;
381
                }
382
            }
383
        }
384
        return res;
385
    }
386
387
    // Full collection
388
    /**
389
     * Mark all live objects in the heap.
390
     * 
391
     * @param bootHeap
392
     * @param firstHeap
393
     */
394
    private final void markHeap(VmBootHeap bootHeap, VmDefaultHeap firstHeap, boolean locking) {
395
        final long beginTime = GenGCStatistics.currentMillis();
396
        stats.lastMarkIterations = 0;
397
398
        if (writeBarrier != null) {
399
            writeBarrier.setActive(true);
400
        }
401
402
        long markedObjects = 0;
403
        boolean firstIteration = true;
404
        boolean wbChanged = false;
405
        do {
406
            // Do an iteration reset
407
            stats.lastMarkIterations++;
408
            markStack.reset();
409
            if (writeBarrier != null) {
410
                writeBarrier.resetChanged();
411
            }
412
            markVisitor.reset();
413
            markVisitor.setRootSet(true);
414
            // Mark all roots
415
            helper.visitAllRoots(markVisitor, heapManager);
416
            // statics.walk(markVisitor, resolver);
417
            // helper.visitAllThreads(threadMarkVisitor);
418
            // Mark every object in the rootset
419
            // bootHeap.walk(markVisitor, locking, 0, 0);
420
            if (!firstIteration) {
421
                // If there was an overflow in the last iteration,
422
                // we must also walk through the other heap to visit
423
                // all grey objects, since we must still mark
424
                // their children.
425
                markVisitor.setRootSet(false);
426
                bootHeap.walk(markVisitor, locking, Word.zero(), Word.zero());
427
                VmDefaultHeap heap = firstHeap;
428
                while ((heap != null) && (!markStack.isOverflow())) {
429
                    heap.walk(markVisitor, locking, Word.zero(), Word.zero());
430
                    heap = heap.getNext();
431
                }
432
            }
433
            // Test for an endless loop
434
            if ((markVisitor.getMarkedObjects() == 0) && markStack.isOverflow()) {
435
                // Oops... an endless loop
436
                Unsafe.debug("Endless loop in markHeap.... going to die");
437
                helper.die("GCManager.markHeap");
438
            }
439
            // Do some cleanup
440
            markedObjects += markVisitor.getMarkedObjects();
441
            firstIteration = false;
442
            if (writeBarrier != null) {
443
                wbChanged = writeBarrier.isChanged();
444
            }
445
        } while (markStack.isOverflow() || wbChanged);
446
447
        if (writeBarrier != null) {
448
            writeBarrier.setActive(false);
449
        }
450
        
451
        final long duration = GenGCStatistics.getDuration(beginTime);
452
        stats.addMarkDuration(duration);
453
        stats.lastMajorMarkedObjects = markedObjects;
454
    }
455
456
    /**
457
     * Sweep all heaps for dead objects.
458
     * 
459
     * @param firstHeap
460
     */
461
    private void markForSweep(VmDefaultHeap firstHeap) {
462
        final long beginTime = GenGCStatistics.currentMillis();
463
        VmDefaultHeap heap = firstHeap;
464
        markForSweepVisitor.resetMarkedObjectsSize();
465
        while (heap != null) {
466
            heap.lock();
467
            heap.walk(markForSweepVisitor, false, Word.zero(), Word.zero());
468
            heap.unlock();
469
            heap = heap.getNext();
470
        }
471
        final long duration = GenGCStatistics.getDuration(beginTime);
472
        stats.addMarkForSweepDuration(duration);
473
        stats.lastMajorMarkedObjectsSize = markForSweepVisitor.getMarkedObjectsSize();
474
    }
475
476
    /**
477
     * Mark all objects white, so a next GC action is valid
478
     * 
479
     * @param bootHeap
480
     * @param firstHeap
481
     */
482
    private void cleanup(VmBootHeap bootHeap, VmDefaultHeap firstHeap) {
483
        final long beginTime = GenGCStatistics.currentMillis();
484
        bootHeap.walk(setWhiteVisitor, true, Word.zero(), Word.zero());
485
        VmDefaultHeap heap = firstHeap;
486
        while (heap != null) {
487
            heap.defragment();
488
            // heap.walk(setWhiteVisitor, true);
489
            heap = heap.getNext();
490
        }
491
        final long duration = GenGCStatistics.getDuration(beginTime);
492
        stats.addCleanupDuration(duration);
493
    }
494
495
    /**
496
     * Verify all heaps.
497
     * 
498
     * @param bootHeap
499
     * @param firstHeap
500
     */
501
    private void verify(VmBootHeap bootHeap, VmDefaultHeap firstHeap) {
502
        final Word zero = Word.zero();
503
        verifyVisitor.reset();
504
        bootHeap.walk(verifyVisitor, true, zero, zero);
505
        VmDefaultHeap heap = firstHeap;
506
        while (heap != null) {
507
            heap.walk(verifyVisitor, true, zero, zero);
508
            heap = heap.getNext();
509
        }
510
        final int errorCount = verifyVisitor.getErrorCount();
511
        if (errorCount > 0) {
512
            Unsafe.debug(errorCount);
513
            Unsafe.debug(" verify errors. ");
514
            helper.die("Corrupted heap");
515
        }
516
    }
517
518
    public GCStatistics getStatistics() {
519
        return stats;
520
    }
521
    
522
    protected void addToYoungObjects(Object obj) {
523
        youngObj.push(obj);
524
    }
525
}