Прочитав это неясное управление Netty Memory Memory, я начал плакать!

Netty

инструкция

При изучении Netty ByteBuf можно увидеть повсюду, но как эффективно выделить ByteBuf по-прежнему очень сложно. Распределение памяти в пуле Netty по-прежнему относительно сложно. Многие люди изучали и видели это, но это все еще туманно. в основном, чтобы объяснить: ** Netty выделяет детали объединенной памяти вне кучи, ** я с нетерпением жду, чтобы вы поняли! ! !

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

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

ByteBuf Важность

ByteBuf всегда существовал в Netty, и он необходим для чтения и записи!ByteBuf — это контейнер данных Netty, и эффективное размещение ByteBuf имеет решающее значение!

在这里插入图片描述

Netty читает данные из сокета.

在这里插入图片描述
Netty готова записать данные в сокет.

在这里插入图片描述

Здесь мы видим, затем определите, будут ли данные будут записаны в сокет, если это стек, если не построить объект DirectBuffer, код детализации выглядит следующим образом:

if (msg instanceof ByteBuf) {
            ByteBuf buf = (ByteBuf) msg;
            if (buf.isDirect()) {
                return msg;
            }

            return newDirectBuffer(buf);
        }

在这里插入图片描述

Таким образом, эта статья в основном предназначена для объяснения: ** Netty выделяет детали объединенной памяти вне кучи, ** На самом деле, многие детали выделения памяти кучи схожи.

Примечание:Я не понимал, почему память вне кучи должна быть перенесена в память вне кучи.Я не понимал этого раньше.Внезапно у меня была дискуссия с г-ном Дишеном, и дискуссия и дискуссия прояснились.

Обзор

На этот раз основной разговор идет о распределении памяти в пуле., PooledByteBufAllocator — это запись операции для netty по выделению памяти в пуле.

Его предоставление внешних общих операций API:

在这里插入图片描述

Netty при передаче данных определяет, будет ли куча внешней памяти, если нет, то будет ли она упакована:

在这里插入图片描述

Все здесь мы берем **распределение объединенной памяти вне кучи в качестве примера для иллюстрации этой статьи. ** Выделение памяти кучи в пуле — это фактически тот же процесс.

Давайте посмотрим на демонстрационный пример распределения:

public static void main(String[] args) {
    ByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;

    //tiny规格内存分配 会变成大于等于16的整数倍的数:这里254 会规格化为256
    ByteBuf byteBuf = alloc.directBuffer(254);

	//读写bytebuf
    byteBuf.writeInt(126);
    System.out.println(byteBuf.readInt());
    
    //很重要,内存释放
    byteBuf.release();
}

Далее мы проанализируем его на основе этой простой демонстрации.

Класс входа в операцию

Инициализация PooledByteBufAllocator:

Вы можете увидеть операцию инициализации основного класса после его входа:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Теория распределения является Джемаллок, который можно понимать как реализацию Java-версии JEMALLOC.

PoolThreadCache

在这里插入图片描述

Из приведенного выше рисунка вы можете ясно понять основную структуру данных PoolThreadCache.

В начале в этих кешах нет никакой ценности.Только при вызове свободного освобождения (что будет объяснено в последующем освобождении памяти) ранее выделенный объем памяти будет поставлен в очередь кеша.Фактически, каждый распределение Когда пришло время проверить, есть ли что-то в кеше, если есть прямой возврат, если нет, то будет выполняться обычный процесс выделения (распределение памяти будет объяснено).

Давайте посмотрим на содержимое PoolArena directArena:

Давайте взглянем на структуру PoolArena.

PoolArena

在这里插入图片描述

Основную структуру данных PoolArena можно ясно понять на следующем рисунке.

在这里插入图片描述

В PoolArena структурам, соответствующим PoolChunkList и PoolSubpage, являются PoolChunk и PoolSubpage.Давайте подробно рассмотрим эти две части содержимого.

PoolChunk

В первый раз PoolChunkList, PoolSubpage являются значениями по умолчанию, вам нужно добавить Chunk, Chunk по умолчанию 16M. Внутренняя структура представляет собой полное двоичное дерево, которое будет состоять из 4096 узлов, 2048 конечных узлов (размер каждого листового узла страницы равен 8 КБ), размер памяти нелистового узла равен размеру памяти левого поддерева правого поддерева плюс объем памяти. размер .

Полная структура бинарного дерева выглядит следующим образом:

在这里插入图片描述

Это полное бинарное дерево представлено массивом в java.

Единственное предостережение заключается в том, что индексы начинаются с 1, а не с 0.

在这里插入图片描述

depthMapЗначение не меняется после инициализации,memoryMapЗначение изменяется при распределении узла.

在这里插入图片描述

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

