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;
22
23
import java.io.PrintWriter;
24
25
import org.jnode.vm.Unsafe;
26
import org.jnode.vm.VmMagic;
27
import org.jnode.vm.VmSystemObject;
28
import org.jnode.annotation.Inline;
29
import org.jnode.annotation.MagicPermission;
30
import org.jnode.annotation.NoInline;
31
import org.jnode.vm.classmgr.VmArray;
32
import org.jnode.vm.classmgr.VmArrayClass;
33
import org.jnode.vm.classmgr.VmClassLoader;
34
import org.jnode.vm.classmgr.VmClassType;
35
import org.jnode.vm.classmgr.VmNormalClass;
36
import org.jnode.vm.classmgr.VmType;
37
import org.jnode.vm.scheduler.VmProcessor;
38
import org.vmmagic.unboxed.Address;
39
import org.vmmagic.unboxed.Extent;
40
import org.vmmagic.unboxed.ObjectReference;
41
import org.vmmagic.unboxed.Offset;
42
43
/**
44
 * @author Ewout Prangsma (epr@users.sourceforge.net)
45
 */
46
@MagicPermission
47
public abstract class VmHeapManager extends VmSystemObject {
48
49
    public static int TRACE_BASIC = 1;   // enable basic debugging of GC phases
50
    public static int TRACE_ALLOC = 2;   // enable debugging of GC internal allocation
51
    public static int TRACE_TRIGGER = 4;   // enable debugging of GC triggering / scheduling
52
    public static int TRACE_OOM = 8;   // enable debugging of OOM events
53
    public static int TRACE_AD_HOC = 16;  // enable ad hoc debugging
54
    public static int TRACE_FLAGS = 31;  // all of the above
55
56
    /**
57
     * Has this manager been initialized yet
58
     */
59
    private boolean inited = false;
60
61
    /**
62
     * The current debug flags
63
     */
64
    protected int heapFlags = TRACE_BASIC | TRACE_OOM | TRACE_ALLOC;
65
66
    protected OutOfMemoryError OOME;
67
68
    /**
69
     * The memory access helper
70
     */
71
    protected final HeapHelper helper;
72
73
    /**
74
     * Write barrier used
75
     */
76
    private VmWriteBarrier writeBarrier;
77
78
    /**
79
     * Initialize this instance
80
     */
81
    public VmHeapManager(HeapHelper helper) {
82
        this.helper = helper;
83
    }
84
85
    /**
86
     * Initialize this manager. This method is called before the first call to
87
     * allocObject.
88
     */
89
    protected abstract void initialize();
90
91
    /**
92
     * Start any threads needed by this manager.
93
     */
94
    public abstract void start();
95
96
    /**
97
     * Create a new instance of a given class
98
     *
99
     * @param cls
100
     * @return The new instance
101
     */
102
    @Inline
103
    public final Object newInstance(VmType<?> cls) {
104
        return newInstance(cls, ((VmNormalClass<?>) cls).getObjectSize());
105
    }
106
107
    /**
108
     * Create a new instance of a given class with a given object size (in
109
     * bytes)
110
     *
111
     * @param cls
112
     * @param size
113
     * @return The new instance
114
     */
115
    @NoInline
116
    public final Object newInstance(VmType<?> cls, int size) {
117
        testInited();
118
        cls.initialize();
119
        if (cls.isArray()) {
120
            throw new IllegalArgumentException(
121
                "Cannot instantiate an array like this");
122
        }
123
        if (cls.isInterface()) {
124
            throw new IllegalArgumentException(
125
                "Cannot instantiate an interface");
126
        }
127
128
        final Object obj = allocObject((VmNormalClass<?>) cls, size);
129
        if (obj == null) {
130
            if ((heapFlags & TRACE_OOM) != 0) {
131
                debug("Out of memory");
132
            }
133
            throw OOME;
134
        }
135
        return obj;
136
    }
137
138
    /**
139
     * Create a new array
140
     *
141
     * @param arrayCls
142
     * @param elements
143
     * @return The new instance
144
     */
145
    public final Object newArray(VmArrayClass<?> arrayCls, int elements) {
146
        testInited();
147
        if (elements < 0) {
148
            throw new NegativeArraySizeException(
149
                "elements must be greater or equal to 0");
150
        }
151
        if (!arrayCls.isArray()) {
152
            throw new IllegalArgumentException(
153
                "Cannot instantiate a non-array like this ["
154
                    + arrayCls.getName() + "]");
155
        }
156
157
        final int slotSize = VmProcessor.current().getArchitecture()
158
            .getReferenceSize();
159
        final int elemSize;
160
        if (arrayCls.isPrimitiveArray()) {
161
            switch (arrayCls.getSecondNameChar()) {
162
                case 'B': // byte
163
                case 'Z': // boolean
164
                    elemSize = 1;
165
                    break;
166
                case 'C': // char
167
                case 'S': // short
168
                    elemSize = 2;
169
                    break;
170
                case 'I': // int
171
                case 'F': // float
172
                    elemSize = 4;
173
                    break;
174
                case 'D': // double
175
                case 'J': // long
176
                    elemSize = 8;
177
                    break;
178
                default:
179
                    throw new IllegalArgumentException(arrayCls.getName());
180
            }
181
        } else {
182
            elemSize = slotSize;
183
        }
184
185
        arrayCls.incTotalLength(elements);
186
        final Object obj = newArray0(arrayCls, elemSize, elements, slotSize);
187
        if (obj == null) {
188
            if ((heapFlags & TRACE_OOM) != 0) {
189
                debug("Out of memory");
190
            }
191
            throw OOME;
192
        }
193
194
        return obj;
195
    }
196
197
    /**
198
     * Is the system low on memory?
199
     *
200
     * @return boolean
201
     */
202
    public abstract boolean isLowOnMemory();
203
204
    /**
205
     * Start a garbage collection process
206
     */
207
    public abstract void gc();
208
209
    /**
210
     * Create an exact clone of the given object
211
     *
212
     * @param object
213
     * @return Object
214
     */
215
    public final Object clone(Cloneable object) {
216
        testInited();
217
        final VmClassType<?> objectClass = VmMagic.getObjectType(object);
218
        final Address objectPtr = ObjectReference.fromObject(object)
219
            .toAddress();
220
        final int size;
221
        if (objectClass.isArray()) {
222
            final int slotSize = VmProcessor.current().getArchitecture()
223
                .getReferenceSize();
224
            final VmArrayClass<?> arrayClass = (VmArrayClass<?>) objectClass;
225
            final int length = objectPtr.loadInt(Offset
226
                .fromIntSignExtend(VmArray.LENGTH_OFFSET * slotSize));
227
            final int elemSize = arrayClass.getComponentType().getTypeSize();
228
            size = (VmArray.DATA_OFFSET * slotSize) + (length * elemSize);
229
        } else {
230
            final VmNormalClass<?> normalClass = (VmNormalClass<?>) objectClass;
231
            size = normalClass.getObjectSize();
232
        }
233
        final Object newObj = allocObject(objectClass, size);
234
        helper.copy(objectPtr, ObjectReference.fromObject(newObj).toAddress(),
235
            Extent.fromIntZeroExtend(size));
236
        return newObj;
237
    }
238
239
    /**
240
     * Gets the size of free memory in bytes.
241
     *
242
     * @return long
243
     */
244
    public abstract long getFreeMemory();
245
246
    /**
247
     * Gets the size of all memory in bytes.
248
     *
249
     * @return the size of all memory in bytes
250
     */
251
    public abstract long getTotalMemory();
252
253
    /**
254
     * Allocate a new instance for the given class. Not that this method cannot
255
     * be synchronized, since obtaining a monitor might require creating one,
256
     * which in turn needs this method.
257
     *
258
     * @param vmClass The class to allocate
259
     * @param size    The size of the class data, without any header
260
     * @return The allocated object
261
     */
262
    protected abstract Object allocObject(VmClassType<?> vmClass, int size);
263
264
    /**
265
     * Create a new instance of an array with a given class, no constructor will
266
     * be called, the object will be filled with zeros.
267
     *
268
     * @param vmClass
269
     * @param elemSize The length in bytes of each element
270
     * @param elements The number of elements
271
     * @param slotSize
272
     * @return The new instance
273
     */
274
    @Inline
275
    private final Object newArray0(VmClassType vmClass, int elemSize,
276
                                   int elements, int slotSize) {
277
        final int size = (VmArray.DATA_OFFSET * slotSize)
278
            + (elemSize * elements);
279
        final Object array = allocObject(vmClass, size);
280
        final Address arrayPtr = ObjectReference.fromObject(array).toAddress();
281
        arrayPtr.store(elements, Offset.fromIntSignExtend(VmArray.LENGTH_OFFSET
282
            * slotSize));
283
        return array;
284
    }
285
286
    /**
287
     * Is the given address the address of an allocated object on this heap?
288
     *
289
     * @param ptr The address to examine.
290
     * @return True if the given address if a valid starting address of an
291
     *         object, false otherwise.
292
     */
293
    public abstract boolean isObject(Address ptr);
294
295
    @Inline
296
    private final void testInited() {
297
        if (!inited) {
298
            // Unsafe.debug("testInitid.initialize");
299
            initialize();
300
            inited = true;
301
            OOME = new OutOfMemoryError();
302
            // Unsafe.debug("eo-testInitid.initialize");
303
        }
304
    }
305
306
    /**
307
     * @return Returns the helper.
308
     */
309
    public final HeapHelper getHelper() {
310
        return this.helper;
311
    }
312
313
    /**
314
     * Gets the write barrier used by this heap manager (if any).
315
     *
316
     * @return The write barrier, or null if no write barrier is used.
317
     */
318
    public final VmWriteBarrier getWriteBarrier() {
319
        return writeBarrier;
320
    }
321
322
    /**
323
     * Sets the write barrier.
324
     * Call this method in the constructor.
325
     *
326
     * @param barrier
327
     */
328
    protected final void setWriteBarrier(VmWriteBarrier barrier) {
329
        this.writeBarrier = barrier;
330
    }
331
332
    /**
333
     * Print the statics on this object on out.
334
     */
335
    public abstract void dumpStatistics(PrintWriter out);
336
337
    public abstract GCStatistics getStatistics();
338
339
    public abstract HeapStatistics getHeapStatistics();
340
341
    /**
342
     * Create a per processor data structure for use by the heap manager.
343
     *
344
     * @param cpu
345
     */
346
    public abstract Object createProcessorHeapData(VmProcessor cpu);
347
348
    /**
349
     * A new type has been resolved by the VM. Create a new MM type to reflect
350
     * the VM type, and associate the MM type with the VM type.
351
     *
352
     * @param vmType The newly resolved type
353
     */
354
    public abstract void notifyClassResolved(VmType<?> vmType);
355
356
    /**
357
     * Load classes required by this memory manager at build time.
358
     *
359
     * @param loader
360
     */
361
    public abstract void loadClasses(VmClassLoader loader)
362
        throws ClassNotFoundException;
363
364
    /**
365
     * Get this heap's current flags
366
     *
367
     * @return the flags
368
     */
369
    public int getHeapFlags() {
370
        return heapFlags;
371
    }
372
373
    /**
374
     * Set this heap's flags
375
     *
376
     * @param reapFlags the new heap flags
377
     * @return the previous heap flags
378
     */
379
    public int setHeapFlags(int reapFlags) {
380
        int res = this.heapFlags;
381
        this.heapFlags = reapFlags;
382
        return res;
383
    }
384
385
    /**
386
     * Output a debug message controlled by the heap trace flags.
387
     *
388
     * @param text
389
     */
390
    public void debug(String text) {
391
        if ((heapFlags & TRACE_FLAGS) != 0) {
392
            Unsafe.debug(text);
393
        }
394
    }
395
396
    /**
397
     * Output a a number as debug message controlled by the heap trace flags.
398
     *
399
     * @param number
400
     */
401
    protected void debug(int number) {
402
        if ((heapFlags & TRACE_FLAGS) != 0) {
403
            Unsafe.debug(number);
404
        }
405
    }
406
}