Сколько памяти занимает учебник

Обновлено: 06.07.2024

Мне было интересно, какие коллекции сколько съедают дополнительной памяти при хранении объектов. Я провёл замеры накладных расходов для популярных коллекций, предполагающих хранение однотипных элементов (то есть списки и множества) и свёл результаты на общий график. Вот картинка для 64-битной Hotspot JVM (Java 1.6):


А вот для 32-битной Hotspot:

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

Материалы и методы

Измерения проводились для стандартных коллекций Java из java.util и java.util.concurrent, а также для обычного Java-массива. Коллекции IdentityHashSet и ConcurrentHashSet созданы из соответствующих Map с помощью метода Collections.newSetFromMap(). Все коллекции инициализировались по умолчанию без предварительного указания количества элементов (ну кроме массива, для которого это обязательно), а затем заполнялись тестовыми объектами с помощью метода add (а для массива просто выполнялось присваивание). Было создано около 500 коллекций каждого типа с разным количеством элементов от 1 до 10000. В качестве элементов использовались 10000 различных строк случайной длины, состоящих из случайных букв. В принципе сами элементы влияют лишь на ConcurrentHashSet, да и то незначительно, поэтому графики будут выглядеть похожим образом для любых данных.


После заполнения массивов снимался дамп памяти с процесса и анализировался с помощью Eclipse Memory Analyzer, который очень правильно подсчитал Retained set каждой из коллекций, не включив туда сами объекты, а включив только накладные расходы. Выглядит это, например, так:

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

Видно, что каждая коллекция имеет нижнюю границу накладных расходов и чем больше элементов, тем чаще она оказывается близко к ней. Однако для некоторых коллекций нельзя сказать, что функция накладных расходов сходится к этой границе в математическом смысле. Например, ArrayList хотя всё чаще оказывается у значения 8 байт (для 64bit), но он продолжает прыгать к значению 12 байт при каждом новом выделении памяти.

Интересно, что графики для 32bit и для 64bit очень похожи: для большинства коллекций графики отличаются вдвое кроме двух исключений: ConcurrentSkipListSet и LinkedList. Рассмотрим каждую коллекцию по отдельности и разберёмся, почему для неё график именно такой.

Array

Самый простой вариант — массив, для которого наперёд известно число элементов. В нём на каждый объект хранится ссылка: 4 (8) байт (в скобках значение для 64-битной JVM), кроме того хранится длина массива — int, 4 байта, и дескриптор объекта — 8 (16) байт. Вдобавок каждый объект выравнивается по 8 байтам из-за чего массивы с чётным числом элементов на 32bit теряют по 4 байта. Итог: по 4 (8) байт на объект плюс постоянная от 12 до 24 байт.

Пустой массив занимает 16 (24) байт.

ArrayList

Тут почти то же самое с небольшим отличием: так как заранее число элементов в массиве неизвестно, массив выделяется с запасом (по умолчанию на 10 элементов) и при необходимости расширяется чуть больше, чем в полтора раза:

Поэтому график прыгает до 6 (12) байт. Константа также немного больше: 40 (64) байта, так как помимо массива есть ещё сам объект ArrayList, в котором хранится ссылка на массив, фактический размер списка и количество модификаций (для выкидывания ConcurrentModificationException). Тем не менее, это самый экономный способ хранить однотипные данные, если вы заранее не знаете, сколько их будет.

Конструированный по умолчанию ArrayList без элементов занимает 80 (144) байта.

LinkedList

Для связанного списка картинка похожа на массив: стремится к асимптоте по гиперболе. Для каждого элемента списка создаётся по одному служебному объекту типа java.util.LinkedList.Entry. Каждый из этих объектов содержит по три ссылки (на сам элемент списка, на предыдущий и последующий Entry), при этом из-за выравнивания в 32bit теряется по 4 байта, поэтому в итоге требуется 24 (40) байт на каждый Entry. Константа включает в себя дескриптор объекта LinkedList, головной Entry и ссылку на него, размер списка и количество модификаций и равна 48 (80) байт. Столько же занимает пустой список, так как никакой памяти про запас здесь, конечно, не выделяется.

TreeSet

Вообще все используемые множества построены на основе Map. Отдельная реализация в некоторых случаях могла бы быть несколько компактнее, но общий код, конечно, важнее.

