Runtime.avt

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Реализация среды исполнения языка программирования
    Объектно-ориентированный продвинутый векторный транслятор

    Copyright © 2021, 2024, 2026 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package avt.lang;

import avt.lang.array.*;
import platform.dependent.*;
import platform.independent.osservices.*;

public class Runtime(Object)
{
    static final long INSTANCE_ALIGN    = 0x40;
    static final long ARRAY_HEADER_SIZE = 0x40;

    private static int count = 0;               /* все кучи: общее количество куч */
    private static long collectedCounter = 0;   /* счётчики: собранное сборщиком мусора количество байт */
    private static long allocatedBytes = 0;     /* счётчики: занятое количество байт */
    private static long totalBytes = 0;         /* счётчики: всего выделено байт */
    private static long[] pointers;             /* недостижимые объекты: указатели на объекты */
    private static long[] counters;             /* недостижимые объекты: счётчики ссылок на объекты */
    private static long[] sizes;                /* каталоги куч: размеры объектов */
    private static Heap[] heaps;                /* все кучи: объекты куч */
    private static Heap[][] catalogues;         /* каталоги куч: объекты каталогов */
    private static Mutex memoryOperation;
    private static OutOfMemoryError errorOutOfMemory;
    private static InstantiationError errorInstantiation;

    private static Runtime instance;

    private static final long CATALOGUE_HEADER_SIZE = 2 * ARRAY_HEADER_SIZE;

    private static final long MAX_ALLOCATED_BYTES_HEAP      = 1L << 38;
    private static final long MAX_ALLOCATED_BYTES_CATALOGUE = 1L << 37;

    public static Runtime getInstance() { return instance; }

    protected static void finalize() {
        if(Thread.callerTypeTrace() != instance.getClass()) return;
        instance.beforeDestruction();
    }

    protected static boolean initialize(long servicesPtr, Class servicesType) {
        if(instance != null || servicesPtr == 0 || servicesType == null || servicesType.isAbstract() || !PlatformServices.class.isAssignableFrom(servicesType)) return false;
        Object instance = initInstance(servicesPtr, servicesType, servicesType.instanceSize, -1);
        instance.defaultConstructor();
        if(!initHeap()) return false;
        initErrors();
        instance.afterConstruction();
        return true;
    }

    protected static boolean isIntersectRegions(long2 region0, long2 region1) { return Long2.max(region0, region1)[0] < Long2.min(region0, region1)[1]; }

    package static void throwOutOfMemoryError() {
        throw errorOutOfMemory;
    }

    /* размещение инстанций */

    package static Object allocateInstance(Class instanceType, long instanceSize, int arrayLength) {
        if(allocatedBytes + (instanceSize += -instanceSize & (INSTANCE_ALIGN - 1)) > MAX_ALLOCATED_BYTES_HEAP)
        {
            throw errorOutOfMemory;
        }
        if(instanceType == null || instanceType.isPrimitive() || instanceType.isAbstract())
        {
            throw errorInstantiation;
        }
        int cindex = indexOfSize(instanceSize);
        if(cindex < 0)
        {
            throw errorOutOfMemory;
        }
        Object result;
        boolean multiThreaded = instance.fldMultiThreaded;
        if(multiThreaded) memoryOperation.lock();
        try
        {
            Heap[] heaps = catalogues[cindex];
            if(heaps == null && (heaps = createCatalogue(cindex)) == null)
            {
                throw errorOutOfMemory;
            }
            int hindex = getActiveHeapIndex(heaps);
            Heap heap = hindex < heaps.length ? heaps[hindex] : createHeap(cindex);
            if(heap == null)
            {
                throw errorOutOfMemory;
            }
            result = initInstance(heap.getNextInstancePointer(), instanceType, instanceSize, arrayLength);
            heap.captureInstancePointer();
            allocatedBytes += heap.objectSize;
            if(heap.isFull()) modActiveHeapIndex(heaps);
        } finally
        {
            if(multiThreaded) memoryOperation.unlock();
        }
        return result;
    }

    package static Object allocateStruct(Class instanceType, long instanceSize, long structDataPtr) {
        if(allocatedBytes + (instanceSize += -instanceSize & (INSTANCE_ALIGN - 1)) > MAX_ALLOCATED_BYTES_HEAP)
        {
            throw errorOutOfMemory;
        }
        if(instanceType == null || instanceType.isPrimitive() || instanceType.isAbstract())
        {
            throw errorInstantiation;
        }
        int cindex = indexOfSize(instanceSize);
        if(cindex < 0)
        {
            throw errorOutOfMemory;
        }
        Object result;
        boolean multiThreaded = instance.fldMultiThreaded;
        if(multiThreaded) memoryOperation.lock();
        try
        {
            Heap[] heaps = catalogues[cindex];
            if(heaps == null && (heaps = createCatalogue(cindex)) == null)
            {
                throw errorOutOfMemory;
            }
            int hindex = getActiveHeapIndex(heaps);
            Heap heap = hindex < heaps.length ? heaps[hindex] : createHeap(cindex);
            if(heap == null)
            {
                throw errorOutOfMemory;
            }
            result = initStruct(heap.getNextInstancePointer(), instanceType, instanceSize, structDataPtr);
            heap.captureInstancePointer();
            allocatedBytes += heap.objectSize;
            if(heap.isFull()) modActiveHeapIndex(heaps);
        } finally
        {
            if(multiThreaded) memoryOperation.unlock();
        }
        return result;
    }