Если значение массива depthMap равно 0, это означает, что может быть выделено 16 МБ пространства, если оно равно 1, это означает, что может быть выделено 8 МБ, если оно равно 2, это означает, что может быть выделено 4 МБ, если оно равно 3, это означает, что может быть выделено 2 МБ. выделено ……………… Если это 11, ему может быть выделено 8 КБ пространства.

Если узел был выделен, его можно установить равным 12.

Как определить размер потребности выделить в глубину?

Если выделяемой памяти меньше 8к после нормализации, то ее можно выделить на 8к (то есть глубина 11).

Если это 8k или больше, чем 8k, глубину можно найти с помощью следующего кода:

int d = maxOrder - (log2(normCapacity) - pageShifts);

Зная глубину, как найти этот узел? ? ?

在这里插入图片描述

Найдя узел, сначала отобразите занятый узел, а затем обновите родителя родительского узла родительского узла.Родительский узел выглядит следующим образом:

在这里插入图片描述

SubpagePool

在这里插入图片描述

На приведенном выше рисунке показана структура памяти SubpagePool. Когда мы выделяем страницу, мы знаем, выделена ли она в соответствии со значением memoryMap, так что, если это subpagePool?

SubpagePool делится на 2 категории: tinySubpagePools и smallSubpagePools, размер тоже для приведенного выше рисунка, каждая категория имеет фиксированный размер, если выделить размер 256b, то страница будет 8k, 8*1024/256 = 32 блока. Итак, как указать, что каждый из них также выделен?

private final long[] bitmap;

Поскольку тип long занимает 64 бита, нам нужно представить здесь только 32 бита, поэтому мы можем использовать тип long, 1 для каждого бита двоичного файла указывает на то, что он использовался, а 0 указывает на то, что он не использовался.

Поскольку подстранице нужно не только найти узел полного бинарного дерева, но также нужно знать номер и номер длинного, так что это сложнее:

在这里插入图片描述

Первые 32 бита типа long используются для представления первых битов подстраницы, а последние 32 бита используются для представления узла полного двоичного дерева, которое идеально.

Выделить ядра

Запись распределения: ByteBuf byteBuf = alloc.directBuffer(256);

Следуйте за кодом:

在这里插入图片描述

Посмотрим: PooledByteBuf buf = newByteBuf(maxCapacity);

Создайте объект PooledByteBuf. Наконец, возвращается объект PooledByteBuf.

Давайте посмотрим на структуру наследования классов:

在这里插入图片描述

All ByteBuf byteBuf = alloc.directBuffer(256); В этом предложении нет ничего плохого, и об ошибке не будет сообщено.

Давайте взглянем на подробную реализацию newByteBuf(maxCapacity):

在这里插入图片描述

Здесь используется технология пула объектов Recycler, реализованная Netty. Дизайн Recycler тоже очень деликатный.Вы можете написать статью о Recycler в будущем.Сегодня не в фокусе.Пока мы знаем, что из-за стоимости выделения объекта POLLEDBYTebuf нам нужно часто использовать объект PolledBytebuf, и если у вас есть какие-либо требования к производительности, то технология Pihua — хороший выбор.(Например, пул потоков, пул соединений с базой данных и т. д., которые мы использовали ранее, аналогичны.) Технология объединения** пулов в определенной степени снижает нагрузку на производительность, вызванную частым созданием объектов. ** На самом деле, эта подобная идея очень распространена (например, стоимость запросов к базе данных высока, кэширование в Redis, идея та же), и вы также можете испытать (PoolThreadCache) в продолжении этого статья.

Через PooledByteBuf buf = newByteBuf(maxCapacity); достаточно получить исходный объект.

Суть распределения: allocate(cache, buf, reqCapacity);

在这里插入图片描述

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

Детали выделения шага: посмотрите, какой тип страницы или подстраницы необходимо выделить. Если это подстраница, проверьте, является ли она tinySubpagePools или smallSubpagePools, найдите соответствующий слот и посмотрите, есть ли какая-либо доступная PoolSubpage в связанном списке. выход.Если нет, то нужно сначала выделить страницу.Проверить есть ли подходящая по чанку.Если есть подходящая,разместить страницу на этих существующих чанках(то же самое и при размещении страниц). )

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

свободное ядро

Запись выпуска: byteBuf.release();

Следуйте коду:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

С помощью этого кода мы помещаем это в соответствующую очередь:

在这里插入图片描述

Кэш хранится в соответствующей очереди Cache.

Адрес исходного кода статьи на github:nettydemo, или официальный аккаунт отвечает на «Netty», чтобы получить адрес исходного кода.


Если вы чувствуете себя вознагражденным после прочтения, пожалуйста, поставьте лайк, подпишитесь и добавьте официальную учетную запись [Ingenuity Zero], чтобы узнать больше захватывающей истории! ! !