График тоже похож на LinkedList и массив. Для каждого элемента создаётся ветвь дерева java.util.TreeMap.Entry, которая содержит пять ссылок: ключ, значение, родитель, левый и правый ребёнок. Кроме них хранится булева переменная, указывающая цвет ветки, красный или чёрный (см. красно-чёрное дерево). Отдельная булева переменная занимает 4 байта, поэтому запись целиком занимает 32 (64) байта.

Постоянные данных в TreeMap такие: ссылка на компаратор, ссылка на корень дерева (в отличие от головоного Entry в LinkedList корень используется по назначению — ссылается на реальный элемент множества), ссылки на entrySet, navigableKeySet, descendingMap (эти объекты создаются по первому требованию), размер и количество модификаций. С дескриптором объекта TreeMap получается 48 (80) байт. Сам TreeSet добавляет только свой дескриптор и ссылку на TreeMap. В сумме выходит 64 (104) байта. Пустое множество весит столько же. Кстати, расход памяти не зависит от степени сбалансированности дерева.

HashSet

HashSet основан на HashMap, который устроен несколько хитрее, чем TreeMap. Для каждого элемента заводится запись java.util.HashMap.Entry, содержащая ссылки на ключ, значение, следующую Entry (если несколько записей попало в одну ячейку хэш-таблицы), а также само значение хэша. Всего Entry весит 24 (48) байт.

Помимо Entry есть ещё и хэш-таблица, со ссылками на Entry, которая содержит 16 элементов изначально и увеличивается вдвое, когда количество элементов превышает 75% от её размера (75% — это значение loadFactor по умолчанию, его можно указать в конструкторе). То есть при конструировании по умолчанию увеличение таблицы происходит, когда количество элементов превышает 12, 24, 48, 96 и т. д. (2 n *3, последний всплеск на графике — 6144 элемента). Сразу после увеличения таблица в 2/0.75=2.67 раз больше числа элементов, то есть полный расход составляет около 34.67 (69.33) байт на элемент (не считая константы). Непосредственно перед увеличением таблица только в 1/0.75=1.33 раза больше числа элементов, и полный расход составляет 29.33 (58.67) байт на элемент. Заметьте, что расход памяти совершенно не зависит от того, насколько часто происходят коллизии хэшей.

Постоянную компоненту желающие могут вычислить сами, я только скажу, что инициализированный по умолчанию пустой HashSet весит 136 (240) байт.

LinkedHashSet

Почти то же самое, что и в HashSet. Используется java.util.LinkedHashMap.Entry, которая наследует java.util.HashMap.Entry, добавляя две ссылки на предыдущий и следующий элементы, поэтому график на 8 (16) байт выше, чем для HashSet, достигая перед расширением таблицы 37.33 (74.67), а после — рекордных 42.67 (85.33). Константа тоже увеличилась, так как наподобие LinkedList хранится головной Entry, который не ссылается на элемент множества. Свежесозданный LinkedHashSet весит 176 (320) байт.

IdentityHashSet (через newSetFromMap)

IdentityHashMap — очень интересная штука. Она нарушает стандартный контракт Map, сравнивая ключи по ==, а не по equals и используя System.identityHashCode. Ещё она интересна тем, что не создаёт объектов вроде Entry, а просто хранит все ключи и значения в одном массиве (ключи в чётных элементах, значения — в нечётных). В случае коллизии она не создаёт список, а записывает объект в первую свободную ячейку по ходу массива. Благодаря этому достигаются рекордно низкие накладные расходы среди всех множеств.

IdentityHashMap увеличивает размер массива вдвое каждый раз, когда он заполнен больше, чем на 2/3 (в отличие от HashMap этот коэффициент не настраивается). По умолчанию массив создаётся на 32 элемента (то есть размер массива 64). Соответственно, расширение происходит при превышении 21, 42, 85, 170 и т. д. ([2 n /3], последний всплеск на графике — 5461). Перед расширением массив содержит в 3 раза больше элементов, чем ключей в IdentityHashMap, а после расширения — в 6 раз. Таким образом, накладные расходы составляют от 12 (24) до 24 (48) байт на элемент. Пустое множество по умолчанию занимает довольно много — 344 (656) байт, но уже при девяти элементах становится экономичнее всех прочих множеств.

ConcurrentHashSet (через newSetFromMap)

