From 304449e8161121c18e7c5a3dc91d33a82d03b782 Mon Sep 17 00:00:00 2001 From: Kuzmin <«vkuzmin@icerockdev.com»> Date: Wed, 20 May 2026 10:33:05 +0300 Subject: [PATCH 1/5] update memory_management --- learning/memory_management.md | 62 ++++++++++++++--------------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/learning/memory_management.md b/learning/memory_management.md index cc6e4e7d8..fea1bf0d5 100644 --- a/learning/memory_management.md +++ b/learning/memory_management.md @@ -45,42 +45,27 @@ murzik = null ![Иллюстрация](./reference.png) -[Наглядный пример цикла сильных ссылок на iOS](https://swiftbook.ru/content/languageguide/automatic-reference-counting/#strong-reference-cycles-between-class-instances) +[Наглядный пример цикла сильных ссылок на iOS](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/#Resolving-Strong-Reference-Cycles-Between-Class-Instances) ## Подход Garbage Collector на андроид -В Андроид байт-код исполняет своя виртуальная java машина – ART (Android Runtime). Поэтому реализация сборщика мусора схожа с реализацией GC в JVM. +В Андроид байт-код исполняет своя виртуальная машина – ART (Android Runtime). Поэтому реализация сборщика мусора схожа с реализацией GC в JVM. Его работа будет запущена в следующих случаях: -- когда возникнет условие нехватки памяти (невозможно провести операцию по выделению памяти) -- когда заполнение памяти кучи достигнет определенного лимита -- когда сборщик мусора был вызван непосредственно из кода программы +- когда возникнет условие нехватки памяти (невозможно провести операцию по выделению памяти); +- когда заполнение памяти кучи достигнет определённого лимита; +- когда сборщик мусора был вызван непосредственно из кода программы. -[Как работает последовательный сборщик мусора (генерационный)](http://alexandr.logdown.com/posts/22570) -Такой сборщик мусора в своей работе использует модель stop-the-world, останавливая работу приложения на время, необходимое для полного прохождения цикла сборки мусора. Сборка мусора выполняется только одним потоком. +В ранних версиях Android (Dalvik, Android 4.4 и ниже) использовался последовательный stop-the-world сборщик. С переходом на ART (Android 5.0+) GC стал поколенческим: объекты разделяются на «молодое» и «старое» поколения, что позволяет собирать короткоживущие объекты быстро и часто, не затрагивая всё дерево объектов. Начиная с Android 8 (Oreo) стандартным алгоритмом стал **Concurrent Copying (CC) GC**, использующий регионовую модель памяти (RegionTLAB) и read-барьеры. Это позволяет выполнять сборку мусора конкурентно с основными потоками приложения и дефрагментировать кучу без длительных остановок. -Также существует параллельный сборщик мусора **Concurrent Mark Sweep GC** -Выполняет часть работ по сборке мусора параллельно с основными потоками приложения. +[Подробнее про сборку мусора в ART](https://source.android.com/docs/core/runtime/gc-debug) -**Алгоритм его работы можно описать так:** +### Что GC на андроид считает “мусором”? -- Объекты создаются в памяти; -- В момент, когда нужно запустить сборщик мусора приложение приостанавливается; -- Сборщик проходится по дереву объектов, помечая живые объекты; -- Сборщик проходится по всей памяти, находя все не отмеченные куски памяти и сохраняя их в «free list»; -- Когда новые объекты начинают создаваться, они создаются в памяти доступной во «free list». - -**Минусы этого способа:** -- Приложение не работает, пока происходит сборка мусора; -- Время остановки напрямую зависит от размеров памяти и количества объектов - -### Вернемся к подходу GC на андроид, что же он считает “мусором”? - -Объекты, на которые отсутствуют ссылки, считаются мусором. Также живыми могут считаться только те объекты, до которых мы можем добраться посредством цепочки ссылок, начиная с корневой (Garbage Collector Root) - ссылки, непосредственно существующей в выполняемом коде. Если мы представим все объекты и ссылки между ними как дерево, то нам нужно будет пройти с корневых узлов (точек) по всем рёбрам. При этом узлы, до которых мы сможем добраться - не мусор, все остальные - мусор. Этот подход получил название “трассировка” (tracing). Существует несколько типов корневых точек: +Объекты, на которые отсутствуют ссылки, считаются мусором. Живыми могут считаться только те объекты, до которых мы можем добраться посредством цепочки ссылок, начиная с корневой (Garbage Collector Root) — ссылки, непосредственно существующей в выполняемом коде. Если мы представим все объекты и ссылки между ними как дерево, то нам нужно будет пройти с корневых узлов (точек) по всем рёбрам. При этом узлы, до которых мы сможем добраться — не мусор, все остальные — мусор. Этот подход получил название «трассировка» (tracing). Существует несколько типов корневых точек: - локальные переменные и параметры методов; -- активные потоки; +- активные потоки (стек вызовов); - статические переменные (так как на них ссылаются их классы); -- Application и Context Именно поэтому в андроид не возникает проблемы с утечкой памяти при возникновении циклических зависимостей. Взглянем на иллюстрацию, которая уже была приведена ранее: ![Иллюстрация](./gc.jpg) @@ -100,6 +85,7 @@ override fun onResume() { currentUser.addOnUserUpdateListener(this) } ``` + Как добиться в этой ситуации утечки памяти? Забыть отписаться от уведомлений в методе onPause: ```kotlin override fun onPause() { @@ -127,14 +113,21 @@ Activity будет продолжать обновлять интерфейс [Реальный кейс использования слабых ссылок в общем коде](https://kmm.icerock.dev/learning/libraries/moko/moko-units#%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE-%D0%BB%D0%B8-%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D0%B2%D0%B0%D1%82%D1%8C-%D0%BB%D1%8F%D0%BC%D0%B1%D0%B4%D1%83-%D0%B2-unititem) ## Управление памятью в Kotlin Native -Осуществляется комбинированно с помощью автоматического подсчета ссылок на объекты (аналогично подходу ARC, работает в режиме реального времени) + с помощью сборщика мусора для циклических ссылок, основанный на алгоритме пробного удаления. Каждый запуск такого сборщика создаёт микропаузы в работе кода, поэтому частота его запуска строго определена. -Сборщик мусора в K/N на каждом потоке свой, поэтому можно создавать кучу объектов на фоновом потоке, они будут удаляться из памяти сравнительно долго, но не будут влиять на работу главного потока. +Начиная с Kotlin 1.7.20 (2022) в Kotlin/Native используется новый memory manager, ставший единственным с Kotlin 1.9.20 (старая модель с заморозкой объектов полностью удалена). В его основе — tracing garbage collector, который выполняется на отдельном потоке и запускается по достижении порога аллокаций (адаптивно, с автонастройкой) либо по таймеру. + +Ключевые особенности текущей модели: +- **Общая куча** — объекты хранятся в едином heap и доступны из любого потока без заморозки; +- **Конкурентный sweep** — фаза очистки выполняется параллельно с работой приложения; +- **Параллельный mark** — фаза разметки живых объектов использует несколько потоков (включая потоки приложения); +- **Циклический сборщик** (cycle collector) — отдельный механизм для обнаружения и удаления циклических ссылок, которые не может обработать обычный tracing GC; +- **Weak references** — поддерживаются в полном объёме. -Для Kotlin / Native был разработан набор ограничений, направленный на возможность обмена данными между потоками. Граф объекта должен быть сначала заморожен, чтобы предотвратить его изменение. Только после этого он может быть передан другим потокам. -[Подробнее в источнике](https://blog.jetbrains.com/kotlin/2021/05/kotlin-native-memory-management-update/) +Таким образом, современный Kotlin/Native по подходу к управлению памятью стал гораздо ближе к JVM, чем к ARC: разделяемая куча, tracing GC, отсутствие ограничений на передачу объектов между потоками. -[Работа с новой моделью памяти в Kotlin Native](https://habr.com/ru/post/578716/) +[Официальная документация Kotlin/Native memory manager](https://kotlinlang.org/docs/native-memory-manager.html) +[Подробнее об эволюции memory manager в Kotlin/Native](https://blog.jetbrains.com/kotlin/2021/05/kotlin-native-memory-management-update/) +[Migration guide: старая модель → новая](https://kotlinlang.org/docs/native-migration-guide.html) #### Проверь себя 1. Назовите механизмы работы с памятью в android и в ios. @@ -144,11 +137,4 @@ Activity будет продолжать обновлять интерфейс 5. Кратко опишите, как работает Garbage Collector. 6. Кратко опишите, как работает ARC. 7. В чем заключается различие подхода к управлению памятью на android и ios? -8. Какой подход к управлению памятью используется в Kotlin Native? - - - - - - - +8. Какой подход к управлению памятью используется в Kotlin Native (начиная с Kotlin 1.7.20)? From d3dadd7d2284a60cdd48d7b04465d0cacde9c22b Mon Sep 17 00:00:00 2001 From: Kuzmin <«vkuzmin@icerockdev.com»> Date: Wed, 20 May 2026 12:17:44 +0300 Subject: [PATCH 2/5] update socket --- learning/socket.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/learning/socket.md b/learning/socket.md index 3ef78edad..da5e87356 100644 --- a/learning/socket.md +++ b/learning/socket.md @@ -11,58 +11,58 @@ sidebar_position: 22 - обновление статуса выполнения заказа - чаты - биржи -- и тд. +- и т. д. -Http запросы также построены на базе сокетного соединения: +HTTP-запросы также построены на базе сокетного соединения: - открывается сокетное соединение - отправляются какие-то данные - в ответ также что-то отправляется - соединение прерывается -Если сокет открыт вручную - то такое соединение можно долго держать открытым, оно не закроется после одного запроса. -Push Notifications также работают на базе сокетного соединения. Как только сервер узнает, что пользователей нужно о чем-то уведомить - отправляет в этот сокет сообщение с информацией. +Если сокет открыт вручную — то такое соединение можно долго держать открытым, оно не закроется после одного запроса. +Push Notifications также работают на базе сокетного соединения. Как только сервер узнает, что пользователей нужно о чём-то уведомить — отправляет в этот сокет сообщение с информацией. -Также, для удобной работы с сокетами необходима обработка ситуации потери связи - реконнект. +Также для удобной работы с сокетами необходима обработка ситуации потери связи — переподключение. [Пример](https://android-tools.ru/coding/sokety-v-android/) реализации сокета на Android. ## WebSocket -[В чем разница](https://ru.stackoverflow.com/questions/507746/%D0%92-%D1%87%D0%B5%D0%BC-%D1%80%D0%B0%D0%B7%D0%BD%D0%B8%D1%86%D0%B0-%D0%BC%D0%B5%D0%B6%D0%B4%D1%83-socket%D0%BE%D0%BC-%D0%B8-websocket%D0%BE%D0%BC) между Socket и WebSocket. +[В чём разница](https://ru.stackoverflow.com/questions/507746/%D0%92-%D1%87%D0%B5%D0%BC-%D1%80%D0%B0%D0%B7%D0%BD%D0%B8%D1%86%D0%B0-%D0%BC%D0%B5%D0%B6%D0%B4%D1%83-socket%D0%BE%D0%BC-%D0%B8-websocket%D0%BE%D0%BC) между Socket и WebSocket. WebSocket [простыми словами](https://www.youtube.com/watch?v=SxMvxIHBahU). -Web Socket. Что это такое? Как с этим жить? - [видео](https://www.youtube.com/watch?v=bTxax4k-b8o) от [Mad Brains](https://madbrains.ru/). +WebSocket. Что это такое? Как с этим жить? — [видео](https://www.youtube.com/watch?v=bTxax4k-b8o) от [Mad Brains](https://madbrains.ru/). [Видео](https://www.youtube.com/watch?v=tF0-p4FDepk) про работу с сетью через WebSocket + [OkHttp](https://square.github.io/okhttp/). [Статья](https://apptractor.ru/info/articles/websockets-ios.html) о том, как использовать WebSocket на iOS 13. -[Статья](https://ssaurel.medium.com/learn-to-use-websockets-on-android-with-okhttp-ba5f00aea988) про WebSocket на Android с OKHttp -[WebSocket](https://ktor.io/docs/websocket-client.html) в Ktor. -[Гайд](https://ktor.io/docs/getting-started-ktor-client-chat.html) от Ktor, как сделать чат используя WebSocket и KMM. +[Статья](https://ssaurel.medium.com/learn-to-use-websockets-on-android-with-okhttp-ba5f00aea988) про WebSocket на Android с OkHttp +[WebSocket](https://ktor.io/docs/client-websockets.html) в Ktor. +[Гайд](https://ktor.io/docs/client-websockets.html) от Ktor, как работать с WebSocket. ## SocketIO [Официальный сайт](https://socket.io/) библиотеки [Статья](https://coderlessons.com/tutorials/kompiuternoe-programmirovanie/uznaite-socket-io/socket-io-kratkoe-rukovodstvo) -[Еще описание](https://brander.ua/ru/technologies/socketio) +[Ещё описание](https://brander.ua/ru/technologies/socketio) -Если кратко, то `SocketIO` это тот же WebSocket, но с другим протоколом обмена данными внутри. Т.е. не удастся на одной из сторон клиент/сервер использовать `SocketIO`, а на другой просто WebSocket. +Если кратко, то `SocketIO` — это тот же WebSocket, но с другим протоколом обмена данными внутри. То есть не получится на одной стороне использовать `SocketIO`, а на другой — просто WebSocket. -Прочитайте статью про [разницу между веб-сокетами и Socket.IO](https://habr.com/ru/post/498996/) там будут примеры на JS, не пугайтесь :) +Прочитайте статью о [разнице между веб-сокетами и Socket.IO](https://habr.com/ru/post/498996/) — там будут примеры на JS, не пугайтесь :) [Использование](https://socket.io/blog/native-socket-io-and-android/) SocketIO на Android. -Если ваш сервер использует SocketIO, то вы также можете использовать библиотеку [moko-soсket-io](../learning/libraries/moko/moko-socket-io) на стороне клиента. +Если ваш сервер использует SocketIO, то вы также можете использовать библиотеку [moko-socket-io](../learning/libraries/moko/moko-socket-io) на стороне клиента. [Страница](https://myrusakov.ru/long-polling-websockets-sse-and-comet.html) про запросы, с картинками. -## LongPooling-запросы +## Long Polling-запросы -Также, следует знать про еще один тип запросов - LongPooling-запросы +Также следует знать ещё об одном типе запросов — Long Polling. -Это более простая и "ленивая" замена сокетам. Как правило, подходит в том случае, если нам не нужно реализовывать систему реального времени. +Это более простая и «ленивая» замена сокетам. Как правило, подходит в том случае, если нам не нужно реализовывать систему реального времени. Как это выглядит: - клиент отправляет запрос на сервер, у этого запроса очень большое время ожидания ответа -- сервер ответит только тогда, когда у него появится что-то новое по запросу клиента, что-то, что он еще не отправлял -- если ничего нового нет, сервер просто ждет, когда появится что-то новое +- сервер ответит только тогда, когда у него появится что-то новое по запросу клиента, что-то, что он ещё не отправлял +- если ничего нового нет, сервер просто ждёт, когда появится что-то новое - если время ожидания ответа истекло, клиент тут же повторит запрос - как только клиент получит ответ на запрос, он сразу же его продублирует -Для работы таких запросов эту механику необходимо реализовать на сервере. В добавок, для поддержки такого соединения трафика требуется больше, чем для поддержки сокетного. +Для работы таких запросов этот механизм необходимо реализовать на сервере. Вдобавок для поддержки такого соединения трафика требуется больше, чем для поддержки сокетного. From 02312f92d81923320b9ab9eb5bff5849b30e0bb2 Mon Sep 17 00:00:00 2001 From: Kuzmin <«vkuzmin@icerockdev.com»> Date: Wed, 20 May 2026 15:06:16 +0300 Subject: [PATCH 3/5] update compiler-plugins --- learning/kotlin/compiler-plugins.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/learning/kotlin/compiler-plugins.md b/learning/kotlin/compiler-plugins.md index 69b4672a2..468edd4ed 100644 --- a/learning/kotlin/compiler-plugins.md +++ b/learning/kotlin/compiler-plugins.md @@ -1,15 +1,15 @@ # Плагины компилятора -Компилятор Kotlin позволяет делать модификацию логики компиляции за счет подключения компиляторных -плагинов. На данный момент Kotlin/JVM и Kotlin/JS используют один вариант компиляторных плагинов, а -Kotlin/Native другой. Для подключения используется специальный gradle плагин, который сообщает -компилятору откуда и какие плагины компилятора нужно взять. +Компилятор Kotlin позволяет модифицировать логику компиляции за счёт подключения компиляторных +плагинов. Начиная с Kotlin 1.9+ / 2.0 JVM, JS и Native используют единую IR-инфраструктуру +для плагинов. Для подключения используется специальный Gradle-плагин, который сообщает +компилятору, откуда и какие плагины компилятора нужно взять. ## Примеры - https://github.com/Foso/KotlinCompilerPluginExample -- https://github.com/Foso/MpApt -- https://github.com/icerockdev/moko-widgets/tree/master/plugin +- https://github.com/Foso/MpApt (архивирован, вместо него рекомендуется KSP) +- https://github.com/icerockdev/moko-widgets/tree/master/plugin (архивирован) - https://github.com/AhmedMourad0/no-copy - https://kotlinlang.org/docs/all-open-plugin.html @@ -33,7 +33,7 @@ kotlin.daemon.jvm.options=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y, ## Ссылки -- https://bnorm.medium.com/writing-your-second-kotlin-compiler-plugin-part-1-project-setup-7b05c7d93f6c (6 частей у статьи) +- https://blog.bnorm.dev/writing-your-second-compiler-plugin-part-1 (статья из 6 частей) - https://medium.com/@heyitsmohit/writing-kotlin-compiler-plugin-with-arrow-meta-cf7b3689aa3e -- https://www.youtube.com/watch?v=w-GMlaziIyo +- https://www.youtube.com/watch?v=w-GMlaziIyo — KotlinConf 2018, Kevin Most «Writing Your First Kotlin Compiler Plugin» - https://github.com/ShikaSD/kotlin-compiler-notes From e15f10a3f4de4ea53059d6634a11f1f093d49c68 Mon Sep 17 00:00:00 2001 From: Kuzmin <«vkuzmin@icerockdev.com»> Date: Wed, 20 May 2026 15:13:17 +0300 Subject: [PATCH 4/5] update kotlin-init-cases --- learning/kotlin/kotlin-init-cases.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/learning/kotlin/kotlin-init-cases.md b/learning/kotlin/kotlin-init-cases.md index b559f77f3..8ca54cc7a 100644 --- a/learning/kotlin/kotlin-init-cases.md +++ b/learning/kotlin/kotlin-init-cases.md @@ -32,7 +32,7 @@ class Person(val name: String) { ## Порядок инициализации вложенных и внутренних классов -Вложенные классы не имеют доступа к переменным внешнего класса. При создании экземпляра вложенного класса экземпляр внешнего класса (в котором объявлен вложенный), создан не будет. +Вложенные классы не имеют доступа к переменным внешнего класса. При создании экземпляра вложенного класса экземпляр внешнего класса (в котором объявлен вложенный) создан не будет. Экземпляр внутреннего класса всегда хранит ссылку на экземпляр внешнего класса. Вначале создается экземпляр внешнего класса, а затем на его основе - экземпляр внутреннего класса @@ -102,7 +102,7 @@ class Person { Как и в предыдущем случае, компилятор не подсветит ошибку ````kotlin -object { +val obj = object { val string: String init { @@ -215,13 +215,13 @@ println(case.result) Вернемся к иллюстрации: [алгоритм](./init_algorhitm.png) -В процессе инициализации при создании экземпляра класса **ClassWithSecondaryConstructor** после вызова вторичного конструктора будет вызван основной конструктор класса + произойдет выполнение кода в его блоке *init* перед выполнением кода в теле вторичного конструктора. Такми образом, этот код +В процессе инициализации при создании экземпляра класса **ClassWithSecondaryConstructor** после вызова вторичного конструктора будет вызван основной конструктор класса + произойдет выполнение кода в его блоке *init* перед выполнением кода в теле вторичного конструктора. Таким образом, этот код ````kotlin result += "not " ```` -будет выполнен перед конкатенацией значений, переданных в параметры вторичного констуктора +будет выполнен перед конкатенацией значений, переданных в параметры вторичного конструктора ````kotlin this.result += secondValue From 0f16ecde41fefb2a28b184c57ee81bb1e10da425 Mon Sep 17 00:00:00 2001 From: Kuzmin <«vkuzmin@icerockdev.com»> Date: Thu, 21 May 2026 15:54:33 +0300 Subject: [PATCH 5/5] fix after review --- learning/kotlin/kotlin-init-cases.md | 4 ++-- learning/memory_management.md | 16 +++++++++++----- learning/socket.md | 4 ++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/learning/kotlin/kotlin-init-cases.md b/learning/kotlin/kotlin-init-cases.md index 8ca54cc7a..ff4da3bb7 100644 --- a/learning/kotlin/kotlin-init-cases.md +++ b/learning/kotlin/kotlin-init-cases.md @@ -2,7 +2,7 @@ ## Порядок инициализации при создании объекта класса -1. Объявление свойств, указанных в конструкторе класса и присвоение им значений; +1. Объявление свойств, указанных в конструкторе класса, и присвоение им значений; 2. Инициализация свойств в теле класса; 3. Выполнение кода в блоке init *если свойств в теле класса и блоков init объявлено несколько, они инициализируются в том же порядке, в каком были объявлены* @@ -180,7 +180,7 @@ Bounds(x = 1.0, y = 2.0, width = 0.0, height = 0.0) Bounds(x = 1.0, y = 2.0, width = 3.0, height = 4.0) ```` -При создании класса **Player** вначале происходит инициализация его класса-родителя GameObject, внутри которого создается объект класса **Bounds**. К моменту инициализации класса **Player** свойство **bounds** внутри класса-родителя уже проинициализировано значениями: x=1, y=2, которые были переданы в параметры конструктора **Player** и значениями ширины и высоты из родителя **GameObject** width=0.0, height=0.0 +При создании класса **Player** вначале происходит инициализация его класса-родителя GameObject, внутри которого создается объект класса **Bounds**. К моменту инициализации класса **Player** свойство **bounds** внутри класса-родителя уже проинициализировано значениями: x=1, y=2, которые были переданы в параметры конструктора **Player**, и значениями ширины и высоты из родителя **GameObject** width=0.0, height=0.0 ## Кейс, иллюстрирующий приоритет выполнения блока init перед вторичным конструктором diff --git a/learning/memory_management.md b/learning/memory_management.md index fea1bf0d5..b7a2bbab9 100644 --- a/learning/memory_management.md +++ b/learning/memory_management.md @@ -5,19 +5,23 @@ sidebar_position: 19 # Работа с памятью Оперативная память (RAM - память с произвольным доступом) - используется центральным процессором, чтобы загрузить в нее операционную систему, как android, так и ios. При запуске приложения для него выделяется некоторый объем оперативной памяти, он называется памятью кучи (heap). При создании объекта (экземпляра класса) под него выделяется участок памяти в куче. Этот процесс осуществляется во время выполнения программы, поэтому такое выделение памяти называют динамическим. Созданный объект получает ссылку - переменную, содержащую адрес ячейки памяти, в которой он хранится. + Пример создания объекта на Kotlin: + ```kotlin class Cat { val name: String } val murzik = Cat() ``` + Переменная murzik хранит ссылку на объект класса Cat. -Объекты, созданные в куче, имеют глобальный доступ и на них могут ссылаться из любой части приложения. По мере создания новых объектов количество доступной памяти уменьшается. Поэтому необходимо постоянно освобождать ранее выделенную память. -Для управления динамическим распределением памяти используется сборщик мусора — программный объект, который следит за выделением памяти и обеспечивает её своевременное освобождение. Сборщик мусора на андроиде (Garbage Collector) и механизм автоматического подсчета ссылок (Automatic Reference Counting) на iOS объединены общей целью, но подходят к решению этой задачи по-разному. Они должны находить мусор - неиспользуемые объекты - и освобождать от них память. +Объекты, созданные в куче, имеют глобальный доступ, и на них могут ссылаться из любой части приложения. По мере создания новых объектов количество доступной памяти уменьшается. Поэтому необходимо постоянно освобождать ранее выделенную память. +Для управления динамическим распределением памяти используется сборщик мусора — программный объект, который следит за выделением памяти и обеспечивает её своевременное освобождение. Сборщик мусора (Garbage Collector) на Android и механизм автоматического подсчета ссылок (Automatic Reference Counting) на iOS объединены общей целью, но подходят к решению этой задачи по-разному. Они должны находить мусор - неиспользуемые объекты - и освобождать от них память. ## Однако что считается “мусором”? + Объект считается неиспользуемым, если он становится недостижим: это значит, что на него не остается сильных ссылок (подход ARC и GC), либо цепочка сильных ссылок, которая могла бы связать объект с другими объектами, обрывается (подход GC). ![Иллюстрация](./gc.jpg) @@ -33,14 +37,16 @@ murzik = null ## Утечки памяти Утечкой памяти называют ситуацию, когда в куче присутствуют объекты, которые больше не используются, но сборщик мусора не может удалить их из памяти. + Утечка памяти приводит к следующим проблемам: - У нас будет быстрее заканчиваться память в куче, из-за чего придется чаще запускать Garbage Collector. Это может привести к фризам на экране пользователя из-за приостановки работы приложения во время сборки мусора. - Снижение производительности приложения. -- У каждого приложения размер кучи ограничен, а утечка памяти "съедает" ее часть. Если мы привысим лимит то наше приложение крашнется с ошибкой. +- У каждого приложения размер кучи ограничен, а утечка памяти "съедает" ее часть. Если мы привысим лимит, то наше приложение крашнется с ошибкой. Рассмотрим различные подходы работы сборщика мусора подробнее. ## Механизм автоматического подсчета ссылок + Каждый объект имеет счетчик, который хранит информацию о том, сколько ссылок указывает на объект. Когда ссылка уничтожается, счетчик уменьшается. Если значение счетчика равно нулю, то объект можно считать мусором. ARC не освободит экземпляр, если на него есть хотя бы одна сильная ссылка. Главным минусом такого подхода является сложность обеспечения точности счетчика. Также при таком подходе сложно выявлять циклические зависимости (когда два объекта больше не используются, но каждый из них ссылается на другой). Это приводит к утечкам памяти: такую память невозможно освободить и выделить под другие объекты. ![Иллюстрация](./reference.png) @@ -62,10 +68,10 @@ murzik = null ### Что GC на андроид считает “мусором”? -Объекты, на которые отсутствуют ссылки, считаются мусором. Живыми могут считаться только те объекты, до которых мы можем добраться посредством цепочки ссылок, начиная с корневой (Garbage Collector Root) — ссылки, непосредственно существующей в выполняемом коде. Если мы представим все объекты и ссылки между ними как дерево, то нам нужно будет пройти с корневых узлов (точек) по всем рёбрам. При этом узлы, до которых мы сможем добраться — не мусор, все остальные — мусор. Этот подход получил название «трассировка» (tracing). Существует несколько типов корневых точек: +Объекты, на которые отсутствуют ссылки, считаются мусором. Живыми могут считаться только те объекты, до которых мы можем добраться посредством цепочки ссылок, начиная с корневой (Garbage Collector Root) — ссылки, непосредственно существующей в выполняемом коде. Если мы представим все объекты и ссылки между ними как дерево, то нам нужно будет пройти с корневых узлов (точек) по всем рёбрам. При этом узлы, до которых мы сможем добраться, — не мусор, все остальные — мусор. Этот подход получил название «трассировка» (tracing). Существует несколько типов корневых точек: - локальные переменные и параметры методов; - активные потоки (стек вызовов); -- статические переменные (так как на них ссылаются их классы); +- статические переменные (так как на них ссылаются их классы). Именно поэтому в андроид не возникает проблемы с утечкой памяти при возникновении циклических зависимостей. Взглянем на иллюстрацию, которая уже была приведена ранее: ![Иллюстрация](./gc.jpg) diff --git a/learning/socket.md b/learning/socket.md index da5e87356..974dbc51d 100644 --- a/learning/socket.md +++ b/learning/socket.md @@ -6,12 +6,12 @@ sidebar_position: 22 Про сокет в [Википедии](https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D0%BA%D0%B5%D1%82_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D1%8B%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81)). -Кейсы, когда нужны именно сокеты, когда сервер должен мочь в любой момент что-то сообщить: +Кейсы, когда нужны именно сокеты, когда сервер должен иметь возможность в любой момент что-то сообщить: - обновление положения такси на карте - обновление статуса выполнения заказа - чаты - биржи -- и т. д. +- и т.д. HTTP-запросы также построены на базе сокетного соединения: - открывается сокетное соединение