    package static Object allocateArray(Class instanceType, long instanceSize, int arrayLength, long arrayDataPtr, Struct arrayParentStruct) {
        if(allocatedBytes + (instanceSize += -instanceSize & (INSTANCE_ALIGN - 1)) > MAX_ALLOCATED_BYTES_HEAP)
        {
            throw errorOutOfMemory;
        }
        if(instanceType == null || instanceType.isPrimitive() || instanceType.isAbstract())
        {
            throw errorInstantiation;
        }
        int cindex = indexOfSize(instanceSize);
        if(cindex < 0)
        {
            throw errorOutOfMemory;
        }
        Object result;
        boolean multiThreaded = instance.fldMultiThreaded;
        if(multiThreaded) memoryOperation.lock();
        try
        {
            Heap[] heaps = catalogues[cindex];
            if(heaps == null && (heaps = createCatalogue(cindex)) == null)
            {
                throw errorOutOfMemory;
            }
            int hindex = getActiveHeapIndex(heaps);
            Heap heap = hindex < heaps.length ? heaps[hindex] : createHeap(cindex);
            if(heap == null)
            {
                throw errorOutOfMemory;
            }
            result = initArray(heap.getNextInstancePointer(), instanceType, instanceSize, arrayLength, arrayDataPtr, arrayParentStruct);
            heap.captureInstancePointer();
            allocatedBytes += heap.objectSize;
            if(heap.isFull()) modActiveHeapIndex(heaps);
        } finally
        {
            if(multiThreaded) memoryOperation.unlock();
        }
        return result;
    }

    static native long toPointer(Object instanceRef);

    static native Object toInstance(long instancePtr);

    static native Object initInstance(long instancePtr, Class instanceType, long instanceSize, int arrayLength);

    static native Object initStruct(long instancePtr, Class instanceType, long instanceSize, long structDataPtr);

    static native Object initArray(long instancePtr, Class instanceType, long instanceSize, int arrayLength, long arrayDataPtr, Struct arrayParentStruct);

    /* сбор мусора */

    static void collectGarbage() {
        boolean unstable;
        GarbageCollectorThread thread = instance.fldGCThread;
        for(int hindex = 0; hindex < count; hindex++)
        {
            if(thread != null && thread.terminated) return;
            Heap heap = heaps[hindex];
            heap.initForGC();
            for(int ilength = heap.length, int iindex = 0; iindex < ilength; iindex++)
            {
                if(thread != null && thread.terminated) return;
                long instancePtr = heap[iindex];
                if(refsToInstance(instancePtr)[0] != 0) heap.makeAttainedInstancePointer(instancePtr);
            }
        }
        do for(unstable = false, int hindex = 0; hindex < count; hindex++)
        {
            if(thread != null && thread.terminated) return;
            Heap heap = heaps[hindex];
            if(heap.length <= 0) heap.initForGC();
            int attained;
            for(int iindex = heap.visited; iindex < (attained = heap.attained); iindex++)
            {
                if(thread != null && thread.terminated) return;
                for(long instancePtr = heap[iindex], int tlength = getInstanceRefsCount(instancePtr), int tindex = -1; tindex < tlength; tindex++)
                {
                    if(thread != null && thread.terminated) return;
                    long targetPtr = getInstanceRefAtIndex(instancePtr, tindex);
                    Heap anot = heap.isOwnInstancePointer(targetPtr) ? heap : heapOf(targetPtr);
                    if(anot == null) continue;
                    if(anot.length <= 0) anot.initForGC();
                    anot.makeAttainedInstancePointer(targetPtr);
                    unstable = true;
                }
            }
            heap.visited = attained;
        } while(unstable);
        int plength = 0;
        int hlength = count;
        for(int hindex = 0; hindex < hlength; hindex++)
        {
            if(thread != null && thread.terminated) return;
            Heap heap = heaps[hindex];
            plength += heap.length - heap.attained;
        }
        if(!ensurePointersCapacity(plength))
        {
            throw errorOutOfMemory;
        }
        for(plength = 0, pointers.length = pointers.capacity, long previousPtr = Long.MIN_VALUE, int hindex = 0; hindex < hlength; hindex++)
        {
            if(thread != null && thread.terminated) return;
            Heap heap = heaps[hindex];
            for(int ilength = heap.length, int iindex = heap.attained; iindex < ilength; iindex++)
            {
                if(thread != null && thread.terminated) return;
                long instancePtr = heap[iindex];
                if(previousPtr >= instancePtr) break;
                pointers[plength++] = previousPtr = instancePtr;
            }
            heap.finForGC();
        }
        for(long refCount, pointers.length = plength, Array.fill(counters, 0, plength, 0); ; )
        {
            unstable = false;
            for(int pindex = 0; pindex < plength; pindex++)
            {
                if(thread != null && thread.terminated) return;
                if(counters[pindex] < 0) continue;
                for(long instancePtr = pointers[pindex], int tlength = getInstanceRefsCount(instancePtr), int tindex = -1; tindex < tlength; tindex++)
                {
                    if(thread != null && thread.terminated) return;
                    int aindex = indexOfPointer(getInstanceRefAtIndex(instancePtr, tindex));
                    if(aindex >= 0 && (refCount = counters[aindex]) >= 0) counters[aindex] = refCount + 1;
                }
            }
            for(int pindex = 0; pindex < plength; pindex++)
            {
                if(thread != null && thread.terminated) return;
                if((refCount = counters[pindex]) < 0 || refsToInstance(pointers[pindex]) == new long2 { 0, refCount }) continue;
                counters[pindex] = -1;
                unstable = true;
            }
            if(!unstable) break;
            for(int pindex = 0; pindex < plength; pindex++)
            {
                if(thread != null && thread.terminated) return;
                if(counters[pindex] > 0) counters[pindex] = 0;
            }
        }
        for(int pindex = 0; pindex < plength; pindex++)
        {
            if(thread != null && thread.terminated) return;
            if(counters[pindex] >= 0) invokeBeforeDestruction(pointers[pindex]);
        }
        for(int pindex = 0; pindex < plength; pindex++)
        {
            if(thread != null && thread.terminated) return;
            if(counters[pindex] >= 0) deallocateInstance(pointers[pindex]);
        }
    }