ConcurrentHashMap — первая коллекция, в которой график зависит от самих элементов (а точнее от их хэш-функций). Грубо говоря, это набор фиксированного числа сегментов (по умолчанию их 16), каждый из которых является синхронным аналогом HashMap. Часть бит из модифицированного хэш-кода используется для выбора сегмента, обращение к разным сегментам может происходить параллельно. В пределе накладные расходы совпадают с накладными расходами самого HashMap, потому что java.util.concurrent.ConcurrentHashMap.HashEntry устроен почти так же, как java.util.HashMap.Entry. Увеличение размера сегментов происходит независимо, потому график не поднимается одномоментно: сперва увеличиваются сегменты, в которые попало больше элементов.

Эта коллекция вышла на первое место по начальному размеру — 1304 (2328) байт, потому что сразу же заводится 16 сегментов, в каждом из которых таблица на 16 записей и несколько вспомогательных полей. Однако для 10000 элементов ConcurrentHashSet превышает размер HashSet всего на 0.3%.

ConcurrentSkipListSet

Реализована через ConcurrentSkipListMap и, на мой взгляд, самая сложная из описанных коллекций. Идея алгоритма была описана на Хабре, поэтому здесь я в детали вдаваться не буду. Замечу только, что результирующий объём памяти здесь не зависит от данных, но при этом недетерменирован, так как коллекция инициируется генератором псевдослучайных чисел. На основании следующего псевдослучайного числа принимается решение, добавлять ли индексную запись и на сколько уровней. На каждый элемент обязательно создаётся один объект java.util.concurrent.ConcurrentSkipListMap.Node, который содержит ссылки на ключ, значение и следующий Node, формируя односвязный список. Это даёт 24 (40) байт на каждый элемент. Кроме того, создаётся примерно одна индексная запись (java.util.concurrent.ConcurrentSkipListMap.Index) на каждые два элемента (на первом уровне есть индекс для каждой четвёртой записи, на втором для каждой восьмой и т. д.). Каждая индексная запись весит столько же, сколько и Node (там тоже три ссылки), поэтому в сумме каждый элемент требует около 36 (60) байт. Есть ещё головные записи для каждого уровня (HeadIndex), но их немного (примерно логарифм от числа элементов), поэтому ими можно пренебречь.

В пустом ConcurrentSkipListSet создаётся один HeadIndex и один пустой Node; после конструирования по умолчанию коллекция занимает 112 (200) байт.

Какую часть лазерного диска объёма 700 МБ займёт текст учебника, в котором 250 страниц?

(на одной странице примерно 40 строк по 50 символов в каждой).

Пожалуйста распишите по подробнее.


250 стр * 40строк * 50симв = 50 000 симв .

700 * 1024 - 50000 / 1024 = .

Столько останется свободного места.


1. Найти максимальное количество книг (каждая объемом 200 страниц, на каждой странице 60 строк, 80 символов в строке), полностью размещенных на лазерном диске емкостью 600 Мбайт?

1. Найти максимальное количество книг (каждая объемом 200 страниц, на каждой странице 60 строк, 80 символов в строке), полностью размещенных на лазерном диске емкостью 600 Мбайт.


Seroga888 16 дек. 2020 г., 21:26:23 | 10 - 11 классы

Сколько байт потребуется, чтобы сохранить на диске одну страницу текста, если в одной строке помещается 60 символов, а на странице - 40 строк?

Сколько байт потребуется, чтобы сохранить на диске одну страницу текста, если в одной строке помещается 60 символов, а на странице - 40 строк?


Гденаходитсяхургада 28 июл. 2020 г., 11:40:42 | 5 - 9 классы

Книга, набранная на компьютере и сохранённая на жёстком диске в виде файла содержит 1200 страниц, на каждой странице 45 строк, в каждой строке 60 символов?

Книга, набранная на компьютере и сохранённая на жёстком диске в виде файла содержит 1200 страниц, на каждой странице 45 строк, в каждой строке 60 символов.

Сколько таких книг можно сохранить на лазерном компакт диске, если объём информации, вмещающийся в нём равен 700 Мбайт?


Hxbycbhdvdvhfvh 23 июл. 2020 г., 10:16:09 | 5 - 9 классы

Для хранения текста требуется 10 кбайт?

Для хранения текста требуется 10 кбайт.

Сколько страниц займёт этот текст, если на странице размещается 40 строк по 64 символа в строке?


Созданный на компьютере текст занимает 6 полных страниц?

