Детальнее вне памяти Java Heap

Китайский Новый год приближается, совсем недавно немного времени , чтобы подготовить по идее записать последнюю колонку, я рекомендую вам прочитать: juejin.im/post/5e19d6 ...

Ухань ест дичь, что несколько глупо [], просить свою мать СПРЯТАННОГО

Начало текста

При запуске программы на Java, Java виртуальной машины потребности в использовании памяти для хранения различных данных. виртуальная машина Java спецификация этих областей памяти называется областью данных времени выполнения:

Внешняя динамическая память, относится к распределению за пределами области памяти Java куч, которая не является управлением JVM, это не повлияет на дс.

Это будет java.nio.DirectByteBuffer, например, для анализа внешней памяти кучи.

    // Primary constructor
    //
    DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }
复制代码

книга памяти

Как можно видеть из метода строительства DirectByteBuffer, кучи пусков выделения памяти на внешней стороне Bits.reserveMemory(size, cap);в.

Биты в классе, смотреть на собственность и несколько членов кучи вне памяти, связанные с:

    private static volatile long maxMemory = VM.maxDirectMemory();
    private static final AtomicLong reservedMemory = new AtomicLong();
    private static final AtomicLong totalCapacity = new AtomicLong();
    private static final AtomicLong count = new AtomicLong();
    private static volatile boolean memoryLimitSet = false;
复制代码

maxMemory

Максимальный объем памяти , выделенный от кучи , установленного пользователем, параметры JVM -XX:MaxDirectMemorySize=конфигурации.

reservedMemory

Он был использован вне кучи объема памяти. Использование AtomicLong для обеспечения безопасности многопоточных.

totalCapacity

Суммарная мощность. Кроме того, использование AtomicLong.

подсчитывать

Рекорд часть за пределами выделения кучи памяти.

memoryLimitSet

Переменная флаг, то летучее ключевое слово. Используется для записи, был ли инициализирован maxMemory поле.


Перед назначением внешней памяти кучи, Jdk с использованием tryReserveMemoryметода достигает оптимистическую блокировку , чтобы обеспечить верхний предел общего числа кучного распределения памяти не больше реальной конструкции.

private static boolean tryReserveMemory(long size, int cap) {

        // -XX:MaxDirectMemorySize limits the total capacity rather than the
        // actual memory usage, which will differ when buffers are page
        // aligned.
        long totalCap;
        while (cap <= maxMemory - (totalCap = totalCapacity.get())) {
            if (totalCapacity.compareAndSet(totalCap, totalCap + cap)) {
                reservedMemory.addAndGet(size);
                count.incrementAndGet();
                return true;
            }
        }

        return false;
    }
复制代码

TryReserveMemory логика также является относительно простым, в то время цикла + CAS используется для обеспечения достаточно свободного места, и обновляет общее пространство, оставшееся пространство, и количество внешней памяти кучи.

Как можно видеть, если CAS не удается, но достаточно мощности, в то время как цикл будет входить в следующий раунд КАС попытки обновления, пока обновление не будет успешной или недостаточной мощности.

В следующем фрагменте кода, написанные комментарии ясно: ссылки в силу при отложенном состоянии и попробуйте еще раз, если ссылка, содержащаяся в соответствующем очистителя, это поможет освободить внешнюю кучу памяти.

        final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();

        // retry while helping enqueue pending Reference objects
        // which includes executing pending Cleaner(s) which includes
        // Cleaner(s) that free direct buffer memory
        while (jlra.tryHandlePendingReference()) {
            if (tryReserveMemory(size, cap)) {
                return;
            }
        }
复制代码

В методе tryHandlePendingReference только четыре строки кода:

        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            public boolean tryHandlePendingReference() {
                return Reference.tryHandlePending(false);
            }
        });
复制代码

Тот, кто видел опорный фантом колонки, чтобы объяснить читатель здесь уже не знает, что делать здесь находится за пределами кучи выпущен:

jlra.tryHandlePendingReference()Jdk фактически вызвать метод и обрабатывать на рассмотрении в состоянии ссылкой тот же самый метод Референс-обработчик поток вызывает.

О Референс-обработчик потока, см: juejin.im/post/5e19d6 ...

Впоследствии JDK будет взять на себя инициативу, чтобы назватьSystem.gc();

В reserveMemory процесса, только внешняя динамической память первого заданное значение не связанные атрибуты, но не выделение реальной памяти.