    private static void deallocateInstance(long instancePtr) {
        Heap heap = heapOf(instancePtr);
        if(heap == null) return;
        finInstance(instancePtr);
        int cindex = heap.catalogueIndex;
        int hindex = heap.heapIndex;
        boolean multiThreaded = instance.fldMultiThreaded;
        if(multiThreaded) memoryOperation.lock();
        try
        {
            Heap[] heaps = catalogues[cindex];
            heap.releaseInstancePointer(instancePtr);
            long objectSize = heap.objectSize;
            collectedCounter += objectSize;
            allocatedBytes -= objectSize;
            if(getActiveHeapIndex(heaps) > hindex) setActiveHeapIndex(heaps, hindex);
        } finally
        {
            if(multiThreaded) memoryOperation.unlock();
        }
    }

    private static void invokeBeforeDestruction(long instancePtr) {
        Object instance = toInstance(instancePtr);
        if(instance.getClass().isArray()) return;
        try
        {
            instance.beforeDestruction();
        } catch(Throwable exception) {  }
    }

    private static native void finInstance(long instancePtr);

    private static native int getInstanceRefsCount(long instancePtr);

    private static native long getInstanceRefAtIndex(long instancePtr, int index);

    private static native long2 refsToInstance(long instancePtr);

    /* инициализация кучи */