Созданный на компьютере текст занимает 6 полных страниц.

На каждой странице размещается 30 строк по 70 символов в строке.

Какой объём оперативной памяти (в байтах) займёт этот текст?


Tanyhakyznez1 18 мар. 2020 г., 00:39:58 | 5 - 9 классы

Текст занимает 6 полных страниц?

Текст занимает 6 полных страниц.

На каждой странице размещается 25 строк по 45 символов в строке.

Какой объём оперативной памяти займёт этот текст?


Vasilinka2012 11 нояб. 2020 г., 14:59:38 | 10 - 11 классы

Каждая страница текста состоит из 30 строк по 50 символов в строке?

Каждая страница текста состоит из 30 строк по 50 символов в строке.

Объём текста 9000 байт.

Сколько страниц в тексте?


Bossamalka0021 2 янв. 2020 г., 10:33:37 | 1 - 4 классы

Каждая страница учебника содержит 60 строк в каждой строке 40 символов?

Каждая страница учебника содержит 60 строк в каждой строке 40 символов.

Учебник состоит из 176 страниц.

Определиие информацию объём учебника.


Текст занимает полных 5 страниц?

Текст занимает полных 5 страниц.

На каждой странице размещается 30 строк по 70 символов в строке.

Какой объёмом оперативной памяти ( в байтах) займёт этот текст?

(Решить в таблице, то есть Дано : Решение : ).


Анна21102007 17 дек. 2020 г., 02:32:07 | 5 - 9 классы

СРОЧНО ПРОШУ ПОЖАЛУЙСТА ?

СРОЧНО ПРОШУ ПОЖАЛУЙСТА !

Какую часть лазерного диска объема 700 Мб займет текст учебника, в котором 250 страниц?

( Одна страница весит примерно 2000 байт).

На этой странице сайта, в категории Информатика размещен ответ на вопрос Какую часть лазерного диска объёма 700 МБ займёт текст учебника, в котором 250 страниц?. По уровню сложности вопрос рассчитан на учащихся 10 - 11 классов. Чтобы получить дополнительную информацию по интересующей теме, воспользуйтесь автоматическим поиском в этой же категории, чтобы ознакомиться с ответами на похожие вопросы. В верхней части страницы расположена кнопка, с помощью которой можно сформулировать новый вопрос, который наиболее полно отвечает критериям поиска. Удобный интерфейс позволяет обсудить интересующую тему с посетителями в комментариях.


Используем формулу I = log(2)1 / p I1 = log(2)[1 / (128 / 256)] = log(2)2 = 1 бит I2 = log(2)[1 / (64 / 256)] = log(2)4 = 2 бита I3 = log(2)[1 / (32 / 256)] = log(2)8 = 3 бита I4 = log(2)[1 / (32 / 256)] = log(2)8 = 3 бита.


4 колонки и монитор.


25 ^ 215 + 5 ^ 400 - 625 = 5 ^ 430 + 5 ^ 400 - 5 ^ 4 В пятеричной системе : 1[430 нулей] + 1[400 нулей] - 10000 По правилам вычитания в столбик (5 - 1 = 4 - 0 = 4), получим 400 - 4 = 396 четверок.

Эта статья покажет вам, сколько памяти занимает написанный вами объект Java?

Модель памяти заголовка объекта Java

32

Давайте сначала посмотрим, какова модель памяти объекта Java? Поскольку наши виртуальные машины делятся на 32-битные и 64-битные, в их моделях должны быть различия.Ниже я перечисляю 32-битные виртуальные машины и 64-битные виртуальные машины. Java Модель памяти заголовка объекта. Чтобы

64

64

Поскольку местная среда автора jdk1.8 , 64-битная виртуальная машина, здесь я использую 64-битную виртуальную машину (сжатие открытых указателей) для анализа, потому что по умолчанию jdk1.8 Сжатие указателя включено по умолчанию в 64-битных виртуальных машинах.

Заголовок объекта Java в основном состоит из двух частей, первая часть - Mark Word , Это тоже Java Важная часть принципа реализации блокировки, а другая часть Klass Word 。

Klass WordНа самом деле это дизайн виртуальной машины oop-klass model Модель здесь OOP Относится к Ordinary Object Pointer (Обычный указатель на объект), похоже, что указатель на самом деле является объектом, скрытым в указателе. А также klass Содержит метаданные и информацию о методах для описания Java Класс. Он занимает 32-битное пространство в среде, где сжатый указатель включен в 64-битной виртуальной машине.

