#java #android #arraylist #heap-memory
#java #Android #arraylist #куча-память
Вопрос:
Мне было интересно и я тестировал выделение памяти объекта на Android / Java и обнаружил, что в куче происходит что-то странное, ну, может быть, это просто нормально.
Вот что я сделал и нашел; существует текстовый файл данных размером 850 КБ с 26220 строками, по 30 символов в строке. Я получаю все строки через inputstream и добавляю их в arraylist. Затем я сбрасываю кучу в профилировщик Android Studio, и, как вы можете видеть на скриншоте, дамп кучи выглядит как подсчет всех связанных объектов по отдельности и добавление их всех, что в 7 раз увеличивает объем памяти, чем фактические текстовые данные. Обычно общий сохраняемый размер кучи приложений раньше составлял 2,8 МБ, после создания arraylist он получает около 9 МБ, поэтому объем памяти составляет 6,2 МБ для текстовых данных объемом 0,85 МБ. Является ли такое поведение совершенно нормальным в Android / Java или что-то не так с profiler?
Заранее всем спасибо.
Обновить
На этот раз я сделал еще один снимок экрана из гистограммы профилировщика памяти Android Studio, а не из дампа кучи. Интересно, что он показывает потребление памяти, как ожидалось, или, по крайней мере, не является ненормальным на этой гистограмме, в отличие от вводящего в заблуждение / сбивающего с толку дампа кучи. Как вы можете видеть на скриншоте, перед созданием arraylist общая потребляемая память приложением составляет 34,5 МБ, а память Java (предположительно куча) — 5,2 МБ. После создания arraylist из необработанного текстового файла объемом 850 КБ общая память становится 37 МБ, а Java 7,1 МБ, что составляет 2,5 МБ общей памяти и 1,9 МБ разницы в памяти кучи.
Похоже, что сохраненная информация о размере в дампе кучи Android Studio вводит в заблуждение и должна игнорироваться, как предложил Андреас.
Ответ №1:
Вы неправильно читаете.
Почему они суммировали «Сохраненный размер», мне непонятно, потому что это совершенно бесполезное значение.
В этом случае у нас есть ArrayList<String>
с 26220 String
объектами:
-
Это означает, что у нас есть 1
ArrayList
, поэтому только 1 из этих 525 выделений принадлежит нам. Мелкий размер — мелкий (ArrayList) = 10500 / 525 = 20 байт. -
An
ArrayList
ссылается на anObject[]
, поэтому только 1 из этих 661 выделений принадлежит нам. Малый размер зависит от размера массива, но допустим, это 16 байт накладных расходов 4 байта на элемент, поэтому мелкий (объект[]) = 16 4 * 26220 = 104896 байты. -
Элементы массива ссылаются на 26220
String
объектов, поэтому 26220 из этих 39896 выделений принадлежат нам. Мелкий размер составляет 638336/39896 = 16 байт наString
, поэтому мелкий (строка) = 26220 * 16 = 419520 байт. -
String
ссылается наbyte[]
то, что 26220 из этих 40407 выделений принадлежат нам. Небольшой размер зависит от размера массива, но позволяет использовать средний размер 30 символов в строке, то есть 30 байт на массив, что означает, что это 16 байт накладных расходов 30 байт = 46 байт, округленный до кратного 8, означает 48 байт, поэтому 48 * 26220 = 1258560 байт. Не знаю, почему он показывает только 965588 байт, но давайте просто пойдем с этим, так что shallow(byte[]) = 965588 байт.
Сохраненный размер — это малый размер сохраненный размер всего, на что ссылается ссылка:
retained(byte[]) = shallow(byte[]) // byte[] doesn't reference anything
= 965588 bytes
retained(String) = shallow(String) retained(byte[])
= shallow(String) shallow(byte[])
= 419520 965588 = 1385108 bytes
retained(Object[]) = shallow(Object[]) retained(String)
= shallow(Object[]) shallow(String) shallow(byte[])
= 104896 419520 965588 = 1490004 bytes
retained(ArrayList) = shallow(ArrayList) retained(Object[])
= shallow(ArrayList) shallow(Object[]) shallow(String) shallow(byte[])
= 20 104896 419520 965588 = 1490024 bytes
Теперь суммарный «сохраненный размер» смехотворен, потому что, если мы суммируем эти 4 значения, мы в конечном итоге подсчитываем 965588 байт из byte[]
4 раз, получая 965588 1385108 1490004 1490024 = 5330724 байта, и это просто бесполезно. Пожалуйста, игнорируйте суммированное значение «Сохраненный размер», поскольку оно совершенно бессмысленно.
ArrayList
Используется 1490024 байта памяти, а не 6,2 МБ.
Комментарии:
1. Спасибо за подробное объяснение, Андреас. Я также заметил, что это немного больше похоже на гистограмму профилировщика, показывающую ожидаемое потребление памяти. Верно, похоже, что сохраненная информация о размере в дампе кучи Android Studio вводит в заблуждение / сбивает с толку и должна игнорироваться.