    private static boolean initHeap() {
        /* расчёт размеров */
        final int sizesLength = 62;
        final int cataloguesLength = sizesLength;
        final long sizesUnalignedSize = ((long) sizesLength << 3) + ARRAY_HEADER_SIZE;
        final long cataloguesUnalignedSize = ((long) cataloguesLength << 3) + ARRAY_HEADER_SIZE;
        final long pointersSize = MemoryRegion.PAGE_SIZE >> 1;
        final long countersSize = pointersSize;
        final long sizesSize = sizesUnalignedSize + (-sizesUnalignedSize & (INSTANCE_ALIGN - 1));
        final long heapsSize = MemoryRegion.PAGE_SIZE;
        final long cataloguesSize = sizesSize;
        /* выделение памяти под массивы */
        PlatformServices services = PlatformServices.getInstance();
        long2 region = services.allocateRegion(new long2 { 0, pointersSize + countersSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(region == 0) return false;
        long pointersPtr = (long) region;
        long countersPtr = pointersPtr + pointersSize;
        region = services.allocateRegion(new long2 { 0, sizesSize + cataloguesSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(region == 0) return false;
        long sizesPtr = (long) region;
        long cataloguesPtr = sizesPtr + sizesSize;
        region = services.allocateRegion(new long2 { 0, heapsSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(region == 0) return false;
        long heapsPtr = (long) region;
        /* инициализация массивов */
        pointers = (long[]) initInstance(pointersPtr, long[].class, pointersSize, (int) (pointersSize - ARRAY_HEADER_SIZE >> 3));
        counters = (long[]) initInstance(countersPtr, long[].class, countersSize, (int) (countersSize - ARRAY_HEADER_SIZE >> 3));
        sizes = (long[]) initInstance(sizesPtr, long[].class, sizesUnalignedSize, sizesLength);
        heaps = (Heap[]) initInstance(heapsPtr, Heap[].class, heapsSize, (int) (heapsSize - ARRAY_HEADER_SIZE >> 3));
        catalogues = (Heap[][]) initInstance(cataloguesPtr, Heap[][].class, cataloguesUnalignedSize, cataloguesLength);
        /* заполнение массивов начальными данными */
        for(int index = 0; index < 4; index++) sizes[index] = 64L * (index + 1);
        for(int index = 5; index < sizesLength; index += 2) sizes[index] = 1L << (index >> 1) + 7;
        for(int index = 4; index < sizesLength; index += 2)
        {
            long size = (long) ((1L << (index >> 1) + 6) * 0x1.6a09e667f3bcc908p+0R);
            sizes[index] = size + (-size & (INSTANCE_ALIGN - 1));
        }
        /* создание нескольких куч */
        for(int index = 0; index < 4; index++) if(createCatalogue(index) == null || createHeap(index) == null) return false;
        return true;
    }

    /* инициализация объектов ошибок */

    private static void initErrors() {
        /* создание необходимых объектов */
        memoryOperation = PlatformServices.getInstance().newMutex();
        errorOutOfMemory = new OutOfMemoryError();
        errorInstantiation = new InstantiationError();
        /* инициализация строк */
        String.initialize();
        errorOutOfMemory.message = package.getResourceString("!machine-error.out-of-memory");
        errorInstantiation.message = package.getResourceString("!reflective-error.instantiation");
    }

    /* недостижимые объекты */

    private static boolean ensurePointersCapacity(int minCapacity) {
        long[] oldPointersRef = pointers;
        int oldPointersCapacity = oldPointersRef.capacity;
        if(minCapacity < oldPointersCapacity) return true;
        long[] oldCountersRef = counters;
        long oldPointersPtr = toPointer(oldPointersRef);
        long oldCountersPtr = toPointer(oldCountersRef);
        long oldPointersSize = ((long) oldPointersCapacity << 3) + ARRAY_HEADER_SIZE;
        long oldCountersSize = oldPointersSize;
        long newPointersSize = ((long) minCapacity << 3) + ARRAY_HEADER_SIZE;
        long newCountersSize = newPointersSize += -newPointersSize & ((MemoryRegion.PAGE_SIZE >> 1) - 1L);
        PlatformServices services = PlatformServices.getInstance();
        long2 region = services.allocateRegion(new long2 { 0, newPointersSize + newCountersSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(region == 0) return false;
        long newPointersPtr = (long) region;
        long newCountersPtr = newPointersPtr + newPointersSize;
        pointers = (long[]) initInstance(newPointersPtr, long[].class, newPointersSize, (int) (newPointersSize - ARRAY_HEADER_SIZE >> 3));
        counters = (long[]) initInstance(newCountersPtr, long[].class, newCountersSize, (int) (newCountersSize - ARRAY_HEADER_SIZE >> 3));
        finInstance(oldPointersPtr);
        finInstance(oldCountersPtr);
        oldPointersRef = null;
        oldCountersRef = null;
        services.deallocateRegion(new long2 { oldPointersPtr, oldPointersSize + oldCountersSize }, MemoryRegion.NO_ACCESS);
        return true;
    }

    private static int indexOfPointer(long instancePtr) {
        for(int bidx = 0, int eidx = pointers.length - 1; bidx <= eidx; )
        {
            int midx = bidx + eidx >>> 1;
            long currentPtr = pointers[midx];
            if(instancePtr == currentPtr) return midx;
            if(instancePtr < currentPtr)
            {
                eidx = midx - 1;
                continue;
            }
            bidx = midx + 1;
        }
        return -1;
    }

    /* все кучи */

    private static boolean expandHeaps() {
        Heap[] oldRef = heaps;
        long oldPtr = toPointer(oldRef);
        long oldLength = oldRef.capacity;
        long oldSize = (oldLength << 3) + ARRAY_HEADER_SIZE;
        long newSize = oldSize << 1;
        PlatformServices services = PlatformServices.getInstance();
        long2 region = services.allocateRegion(new long2 { 0, newSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(region == 0) return false;
        long newPtr = (long) region;
        Heap[] newRef = (Heap[]) initInstance(newPtr, Heap[].class, newSize, (int) (newSize - ARRAY_HEADER_SIZE >> 3));
        copy(oldPtr + ARRAY_HEADER_SIZE, newPtr + ARRAY_HEADER_SIZE, oldLength);
        heaps = newRef;
        finInstance(oldPtr);
        oldRef = null;
        services.deallocateRegion(new long2 { oldPtr, oldSize }, MemoryRegion.NO_ACCESS);
        return true;
    }

    private static Heap heapOf(long instancePtr) {
        for(int bidx = 0, int eidx = count - 1; bidx <= eidx; )
        {
            int midx = bidx + eidx >>> 1;
            Heap heap = heaps[midx];
            long2 bnd = heap.regionBounds;
            if(instancePtr < bnd[0])
            {
                eidx = midx - 1;
                continue;
            }
            if(instancePtr < bnd[1]) return heap;
            bidx = midx + 1;
        }
        return null;
    }

    /* каталоги куч */

    private static void modActiveHeapIndex(Heap[] catalogue) {
        int index = getActiveHeapIndex(catalogue) + 1;
        for(int length = catalogue.length; index < length; index++) if(!catalogue[index].isFull()) break;
        setActiveHeapIndex(catalogue, index);
    }

    private static native void setActiveHeapIndex(Heap[] catalogue, int newActiveHeapIndex);

    private static native int getActiveHeapIndex(Heap[] catalogue);

    private static int indexOfSize(long instanceSize) {
        for(int bidx = 0, int eidx = sizes.length - 1; bidx <= eidx; )
        {
            int midx = bidx + eidx >>> 1;
            if(instanceSize > sizes[midx])
            {
                bidx = midx + 1;
                continue;
            }
            int pidx = midx - 1;
            if(pidx < 0 || instanceSize > sizes[pidx]) return midx;
            eidx = pidx;
        }
        return -1;
    }

    private static Heap[] expandCatalogue(int index) {
        Heap[] oldRef = catalogues[index];
        long oldPtr = toPointer(oldRef);
        long oldLength = oldRef.capacity;
        long oldSize = (oldLength << 3) + CATALOGUE_HEADER_SIZE;
        long newSize = oldSize << 1;
        PlatformServices services = PlatformServices.getInstance();
        long2 region = services.allocateRegion(new long2 { 0, newSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(region == 0) return null;
        long newPtr = (long) region;
        Heap[] newRef = (Heap[]) initArray(newPtr, Heap[].class, newSize, (int) (newSize - CATALOGUE_HEADER_SIZE >> 3), newPtr + CATALOGUE_HEADER_SIZE, null);
        copy(oldPtr + CATALOGUE_HEADER_SIZE, newPtr + CATALOGUE_HEADER_SIZE, oldLength);
        setActiveHeapIndex(newRef, getActiveHeapIndex(oldRef));
        newRef.length = oldRef.length;
        catalogues[index] = newRef;
        finInstance(oldPtr);
        oldRef = null;
        services.deallocateRegion(new long2 { oldPtr, oldSize }, MemoryRegion.NO_ACCESS);
        return newRef;
    }

    private static Heap[] createCatalogue(int index) {
        /*
            Начальная ёмкость каталога куч —
            в зависимости от размера объекта:
            +---------+----------+-----------+
            |  Индекс |          | Начальная |
            | размера |   Размер |  ёмкость, |
            | в sizes |  объекта |      в КБ |
            +---------+----------+-----------+
            |    ≤ 19 |  ≤ 64 КБ |        64 |
            |      20 | 90,56 КБ |        48 |
            |      21 |   128 КБ |        32 |
            |      22 | 181,1 КБ |        24 |
            |      23 |   256 КБ |        16 |
            |      24 | 362,1 КБ |        12 |
            |      25 |   512 КБ |         8 |
            |      26 | 724,1 КБ |         8 |
            |    ≥ 27 |   ≥ 1 МБ |         4 |
            +---------+----------+-----------+
        */
        final long KB = 1L << 10;
        long catalogueSize = 64 * KB;
        if(index > 19) switch(index)
        {
            case 20:
            {
                catalogueSize = 48 * KB;
                break;
            }
            case 21:
            {
                catalogueSize = 32 * KB;
                break;
            }
            case 22:
            {
                catalogueSize = 24 * KB;
                break;
            }
            case 23:
            {
                catalogueSize = 16 * KB;
                break;
            }
            case 24:
            {
                catalogueSize = 12 * KB;
                break;
            }
            case 25:
            case 26:
            {
                catalogueSize = 8 * KB;
                break;
            }
            default:
            {
                catalogueSize = 4 * KB;
            }
        }
        long2 region = PlatformServices.getInstance().allocateRegion(new long2 { 0, catalogueSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(region == 0) return null;
        long cataloguePtr = (long) region;
        Heap[] result = (Heap[]) initArray(cataloguePtr, Heap[].class, catalogueSize, (int) (catalogueSize - CATALOGUE_HEADER_SIZE >> 3), cataloguePtr + CATALOGUE_HEADER_SIZE, null);
        result.length = 0;
        catalogues[index] = result;
        return result;
    }

    private static Heap createHeap(int index) {
        int maxHeaps;
        int maxObjects;
        long objectSize = sizes[index];
        if(index > 45)
        {
            maxHeaps = 1;
            maxObjects = (int) (MAX_ALLOCATED_BYTES_CATALOGUE / objectSize);
        } else
        {
            maxHeaps = (int) (MAX_ALLOCATED_BYTES_CATALOGUE / (objectSize << 8));
            maxObjects = 256;
        }
        Heap[] catalogue = catalogues[index];
        int length = catalogue.length;
        int capacity = catalogue.capacity;
        if(length >= (index <= 19 || maxHeaps <= capacity ? maxHeaps : capacity) || length >= capacity && (catalogue = expandCatalogue(index)) == null) return null;
        PlatformServices services = PlatformServices.getInstance();
        long addedBytes = objectSize * maxObjects;
        long2 heapBounds = services.allocateRegion(new long2 { 0, addedBytes }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
        if(heapBounds == 0) return null;
        long heapPtr;
        long2 heapRegion = 0;
        if((length & 0x3f) > 0)
        {
            heapPtr = toPointer(catalogue[length - 1]) + Heap.INSTANCE_SIZE;
        } else
        {
            /*
                Количество памяти,
                выделяемой под кучи за один раз —
                в зависимости от размера объекта:
                +---------+----------+------------+
                |  Индекс |          | Количество |
                | размера |   Размер |    памяти, |
                | в sizes |  объекта |       в КБ |
                +---------+----------+------------+
                |    ≤ 33 |   ≤ 8 МБ |         64 |
                |      34 | 11,31 МБ |         48 |
                |      35 |    16 МБ |         32 |
                |      36 | 22,63 МБ |         24 |
                |      37 |    32 МБ |         16 |
                |      38 | 45,25 МБ |         12 |
                |      39 |    64 МБ |          8 |
                |      40 | 90,51 МБ |          8 |
                |    ≥ 41 | ≥ 128 МБ |          4 |
                +---------+----------+------------+
            */
            final long KB = 1L << 10;
            long heapsSize = 64 * KB;
            if(index > 33) switch(index)
            {
                case 34:
                {
                    heapsSize = 48 * KB;
                    break;
                }
                case 35:
                {
                    heapsSize = 32 * KB;
                    break;
                }
                case 36:
                {
                    heapsSize = 24 * KB;
                    break;
                }
                case 37:
                {
                    heapsSize = 16 * KB;
                    break;
                }
                case 38:
                {
                    heapsSize = 12 * KB;
                    break;
                }
                case 39:
                case 40:
                {
                    heapsSize = 8 * KB;
                    break;
                }
                default:
                {
                    heapsSize = 4 * KB;
                }
            }
            heapRegion = services.allocateRegion(new long2 { 0, heapsSize }, MemoryRegion.READABLE | MemoryRegion.WRITEABLE);
            if(heapRegion == 0)
            {
                services.deallocateRegion(heapBounds, MemoryRegion.NO_ACCESS);
                return null;
            }
            heapPtr = (long) heapRegion;
        }
        int position = count;
        if(position >= heaps.capacity && !expandHeaps())
        {
            if(heapRegion != 0) services.deallocateRegion(heapRegion, MemoryRegion.NO_ACCESS);
            services.deallocateRegion(heapBounds, MemoryRegion.NO_ACCESS);
            return null;
        }
        int eidx = position - 1;
        long heapStart = (long) heapBounds;
        if(eidx >= 0 && heapStart < heaps[eidx].regionPointer)
        {
            int bidx = 0;
            do
            {
                int midx = bidx + eidx >>> 1;
                if(heapStart >= heaps[midx].regionPointer)
                {
                    bidx = midx + 1;
                    continue;
                }
                int pidx = midx - 1;
                if(pidx < 0 || heapStart > heaps[pidx].regionPointer)
                {
                    Array.copy(heaps, midx, heaps, midx + 1, position - midx);
                    position = midx;
                    break;
                }
                eidx = pidx;
            } while(bidx <= eidx);
        }
        Heap result = (Heap) initInstance(heapPtr, Heap.class, Heap.INSTANCE_SIZE, -1);
        result.defaultConstructor();
        result.afterConstruction();
        result.init(index, length, heapStart, objectSize, maxObjects);
        catalogue.length = length + 1;
        catalogue[length] = result;
        heaps[position] = result;
        count++;
        totalBytes += addedBytes;
        return result;
    }

    /* вспомогательные методы */

    private static native void copy(long srcPtr, long dstPtr, long length);

    /* регионы памяти */

    private static native long2 getCodeMemoryRegionPointers();

    private static native long2 getDataMemoryRegionPointers();

    private static long2 getObjectMemoryRegionPointers(Object obj) {
        long ptr = toPointer(obj);
        return new long2 { ptr, ptr + obj.getClass().instanceSize };
    }

    private static long2 getArrayMemoryRegionPointers(MutableMeasureable array) {
        long ptr = toPointer(array);
        return new long2 { ptr, ptr + ((long) array.capacity << 3) + ARRAY_HEADER_SIZE };
    }

    private static long2 getCatalogueMemoryRegionPointers(Heap[] array) {
        long ptr = toPointer(array);
        return new long2 { ptr, ptr + ((long) array.capacity << 3) + CATALOGUE_HEADER_SIZE };
    }

    /* инстанционные члены */

    private boolean fldMultiThreaded;
    private Mutex fldSystemOperation;
    private Object fldGCMonitor;
    private GarbageCollectorThread fldGCThread;

    protected () {
        if(instance != null)
        {
            throw new SecurityException(package.getResourceString("security.runtime.construct"));
        }
        instance = this;
    }

    public void exit(int exitCode) {
        throw new SecurityException(package.getResourceString("security.runtime.exit"));
    }

    public final void gc() {
        synchronized with(fldGCMonitor)
        {
            notify();
        }
    }

    public final long freeMemory() { return totalBytes - allocatedBytes; }

    public final long totalMemory() { return totalBytes; }

    public final long collectedBytes() { return collectedCounter; }

    protected void afterConstruction() {
        fldSystemOperation = PlatformServices.getInstance().newMutex();
        Thread.initialize();
        with((Thread) (fldGCThread = new GarbageCollectorThread(fldGCMonitor = new Object())))
        {
            priority = MIN_PRIORITY;
            name = "GC";
            start();
        }
    }

    protected void beforeDestruction() {
        GarbageCollectorThread thread = fldGCThread;
        if(thread != null) thread.terminate();
    }

    protected void nowMultiThreaded() {  }

    protected boolean isCanonicalPointer(long ptr) { return ptr >= 0xffff800000000000L && ptr <= 0x00007fffffffffffL; }

    protected boolean isProtectedMemory(long2 regionBounds) {
        if(
            isIntersectRegions(regionBounds, getCodeMemoryRegionPointers()) ||
            isIntersectRegions(regionBounds, getDataMemoryRegionPointers()) ||
            isIntersectRegions(regionBounds, getObjectMemoryRegionPointers(this)) ||
            isIntersectRegions(regionBounds, getArrayMemoryRegionPointers(pointers)) ||
            isIntersectRegions(regionBounds, getArrayMemoryRegionPointers(counters)) ||
            isIntersectRegions(regionBounds, getArrayMemoryRegionPointers(sizes)) ||
            isIntersectRegions(regionBounds, getArrayMemoryRegionPointers(heaps)) ||
            isIntersectRegions(regionBounds, getArrayMemoryRegionPointers(catalogues))
        ) return true;
        for(int clength = catalogues.length, int cindex = 0; cindex < clength; cindex++)
        {
            Heap[] heaps = catalogues[cindex];
            if(heaps == null) continue;
            if(isIntersectRegions(regionBounds, getCatalogueMemoryRegionPointers(heaps))) return true;
            for(int hlength = heaps.length, int hindex = 0; hindex < hlength; hindex++)
            {
                Heap heap = heaps[hindex];
                if(heap == null) continue;
                if((hindex & 0x3f) <= 0)
                {
                    final long KB = 1L << 10;
                    long heapsSize = 64 * KB;
                    if(cindex > 33) switch(cindex)
                    {
                        case 34:
                        {
                            heapsSize = 48 * KB;
                            break;
                        }
                        case 35:
                        {
                            heapsSize = 32 * KB;
                            break;
                        }
                        case 36:
                        {
                            heapsSize = 24 * KB;
                            break;
                        }
                        case 37:
                        {
                            heapsSize = 16 * KB;
                            break;
                        }
                        case 38:
                        {
                            heapsSize = 12 * KB;
                            break;
                        }
                        case 39:
                        case 40:
                        {
                            heapsSize = 8 * KB;
                            break;
                        }
                        default:
                        {
                            heapsSize = 4 * KB;
                        }
                    }
                    long heapsStart = toPointer(heap);
                    if(isIntersectRegions(regionBounds, new long2 { heapsStart, heapsStart + heapsSize })) return true;
                }
                if(isIntersectRegions(regionBounds, heap.regionBounds)) return true;
            }
        }
        return false;
    }

    protected final boolean isMultiThreaded() { return fldMultiThreaded; }

    package final void setMultiThreaded() {
        if(!fldMultiThreaded)
        {
            fldMultiThreaded = true;
            nowMultiThreaded();
        }
    }

    package final boolean isTerminated() { return fldGCThread.terminated; }

    package final Mutex systemOperation { read = fldSystemOperation }
}

class GarbageCollectorThread(Thread)
{
    private boolean fldLaunched;
    private boolean fldTerminated;
    private final Object fldMonitor;

    public (Object monitor) { fldMonitor = monitor; }

    public void run() {
        if(!fldLaunched)
        {
            fldLaunched = true;
            do
            {
                try
                {
                    Thread.clean();
                    String.clean();
                    Runtime.collectGarbage();
                    pause();
                } catch(Exception exception) {  }
            } while(!fldTerminated);
        }
    }

    public void interruptio() {
        throw new SecurityException(package.getResourceString("security.thread.interruptio.gc"));
    }

    public void terminate() {
        synchronized with(fldMonitor)
        {
            fldTerminated = true;
            notify();
        }
        if(Thread.current() != this) try
        {
            join();
        } catch(Exception exception) {  }
    }

    public boolean terminated { read = fldTerminated }

    private void pause() throws InterruptedException {
        synchronized with(fldMonitor)
        {
            wait(6000L);
        }
    }
}

class Heap(Object)
{
    public static final long INSTANCE_SIZE = 0x0400L;

    private static final long8 OBJECTS_START = new long8 {
        0x0706050403020100L, 0x0f0e0d0c0b0a0908L, 0x1716151413121110L, 0x1f1e1d1c1b1a1918L, 0x2726252423222120L, 0x2f2e2d2c2b2a2928L, 0x3736353433323130L, 0x3f3e3d3c3b3a3938L
    };
    private static final long8 OBJECTS_STEP = new long8 {
        0x4040404040404040L, 0x4040404040404040L, 0x4040404040404040L, 0x4040404040404040L, 0x4040404040404040L, 0x4040404040404040L, 0x4040404040404040L, 0x4040404040404040L
    };

    private static native void initObjectsPointers(byte[] objPtrs);

    private int fldCatalogueIndex;
    private int fldHeapIndex;
    private int fldObjectsAllocated;
    private int fldObjectsLength;
    private int fldGCVisited;
    private int fldGCAttained;
    private int fldGCLength;
    private int fldLock;
    private long fldObjectSize;
    private long fldRegionPointer;
    private long2 fldRegionBounds;
    private byte[] fldObjectsPointers;
    private byte[] fldGCPointers;
    private byte[] fldGCAttainability;

    public () {  }

    public void initForGC() {
        clearAttainability();
        enterLock();
        try
        {
            copyPointers();
            fldGCVisited = 0;
            fldGCAttained = 0;
            fldGCLength = fldObjectsAllocated;
        } finally
        {
            leaveLock();
        }
    }

    public void finForGC() {
        fldGCVisited = 0;
        fldGCAttained = 0;
        fldGCLength = 0;
    }

    public void makeAttainedInstancePointer(long instancePtr) {
        int length = fldGCLength;
        if(length <= 0) return;
        int comp = (int) ((instancePtr - fldRegionPointer) / fldObjectSize);
        if(getAttainability(comp)) return;
        byte[] pointers = fldGCPointers;
        int attained = fldGCAttained;
        label0: if((pointers[attained] & 0xffi) != comp)
        {
            for(int bidx = attained + 1, int eidx = length - 1; bidx <= eidx; )
            {
                int midx = bidx + eidx >>> 1;
                int curr = pointers[midx] & 0xffi;
                if(comp == curr)
                {
                    Array.copy(pointers, attained, pointers, attained + 1, midx - attained);
                    pointers[attained] = (byte) comp;
                    break label0;
                }
                if(comp < curr)
                {
                    eidx = midx - 1;
                    continue;
                }
                bidx = midx + 1;
            }
            return;
        }
        setAttainability(comp);
        fldGCAttained = attained + 1;
    }

    public void releaseInstancePointer(long instancePtr) {
        byte[] pointers = fldObjectsPointers;
        int allocated = fldObjectsAllocated - 1;
        int comp = (int) ((instancePtr - fldRegionPointer) / fldObjectSize);
        if((pointers[allocated] & 0xffi) != comp)
        {
            for(int bidx = 0, int eidx = allocated - 1; bidx <= eidx; )
            {
                int midx = bidx + eidx >>> 1;
                int curr = pointers[midx] & 0xffi;
                if(comp == curr)
                {
                    Array.copy(pointers, midx + 1, pointers, midx, allocated - midx);
                    break;
                }
                if(comp < curr)
                {
                    eidx = midx - 1;
                    continue;
                }
                bidx = midx + 1;
            }
            pointers[allocated] = (byte) comp;
        }
        fldObjectsAllocated = allocated;
    }

    public void captureInstancePointer() {
        byte[] pointers = fldObjectsPointers;
        int allocated = fldObjectsAllocated;
        int comp = pointers[allocated] & 0xffi;
        int eidx = allocated - 1;
        boolean lock = false;
        try
        {
            if(eidx >= 0 && comp < (pointers[eidx] & 0xffi))
            {
                int bidx = 0;
                do
                {
                    int midx = bidx + eidx >>> 1;
                    if(comp >= (pointers[midx] & 0xffi))
                    {
                        bidx = midx + 1;
                        continue;
                    }
                    int pidx = midx - 1;
                    if(pidx < 0 || comp > (pointers[pidx] & 0xffi))
                    {
                        lock = true;
                        enterLock();
                        Array.copy(pointers, midx, pointers, midx + 1, allocated - midx);
                        pointers[midx] = (byte) comp;
                        break;
                    }
                    eidx = pidx;
                } while(bidx <= eidx);
            }
            fldObjectsAllocated = allocated + 1;
        } finally
        {
            if(lock) leaveLock();
        }
    }

    public boolean isFull() { return fldObjectsAllocated >= fldObjectsLength; }

    public boolean isOwnInstancePointer(long instancePtr) { return (instancePtr -= fldRegionPointer) >= 0 && instancePtr < fldObjectSize * fldObjectsLength; }

    public long getNextInstancePointer() { return fldRegionPointer + fldObjectSize * (fldObjectsPointers[fldObjectsAllocated] & 0xffL); }

    public int length { read = fldGCLength }

    public int attained { read = fldGCAttained }

    public int visited { read = fldGCVisited, write = fldGCVisited }

    public int catalogueIndex { read = fldCatalogueIndex }

    public int heapIndex { read = fldHeapIndex }

    public long objectSize { read = fldObjectSize }

    public long regionPointer { read = fldRegionPointer }

    public long2 regionBounds { read = fldRegionBounds }

    public long operator [](int index) { return fldRegionPointer + fldObjectSize * (fldGCPointers[index] & 0xffL); }

    void init(int catalogueIndex, int heapIndex, long regionPtr, long objectSize, int objectsLength) {
        long thisPtr = Runtime.toPointer(this);
        byte[] objPtrs = (byte[]) Runtime.initArray(thisPtr + 0x0140L, byte[].class, Runtime.ARRAY_HEADER_SIZE, objectsLength, thisPtr + 0x0200L, null);
        byte[] gcPtrs = (byte[]) Runtime.initArray(thisPtr + 0x0180L, byte[].class, Runtime.ARRAY_HEADER_SIZE, objectsLength, thisPtr + 0x0300L, null);
        byte[] gcAtt = (byte[]) Runtime.initArray(thisPtr + 0x01c0L, byte[].class, Runtime.ARRAY_HEADER_SIZE, 32, thisPtr + 0x0120L, null);
        initObjectsPointers(objPtrs);
        fldCatalogueIndex = catalogueIndex;
        fldHeapIndex = heapIndex;
        fldObjectsLength = objectsLength;
        fldObjectSize = objectSize;
        fldRegionPointer = regionPtr;
        fldRegionBounds = new long2 { regionPtr, regionPtr + objectSize * objectsLength };
        fldObjectsPointers = objPtrs;
        fldGCPointers = gcPtrs;
        fldGCAttainability = gcAtt;
    }

    private native void enterLock();

    private void leaveLock() { fldLock = 0; }

    private native void copyPointers();

    private native void clearAttainability();

    private native void setAttainability(int compressedPtr);

    private native boolean getAttainability(int compressedPtr);
}