Mark WordЭто является предметом нашего анализа, и здесь также будут представлены соответствующие знания о замках. Mark Word Занимает 64-битное пространство в 64-битной среде виртуальной машины. весь Mark Word Есть несколько ситуаций распространения:

  1. Разблокирован (нормальный):Хэш-код ( identity_hashcode ) Занимает 31 бит, возраст поколения ( age ) Занимает 4 бита, режим отклонения ( biased_lock ) Занимает 1 бит, метка блокировки ( lock ) Занимает 2 бита, а остальные 26 бит не используются (то есть все 0)
  2. Предвзято:Идентификатор потока занимает 54 бита, epoch Занимает 2 бита, возраст поколения ( age ) Занимает 4 бита, режим смещения (biased_lock) занимает 1 бит, флаг блокировки (lock) занимает 2 бита, а оставшийся 1 бит не используется.
  3. Легкий закрытый: Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
  4. Тяжелый вес заблокирован (Тяжелый вес заблокирован): Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
  5. Отметка GC: Бит метки занимает 2 бита, остальные пустые (то есть заполнены 0)

Выше приведен наш анализ модели памяти заголовка объекта Java.Пока это объект Java, он обязательно будет включать заголовок объекта, что означает, что эту часть использования памяти невозможно избежать.Следовательно, в среде авторской 64-битной виртуальной машины и Jdk1.8 (сжатие указателя включено) ни один объект ничего не делает. Пока класс объявлен, его использование памяти составляет не менее 96 бит, то есть не менее 12 байт.

Модель валидации

Давайте напишем код для проверки указанной выше модели памяти. Здесь рекомендуется Openjdk.веселый инструмент, Это может помочь вам просмотреть использование памяти объектом.

Сначала добавьте зависимость от maven

Давайте сначала посмотрим: если мы просто создадим новый обычный класс без добавления каких-либо атрибутов, сколько места это займет?

Согласно нашему предыдущему анализу модели объектной памяти Java, пустой объект имеет только один заголовок объекта, который будет занимать 96 бит, то есть 12 байтов, при условии сжатия указателя.

Запустите инструмент, чтобы просмотреть занятое пространство


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

Здесь мы находим результаты шоу: Instance size:16 bytes , Результат - 16 байт. 12 байт, которые мы предсказали ранее, отличаются. Почему это происходит? Мы видим, что на приведенном выше рисунке есть 3 строки заголовка объекта, каждая из которых занимает 4 байта, поэтому размер заголовка составляет 12 байтов, что согласуется с нашими расчетами.Последняя строка - это 4 байта, заполненные виртуальной машиной.Так почему виртуальная машина должна заполнять 4 байта?

Выравнивание памяти

Хотите знать, почему виртуальная машина должна заполнять 4 байта, нам нужно понять, что такое выравнивание памяти?

Мы, программисты, смотрим на память так:


На рисунке выше показан метод чтения из памяти одной косточки и одной моркови. Но на самом деле ЦП не считывает и не записывает память по одному байту. Напротив, ЦП считывает память блок за блоком, а размер блока может составлять 2, 4, 6, 8, 16 байтов и т. Д. Мы называем размер блока гранулярностью доступа к памяти. Как показано ниже:


Предполагая, что процессор 32-битной платформы, он будет читать блок памяти с детализацией 4 байта. Так зачем вам выравнивание памяти? Есть две основные причины:

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

Я использую легенду, чтобы проиллюстрировать процесс доступа ЦП к выравниванию без памяти:


  1. ЦП впервые считывает первый блок памяти с невыровненным адресом, считывая байты 0–3. И удалите ненужный байт 0.
  2. ЦП снова считывает второй блок памяти по невыровненному адресу, считывая 4-7 байтов. И удалите ненужные байты 5, 6, 7 байт.
  3. Объедините 1-4 байта данных.
  4. Поместите в реестр после слияния.

Следовательно, невыполнение выравнивания памяти приведет к тому, что ЦП выполнит дополнительные операции чтения и потребует дополнительных вычислений. Если выравнивание памяти выполнено, ЦП может напрямую начать чтение с адреса 0, и желаемые данные могут быть прочитаны один раз без дополнительных операций чтения и вычислений, что экономит время работы.Мы использовали пространство для времени, поэтому нам нужно выравнивание памяти.