Выделяет память

После заданного внешнего успеха кучи памяти, JDK вызывает метод небезопасно делать в куче снаружи выделения памяти.

    base = unsafe.allocateMemory(size);
复制代码

allocateMemory является нативным методом для выделения памяти кучи снаружи. В unsafe.cpp, вы можете увидеть его источник:

UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory0(JNIEnv *env, jobject unsafe, jlong size)) {
  size_t sz = (size_t)size;

  sz = align_up(sz, HeapWordSize);
  void* x = os::malloc(sz, mtOther);

  return addr_to_java(x);
} UNSAFE_END
复制代码

Вызов таНос функции выделения памяти и обратный адрес.

Выпуск кучи вне памяти

Мы знаем, в JVM объекте Java является использование gcRoot делать достижимости анализа, чтобы определить, является ли восстановление, в то время как груда внешней памяти не связана с gcRoot, как знать, когда он должен вернуть кучи за пределы памяти его?

Является идеальным решением: DirectByteBuffer, когда соответствующий экземпляр объекта восстанавливается, восстановление синхронизируется внешней памяти кучи.

В это время студенты должны были подумать о finalizeметоде. Это может быть метод Java , C Группа компромисса, когда объект , чтобы быть восстановлены, называют дс. Выглядит немного как деструктор с есть, но , вызвав метод ненадежен и не может гарантировать , что объект будет восстановлен до того гх вызова этого метода.

В JDK, вариант осуществления является использование ссылок , чтобы освободить виртуальную внешнюю кучу памяти. В DirectByteBufferконфигурации метода, есть строка кода следующим образом :

    cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
复制代码

DirectByteBuffer атрибут очистки представляет собой виртуальный справочник.

В Deallocator, тот же самый нативный метод с использованием небезопасным за пределы кучи, чтобы освободить память.

    unsafe.freeMemory(address);
    address = 0;
    Bits.unreserveMemory(size, capacity);```
复制代码
UNSAFE_ENTRY(void, Unsafe_FreeMemory0(JNIEnv *env, jobject unsafe, jlong addr)) {
  void* p = addr_from_java(addr);

  os::free(p);
} UNSAFE_END
复制代码

Точка уборщик вызов находится Референс-обработчик нитка Reference класса.

Когда достижимость изменить объект , на который ссылается, ссылка состояние в состояние ожидания, это будет в tryHandlePendingопределении того , в настоящее время ссылается например процесс очистки, если это так, то метод вызывает его чистая, полная внешняя память куча выздоровела.

другое

Когда предопределенная память, почему должна взять на себя инициативу, чтобы позвонить System.gc

Следующая цитата из блога Cold Spring Son ( lovestblog.cn/blog/2015/0 ... ):

Так как вы хотите назвать System.gc, это, конечно, хочет, чтобы вызвать кучу вне операции ой, чтобы восстановить память, но я хотел бы сказать, находится за пределами памяти кучи не будет причиной каких-либо влияния на дсе (кроме System.gc здесь), но в куче внешнее восстановление памяти на самом деле полагаться на нашем механизме ого, мы должны знать объекты DirectByteBuffer в Java только связан с уровнем и актуальностью этой памяти мы выделяемой из кучи, и он записывает этот базовую память и адрес размер, и с тех пор дс также связано, что дс может работать DirectByteBuffer косвенный объект, соответствующий операции внешней памяти кучи. DirectByteBuffer объекты в создании ассоциации PhantomReference, речь идет о PhantomReference он в основном используется для отслеживания, когда объект восстанавливается, он не может повлиять на дс, дс процесс принятия решений, но если объект найден только в дополнение к ссылаться на него снаружи PhantomReference и нет другого места, чтобы обратиться к нему, и что поставит эту цитату в очередь java.lang.ref.Reference.pending уведомить ReferenceHandler этот демон поток, когда законченный дс для выполнения некоторых пост-обработки, и связанные с ними DirectByteBuffer подкласс PhantomReference PhantomReference в окончательном процессе, будет выпущен в наружной DirectByteBuffer соответствующего блока динамической памяти свободно через интерфейс небезопасных

справочный материал

«Напишите себе виртуальную машину Java»

lovestblog.cn/blog/2015/0...

OpenJDK Источник и примечания

рекомендация

отjuejin.im/post/5e254c1ff265da3e0c4c7ac6