Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Media is too big
VIEW IN TELEGRAM
Ktor Server Fundemantals. Часть 1
Освойте разработку бэкенда на Ktor с использованием Kotlin! Эта серия материалов охватывает основы Ktor, маршрутизацию, обработку запросов, аутентификацию и другие ключевые концепции, которые помогут вам эффективно создавать надежные серверные приложения.
источник
✍️ @kotlin_lib
Освойте разработку бэкенда на Ktor с использованием Kotlin! Эта серия материалов охватывает основы Ktor, маршрутизацию, обработку запросов, аутентификацию и другие ключевые концепции, которые помогут вам эффективно создавать надежные серверные приложения.
источник
✍️ @kotlin_lib
👍3🥰1
This media is not supported in your browser
VIEW IN TELEGRAM
Debounce vs Sample в Kotlin Flow
Ну что ж, пора снова погрузиться в мир Flow! Сегодня мы выносим на первый план два недооценённых инструмента: debounce и sample.
Про debounce многие из вас уже слышали, а вот про sample — гораздо реже. И, если быть честными, некоторые вообще используют debounce неправильно. Так что сейчас мы разложим всё по полочкам и сделаем эти концепции предельно понятными. Погнали! 🔥
https://proandroiddev.com/debounce-vs-sample-in-kotlin-flow-a89b4a94c893
✍️ @kotlin_lib
Ну что ж, пора снова погрузиться в мир Flow! Сегодня мы выносим на первый план два недооценённых инструмента: debounce и sample.
Про debounce многие из вас уже слышали, а вот про sample — гораздо реже. И, если быть честными, некоторые вообще используют debounce неправильно. Так что сейчас мы разложим всё по полочкам и сделаем эти концепции предельно понятными. Погнали! 🔥
https://proandroiddev.com/debounce-vs-sample-in-kotlin-flow-a89b4a94c893
✍️ @kotlin_lib
👍2
Kotlin: val != Immutable? 🤔
Многие новички (и не только) живут с убеждением, что ключевое слово
В недавней статье на ProAndroidDev разбирают популярное заблуждение:
Вот два кейса, когда ваш «неизменяемый»
1️⃣ Изменяемость самого объекта
2️⃣ Кастомные геттеры
Это самый коварный момент. Свойство
В статье также приводят интересную статистику: в опросе 41% разработчиков ответили, что считают
По-настоящему неизменяемым объект становится только тогда, когда он состоит из примитивов или других неизменяемых объектов (например, Data Class, где все поля
https://proandroiddev.com/the-val-property-immutable-in-kotlin-2e4cf49207d0
✍️ @kotlin_lib
Многие новички (и не только) живут с убеждением, что ключевое слово
val гарантирует неизменяемость данных. Но так ли это на самом деле?В недавней статье на ProAndroidDev разбирают популярное заблуждение:
val - это read-only (доступ только для чтения), но никак не immutable (неизменяемость).Вот два кейса, когда ваш «неизменяемый»
val может измениться:1️⃣ Изменяемость самого объекта
val гарантирует только то, что ссылка на объект останется той же. Но если объект внутри изменяемый - его состояние можно менять без проблем.
val list = mutableListOf(1, 2, 3)
list.add(4) // Ссылка та же, содержимое изменилось
2️⃣ Кастомные геттеры
Это самый коварный момент. Свойство
val может возвращать разные значения при каждом обращении, если у него переопределен get().
val random: Int
get() = Random.nextInt()
В статье также приводят интересную статистику: в опросе 41% разработчиков ответили, что считают
val именно immutable, что технически неверно.По-настоящему неизменяемым объект становится только тогда, когда он состоит из примитивов или других неизменяемых объектов (например, Data Class, где все поля
val и нет ссылок на мутабельные типы).https://proandroiddev.com/the-val-property-immutable-in-kotlin-2e4cf49207d0
✍️ @kotlin_lib
👍3🤡3🫡1
🩸 Value Classes: Когда «оптимизация» убивает перформанс
Мы привыкли думать, что
Не всегда. Есть сценарии, где
Рассмотрим классический кейс: попытка написать обобщенный обработчик.
Что происходит под капотом?
В момент вызова
Чтобы передать
1. Создается экземпляр-обертка
2. Этот объект передается в функцию.
3. Если внутри функции мы снова кастуем его к конкретному типу, происходит анбоксинг.
Еще хуже: Интерфейсы
Если ваш
Почему это важно?
Если вы используете value classes в горячих циклах (hot paths) или высоконагруженных стримах, думая, что экономите память, использование их в качестве дженериков или через интерфейсы приведет к обратному эффекту:
🖤 GC Pressure: Вы создаете короткоживущие объекты-обертки тысячами.
🖤 Снижение производительности: Аллокация + боксинг/анбоксинг стоят дороже, чем передача ссылки на обычный объект.
Как лечить?
1. Избегайте интерфейсов на value classes, если критична производительность.
2. Специализируйте функции. Вместо
3. Ждите Project Valhalla. Настоящие примитивные классы в JVM решат эту проблему фундаментально, но пока мы живем с ограничениями Type Erasure.
Итог:
#kotlin #performance #jvm #senior
✍️ @kotlin_lib
Мы привыкли думать, что
value class - это серебряная пуля для Type Safety без оверхеда. Обернули Int в UserId, и в рантайме это просто int, верно?Не всегда. Есть сценарии, где
value class начинает вести себя хуже, чем обычный data class, порождая скрытый боксинг на ровном месте.Рассмотрим классический кейс: попытка написать обобщенный обработчик.
@JvmInline
value class UserId(val id: Int)
fun <T> handle(item: T) {
// Какая-то логика
println(item)
}
fun main() {
val uid = UserId(42)
handle(uid) // <--- Здесь происходит магия (плохая)
}
Что происходит под капотом?
В момент вызова
handle(uid) компилятор сталкивается с проблемой. Функция handle ожидает T (который в JVM стирается до Object), а UserId в скомпилированном виде - это примитив int.Чтобы передать
int туда, где ожидается Object, JVM обязана его упаковать.1. Создается экземпляр-обертка
UserId в куче (heap allocation).2. Этот объект передается в функцию.
3. Если внутри функции мы снова кастуем его к конкретному типу, происходит анбоксинг.
Еще хуже: Интерфейсы
Если ваш
value class реализует интерфейс, и вы работаете с ним через ссылку на этот интерфейс - вы гарантированно получаете боксинг.
interface Identity {
fun raw(): Int
}
@JvmInline
value class GroupId(val id: Int) : Identity {
override fun raw() = id
}
fun process(id: Identity) { ... } // 100% боксинг при вызове
Почему это важно?
Если вы используете value classes в горячих циклах (hot paths) или высоконагруженных стримах, думая, что экономите память, использование их в качестве дженериков или через интерфейсы приведет к обратному эффекту:
Как лечить?
1. Избегайте интерфейсов на value classes, если критична производительность.
2. Специализируйте функции. Вместо
fun <T> handle(t: T) пишите перегрузки для конкретных value типов, если это возможно.3. Ждите Project Valhalla. Настоящие примитивные классы в JVM решат эту проблему фундаментально, но пока мы живем с ограничениями Type Erasure.
Итог:
value class идеален для доменной модели и сигнатур функций, но будьте предельно осторожны, как только они попадают в полиморфный контекст.#kotlin #performance #jvm #senior
✍️ @kotlin_lib
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2
💀 ThreadLocal в мире Coroutines: Цена магии
Все знают:
Большинство знает решение:
Но немногие задумываются, как именно это работает и почему злоупотребление этим механизмом может просадить пропускную способность сервиса.
Проблема: M:N Threading
В мире корутин один и тот же бизнес-процесс может начать выполняться на
Если вы положили данные в
Решение: ThreadContextElement
Kotlin предлагает мост:
Выглядит просто, но под капотом происходит постоянное перекладывание данных.
Что происходит в
Каждый раз, когда корутина возобновляется (resume) на потоке, срабатывает механизм перехватчиков.
1. Mount (updateThreadContext): Перед выполнением блока кода значение из контекста корутины принудительно устанавливается в
2. Execution: Код корутины выполняется.
3. Unmount (restoreThreadContext): Как только корутина снова саспендится или завершается, в
Это гарантирует, что мы не загрязняем потоки пула.
📉 Скрытый оверхед (Performance Penalty)
Это не бесплатно. Если у вас в стеке корутины 5 элементов
На высоконагруженных системах (High Throughput) это создает ощутимое давление, так как эти операции часто включают работу с
Best Practices для Senior-разработчика
1. Не используйте ThreadLocal как шину данных. Передавайте явные параметры в функции (
2. Ограничьте Scope. Используйте
3. MDC Integration. Для логирования лучше использовать специализированные библиотеки, которые умеют эффективно работать с контекстом, или копировать контекст только на границах системы, а не таскать его везде.
Вывод:
#kotlin #coroutines #concurrency #jvm #senior
✍️ @kotlin_lib
Все знают:
ThreadLocal привязан к потоку. Корутины могут прыгать между потоками. Это конфликт.Большинство знает решение:
asContextElement().Но немногие задумываются, как именно это работает и почему злоупотребление этим механизмом может просадить пропускную способность сервиса.
Проблема: M:N Threading
В мире корутин один и тот же бизнес-процесс может начать выполняться на
Thread-1, приостановиться (IO), и продолжить на Thread-2.Если вы положили данные в
ThreadLocal (например, RequestID для логирования) и не передали их в контекст корутины - после саспенда вы эти данные потеряете. Или, что хуже, прочитаете "грязные" данные от другой корутины, которая использовала этот поток ранее.Решение: ThreadContextElement
Kotlin предлагает мост:
ThreadLocal.asContextElement(value).Выглядит просто, но под капотом происходит постоянное перекладывание данных.
val myTL = ThreadLocal<String>()
launch(Dispatchers.Default + myTL.asContextElement("foo")) {
println(myTL.get()) // "foo"
delay(10) // Suspend -> поток освобождается
println(myTL.get()) // "foo" (хотя поток может быть уже другим)
}
Что происходит в
DispatchedContinuation?Каждый раз, когда корутина возобновляется (resume) на потоке, срабатывает механизм перехватчиков.
1. Mount (updateThreadContext): Перед выполнением блока кода значение из контекста корутины принудительно устанавливается в
ThreadLocal текущего потока. Старое значение потока запоминается.2. Execution: Код корутины выполняется.
3. Unmount (restoreThreadContext): Как только корутина снова саспендится или завершается, в
ThreadLocal потока возвращается старое значение.Это гарантирует, что мы не загрязняем потоки пула.
📉 Скрытый оверхед (Performance Penalty)
Это не бесплатно. Если у вас в стеке корутины 5 элементов
ThreadContextElement (MDC, SecurityContext, Tracing, TenantId и т.д.), и ваша корутина делает 100 саспендов (например, частые мелкие IO или yield()), то 100 раз произойдет цикл:Save Old State -> Set New State -> Run -> Restore Old State.На высоконагруженных системах (High Throughput) это создает ощутимое давление, так как эти операции часто включают работу с
ThreadLocalMap, что не так быстро, как доступ к регистрам.Best Practices для Senior-разработчика
1. Не используйте ThreadLocal как шину данных. Передавайте явные параметры в функции (
context receivers в будущем помогут).2. Ограничьте Scope. Используйте
withContext(myTL.asContextElement()) только вокруг тех участков кода, где это действительно нужно (например, вызов легаси библиотеки, зависящей от ThreadLocal), а не на уровне всего GlobalScope или корневой корутины.3. MDC Integration. Для логирования лучше использовать специализированные библиотеки, которые умеют эффективно работать с контекстом, или копировать контекст только на границах системы, а не таскать его везде.
Вывод:
asContextElement - это костыль для совместимости с Thread-bound миром. В чистом Coroutine-мире данные должны течь через аргументы или CoroutineContext, но не через ThreadLocal.#kotlin #coroutines #concurrency #jvm #senior
✍️ @kotlin_lib
❤3👍3
Скрытый боксинг value-классов. Мы всё еще аллоцируем?
Все любят
Смотрим код:
Почему?
В строке
Где еще ловим боксинг?
1. Использование внутри коллекций (
2. Реализация интерфейсов (
3. Nullable-обертки над примитивными value-классами (
Вывод: Используйте
✍️ @kotlin_lib
Все любят
value class (бывшие inline-классы). Обертка без оверхеда, типизация примитивов, красота. Но сеньор должен знать, когда эта магия ломается.Смотрим код:
@JvmInline
value class UserId(val id: String)
fun processUser(id: UserId) { ... } // (1)
fun <T> logItem(item: T) { ... } // (2)
val uid = UserId("101")
processUser(uid) // Всё ок, передается String
logItem(uid) // 💥 А вот тут — боксинг!
Почему?
В строке
(2) функция принимает дженерик T. JVM не знает, что там внутри value class, и вынуждена создать объект-обертку (instantiate UserId), чтобы передать его как Object.Где еще ловим боксинг?
1. Использование внутри коллекций (
List<UserId>).2. Реализация интерфейсов (
value class UserId : IEntity).3. Nullable-обертки над примитивными value-классами (
value class Code(val x: Int) -> Code?).Вывод: Используйте
value class для доменной выразительности, но не обманывайте себя, что это всегда zero-allocation. Если гонитесь за перформансом в горячих циклах, проверяйте байт-код или профайлер.✍️ @kotlin_lib
👍3