Возвращаясь к пустому объекту Java, заполненному 4 байтами,Поскольку исходный заголовок байта составляет 12 байтов, на 64-битных машинах, если память выровнена, это 128 бит, что составляет 16 байтов, поэтому нам нужно заполнить 4 байта.

Расчет непустых объектов занимает память

Мы знаем, что пустой объект занимает 16 байтов, поэтому сколько байтов занимает непустой объект? Мы по-прежнему пишем общий класс для проверки:

Этот демонстрационный класс знакомит с другими объектами, которые мы знаем int Тип - 4 байта, NullObject Объект занимает 16 байт, заголовок объекта - 12 байт,Есть еще одна очень важная ситуация NullObject В текущем классе это ссылка, поэтому реальный объект не будет сохранен, а будет храниться только адрес ссылкиАдрес ссылки занимает 4 байта, поэтому всего 12 + 4 + 4 = 20 байтов, а память выровнена по 24 байтам. Проверим, является ли этот результат:

Результаты приведены ниже:


Мы это видим TestNotNull Пространство, занимаемое классом, составляет 24 байта, из которых заголовок занимает 12 байтов, а переменная a да int Тип, занимают 4 байта, переменная nullObject Это ссылка, которая занимает 4 байта и, наконец, заполняет 4 байта, что в сумме составляет 24 байта, что согласуется с нашим предыдущим прогнозом.Но поскольку мы создали NullObject , Этот объект будет существовать в памяти какое-то время, поэтому нам также нужно добавить 16 байтов памяти для этого объекта, что в сумме составляет 24 байта + 16 байтов = 40 байтов. Последний результат статистической печати на нашем изображении также составляет 40 байт, так что наш анализ верен.

Это также идея того, как анализировать, сколько памяти на самом деле занимает объект. Согласно этой идее, плюс инструмент jol из openJDK, вы можете в основном определить, сколько памяти теряет ваш «объект».

подводить итоги

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

  1. Модель памяти заголовка объекта Java отличается в 32-разрядных виртуальных машинах и 64-разрядных виртуальных машинах. 64-разрядная виртуальная машина разделена на две модели заголовков объектов с включенным сжатием указателя и отключенным сжатием указателя, поэтому существует три типа моделей заголовков объектов. .
  2. Выравнивание памяти происходит в основном по причинам платформы и производительности. В этой статье в основном анализируются причины производительности.
  3. При вычислении заполнения памяти пустым объектом необходимо вычислить выравнивание памяти, а при вычислении памяти для непустого объекта необходимо добавить ссылочную память и исходный объект-экземпляр.

Прилагается Java / C / C ++ / Машинное обучение / Алгоритм и структура данных / Внешний интерфейс / Android / Python / Программист должен прочитать / Список книг:

Эта статья покажет вам, сколько памяти занимает написанный вами объект Java?

Модель памяти заголовка объекта Java

32

Давайте сначала посмотрим, какова модель памяти объекта Java? Поскольку наши виртуальные машины делятся на 32-битные и 64-битные, в их моделях должны быть различия.Ниже я перечисляю 32-битные виртуальные машины и 64-битные виртуальные машины. Java Модель памяти заголовка объекта. Чтобы

64

64

Поскольку местная среда автора jdk1.8 , 64-битная виртуальная машина, здесь я использую 64-битную виртуальную машину (сжатие открытых указателей) для анализа, потому что по умолчанию jdk1.8 Сжатие указателя включено по умолчанию в 64-битных виртуальных машинах.

Заголовок объекта Java в основном состоит из двух частей, первая часть - Mark Word , Это тоже Java Важная часть принципа реализации блокировки, а другая часть Klass Word 。

Klass WordНа самом деле это дизайн виртуальной машины oop-klass model Модель здесь OOP Относится к Ordinary Object Pointer (Обычный указатель на объект), похоже, что указатель на самом деле является объектом, скрытым в указателе. А также klass Содержит метаданные и информацию о методах для описания Java Класс. Он занимает 32-битное пространство в среде, где сжатый указатель включен в 64-битной виртуальной машине.

Mark WordЭто является предметом нашего анализа, и здесь также будут представлены соответствующие знания о замках. Mark Word Занимает 64-битное пространство в 64-битной среде виртуальной машины. весь Mark Word Есть несколько ситуаций распространения:

  1. Разблокирован (нормальный):Хэш-код ( identity_hashcode ) Занимает 31 бит, возраст поколения ( age ) Занимает 4 бита, режим отклонения ( biased_lock ) Занимает 1 бит, метка блокировки ( lock ) Занимает 2 бита, а остальные 26 бит не используются (то есть все 0)
  2. Предвзято:Идентификатор потока занимает 54 бита, epoch Занимает 2 бита, возраст поколения ( age ) Занимает 4 бита, режим смещения (biased_lock) занимает 1 бит, флаг блокировки (lock) занимает 2 бита, а оставшийся 1 бит не используется.
  3. Легкий закрытый: Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
  4. Тяжелый вес заблокирован (Тяжелый вес заблокирован): Указатель блокировки занимает 62 бита, метка блокировки ( lock ) Занимает 2 бита.
  5. Отметка GC: Бит метки занимает 2 бита, остальные пустые (то есть заполнены 0)

Выше приведен наш анализ модели памяти заголовка объекта Java.Пока это объект Java, он обязательно будет включать заголовок объекта, что означает, что эту часть использования памяти невозможно избежать.Следовательно, в среде авторской 64-битной виртуальной машины и Jdk1.8 (сжатие указателя включено) ни один объект ничего не делает. Пока класс объявлен, его использование памяти составляет не менее 96 бит, то есть не менее 12 байт.

Модель валидации

Давайте напишем код для проверки указанной выше модели памяти. Здесь рекомендуется Openjdk.веселый инструмент, Это может помочь вам просмотреть использование памяти объектом.

Сначала добавьте зависимость от maven

Давайте сначала посмотрим: если мы просто создадим новый обычный класс без добавления каких-либо атрибутов, сколько места это займет?

Согласно нашему предыдущему анализу модели объектной памяти Java, пустой объект имеет только один заголовок объекта, который будет занимать 96 бит, то есть 12 байтов, при условии сжатия указателя.

Запустите инструмент, чтобы просмотреть занятое пространство


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

Здесь мы находим результаты шоу: Instance size:16 bytes , Результат - 16 байт. 12 байт, которые мы предсказали ранее, отличаются. Почему это происходит? Мы видим, что на приведенном выше рисунке есть 3 строки заголовка объекта, каждая из которых занимает 4 байта, поэтому размер заголовка составляет 12 байтов, что согласуется с нашими расчетами.Последняя строка - это 4 байта, заполненные виртуальной машиной.Так почему виртуальная машина должна заполнять 4 байта?

Выравнивание памяти

Хотите знать, почему виртуальная машина должна заполнять 4 байта, нам нужно понять, что такое выравнивание памяти?

Мы, программисты, смотрим на память так:


На рисунке выше показан метод чтения из памяти одной косточки и одной моркови. Но на самом деле ЦП не считывает и не записывает память по одному байту. Напротив, ЦП считывает память блок за блоком, а размер блока может составлять 2, 4, 6, 8, 16 байтов и т. Д. Мы называем размер блока гранулярностью доступа к памяти. Как показано ниже:


Предполагая, что процессор 32-битной платформы, он будет читать блок памяти с детализацией 4 байта. Так зачем вам выравнивание памяти? Есть две основные причины:

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

Я использую легенду, чтобы проиллюстрировать процесс доступа ЦП к выравниванию без памяти:


  1. ЦП впервые считывает первый блок памяти с невыровненным адресом, считывая байты 0–3. И удалите ненужный байт 0.
  2. ЦП снова считывает второй блок памяти по невыровненному адресу, считывая 4-7 байтов. И удалите ненужные байты 5, 6, 7 байт.
  3. Объедините 1-4 байта данных.
  4. Поместите в реестр после слияния.

Следовательно, невыполнение выравнивания памяти приведет к тому, что ЦП выполнит дополнительные операции чтения и потребует дополнительных вычислений. Если выравнивание памяти выполнено, ЦП может напрямую начать чтение с адреса 0, и желаемые данные могут быть прочитаны один раз без дополнительных операций чтения и вычислений, что экономит время работы.Мы использовали пространство для времени, поэтому нам нужно выравнивание памяти.

Возвращаясь к пустому объекту Java, заполненному 4 байтами,Поскольку исходный заголовок байта составляет 12 байтов, на 64-битных машинах, если память выровнена, это 128 бит, что составляет 16 байтов, поэтому нам нужно заполнить 4 байта.

Расчет непустых объектов занимает память

Мы знаем, что пустой объект занимает 16 байтов, поэтому сколько байтов занимает непустой объект? Мы по-прежнему пишем общий класс для проверки:

Этот демонстрационный класс знакомит с другими объектами, которые мы знаем int Тип - 4 байта, NullObject Объект занимает 16 байт, заголовок объекта - 12 байт,Есть еще одна очень важная ситуация NullObject В текущем классе это ссылка, поэтому реальный объект не будет сохранен, а будет храниться только адрес ссылкиАдрес ссылки занимает 4 байта, поэтому всего 12 + 4 + 4 = 20 байтов, а память выровнена по 24 байтам. Проверим, является ли этот результат:

Результаты приведены ниже:


Мы это видим TestNotNull Пространство, занимаемое классом, составляет 24 байта, из которых заголовок занимает 12 байтов, а переменная a да int Тип, занимают 4 байта, переменная nullObject Это ссылка, которая занимает 4 байта и, наконец, заполняет 4 байта, что в сумме составляет 24 байта, что согласуется с нашим предыдущим прогнозом.Но поскольку мы создали NullObject , Этот объект будет существовать в памяти какое-то время, поэтому нам также нужно добавить 16 байтов памяти для этого объекта, что в сумме составляет 24 байта + 16 байтов = 40 байтов. Последний результат статистической печати на нашем изображении также составляет 40 байт, так что наш анализ верен.

Это также идея того, как анализировать, сколько памяти на самом деле занимает объект. Согласно этой идее, плюс инструмент jol из openJDK, вы можете в основном определить, сколько памяти теряет ваш «объект».

подводить итоги

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

  1. Модель памяти заголовка объекта Java отличается в 32-разрядных виртуальных машинах и 64-разрядных виртуальных машинах. 64-разрядная виртуальная машина разделена на две модели заголовков объектов с включенным сжатием указателя и отключенным сжатием указателя, поэтому существует три типа моделей заголовков объектов. .
  2. Выравнивание памяти происходит в основном по причинам платформы и производительности. В этой статье в основном анализируются причины производительности.
  3. При вычислении заполнения памяти пустым объектом необходимо вычислить выравнивание памяти, а при вычислении памяти для непустого объекта необходимо добавить ссылочную память и исходный объект-экземпляр.

Прилагается Java / C / C ++ / Машинное обучение / Алгоритм и структура данных / Внешний интерфейс / Android / Python / Программист должен прочитать / Список книг:

Сколько информации может хранить наш мозг? И может ли в нём закончиться место?

Любая информация занимает определенный объем памяти. Наши воспоминания – это тоже информация, а значит человеческий мозг, подобно жесткому диску, должен иметь ограниченный объем. Так ли это на самом деле и сколько информации может хранить наш мозг? Давайте разбираться!

Сколько информации может хранить наш мозг? И может ли в нём закончиться место?

Как происходит процесс запоминания?

Рабочим компонентом мозга является нейрон – электрически возбудимая нервная клетка. Именно нейроны принимают, обрабатывают, хранят и выдают информацию обратно путем электро-химических сигналов.

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

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

Сколько информации может хранить наш мозг? И может ли в нём закончиться место?

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

Сколько в нас гигабайтов?

В мозге среднестатистического человека содержится приблизительно 100 миллиардов нейронов. Каждый из них может участвовать максимум в 10 000 нейронных связях. Каждый нейрон вмещает 4.7 бита информации. На основе этих данных исследователи пришли к выводу, что общая память человека составляет 1 миллион гигабайт (1 петабайт).

Много ли это? Для нас это практически бесконечный объём. Чтобы в совершенстве выучить английский язык, человеку потребуется 1.6 мегабайта памяти. Чтобы запомнить 36 миллионов томов Библиотеки Конгресса США, нужно 0.1 петабайт.

Сколько информации может хранить наш мозг? И может ли в нём закончиться место?

Теоретически можно выучить все книги в мире, только на это потребуется очень много времени

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

Для поддержания всех нейронных связей в рабочем состоянии человеку потребуется регулярно их использовать, затрачивая колоссальное количество энергии. Для её восполнения эрудиту придется жевать пищу сутками напролет. Да и мозг не может работать 24/7, ему тоже нужно отдыхать.

Читайте также: