Telegram Web
Method Handle: краткий обзор

В java 7 появилось новое апи для вызова методов - MethodHandle. В этом посте обсудим его составные части, разницу с Reflection, и где MethodHandle может пригодится.

В чём разница с Reflection API?

MethodHandle выполняет проверки типов, доступов и тд всего один раз - во время создания. Собственно для этого метод хэндл и создавался - оптимизировать вызов методов, неизвестных во время компиляции.

Reflection делает все операции при каждом вызове.

Чем больше вызовов - тем сильнее разница. В OpenJDK даже есть JEP "Переписать реализацию Reflection API на MethodHandle", что говорит о многом.

Как им пользоваться?

1️⃣ Получаем фабрику для будущих метод хэндлов

Lookup pl = MethodHandles.publicLookup();

2️⃣ Задаём сигнатуру метода

MethodType mt = MethodType.methodType(String.class, Integer.class, Long.class);

Первый аргумент (String.class) - это возвращаемое значение, остальные - входные параметры.

3️⃣ Получаем указатель на метод

MethodHandle mh = all.findVirtual(User.class, "findName", mt);

Это ссылка на метод findName в классе User, который принимает на вход два параметра и возвращает String.

4️⃣ Подставляем аргументы и вызываем

String res = mh.invoke(1, 2L);

Выбор методов на каждом этапе гораздо шире, но разбирать мы их, конечно, не будем.

Где пригодится MethodHandle?

Конечно же в библиотеках и фреймворках, которые работают с аннотациями. Но для энтерпрайза у MethodHandle тоже есть микро-кейс: объявление логгера.

Ну вы знаете, в классах бизнес-логики часто добавляют логгирование:

LoggerFactory.getLogger(AccountService.class);

Эта строка копируется из класса в класс, меняется только имя в скобках. Операция настолько простая, что можно забыть это сделать🙂

Если использовать MethodHandle:

Logger.getLogger(MethodHandles.lookup().lookupClass());

то можно спокойно копировать строчку и ничего не менять.

В целом основная миссия MethodHandle - работать в паре с invokedynamic. Но об этом чуть позже🙂
Как устроены лямбды: вызов методов внутри JVM (2/4)

Одним летним утром мы обсудили Method Handle API.

А в первом осеннем посте сделаем полшага назад и посмотрим, как вызываются методы в JVM. Эта история тоже непростая, поэтому начнём с древних времён java 6.

Для разных методов компилятор генерирует разные байткод инструкции:

🍂 invokespecial - приватные методы, конструкторы и вызовы через super
🍂 invokestatic - статические методы
🍂 invokevirtual - public и protected методы
🍂 invokeinterface - вызов методов интерфейса

Байткод можно посмотреть, вызвав команду для скомпилированного файла:

javap -c -v UserInfo.class

При первом использовании файл с байткодом загружается с диска и регистрируется внутри JVM. Нам интересны четыре сущности:

1️⃣ ConstantPool - табличка из class-файла с именами полей, методов и аргументов. Выглядит как-то так:

#14=Utf8        Autumn
#16=Class #18
#17=NameAndType #19:#20

2️⃣ Список байткод инструкций

29: getstatic     #39 
32: aload_3
33: invokevirtual #45

Все конкретные методы и аргументы обозначаются через ссылки из Constant Pool.

3️⃣ Табличка виртуальных методов для класса

4️⃣ Табличка с интерфейсами и реализациями

Таблицы 3 и 4 лежат внутри JVM в особой структуре, чтобы по ним было легко перемещаться.

Каждый из invoke* методов работает по своему алгоритму. Понятно, что для protected метода нужно походить по иерархии, а для приватного это не нужно.

Что тут важно: все структуры строятся при загрузке класса и больше не меняются.

В Java 7 в JVM добавилась инструкция invokedynamic для поддержки динамических языков.

Зачем в JVM поддержка других языков? А потому что JVM - очень развитая и крутая:

🍁 Кроссплатформенность - берёт на себя работу с операционной системой и даёт заняться чистейшим программированием
🍁 Изоляция - можно безопасно запускать несколько приложений
🍁 JIT компиляторы
🍁 Поддержка многопоточности, сборщики мусора, рефлекшн

Думаете, у всех такое есть? А вот и нет.

Но есть один недостаток. JVM совершенно не приспособлена под динамические языки - JavaScript, Python, Ruby. Все invoke* методы должны заранее знать все классы и типы аргументов.

Поэтому в JVM добавили новую инструкцию invokedynamic. А о том, как она работает, поговорим в пятницу.
Как устроены лямбды: invokedynamic (3/4)

10 лет назад в java было 4 байткод инструкции для вызова методов. В Java 7 добавили invokedynamic для поддержки динамических языков.

invokedynamic - это лишь небольшая часть Da Vinci Machine Project. У проекта очень красивое описание:

a multi-language renaissance for the Java Virtual Machine architecture

Среди других фич Interface injection, Lightweight bytecode loading, Tail calls and tail recursion и прочие интересные штуки.

Но вернёмся к invokedynamic. Разберём по шагам, как он работает:

1️⃣ JVM встречает в class файле такой байткод

invokedynamic #7, 0

2️⃣ В пуле констант находим такую запись

#7 = InvokeDynamic #0:#8

3️⃣ Идём по ссылкам и попадаем в специальную секцию .class файла под названием BootstrapMethods.

Если компилятор использует invokedynamic, то он обязан прописать такой метод.

4️⃣ Там мы находим набор инструкций, который по заданным аргументам укажет на конкретный метод. В итоге всё сводится к получению и вызову MethodHandle.

В чём фишка:
В других invoke* алгоритм связывания жёстко задан внутри JVM, а для invokedynamic логика связывания прописывается в отдельном методе, который может быть любым.

Такая вот общая схема. Дальше идут оптимизации. Например, бутстрап метод возвращает не сам MethodHandle, а более гибкий CallSite. У него три реализации: одна с неизменным MethodHandle и две с переменным

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

Так что на следующей неделе соберём всё вместе и погрузимся в детали реализации лямбда-выражений🤓
Как устроены лямбды: собираем всё вместе (4/4)

В последней части наконец-то перейдём к лямбда-выражениям.

Что нам уже известно:

🔸 Лямбды не приводят к созданию анонимных классов
🔸 В java 7 появились invokedynamic и MethodHandle API для поддержки динамических языков. Но эти штуки отлично подошли для некоторых java фич, в том числе лямбда-выражений.

Если не читали - прочитайте, тогда этот пост будет понятней.

Итак, разработчик пишет код с лямбда-выражением

stream().filter(x -> x<3)

В байткоде на месте вызова лямбды появляется инструкция

invokedynamic #2, 0

Следуем по указателям из Constant Pool и попадаем в секцию бутстрап-методов. Видим там вызов

LambdaMetafactory.metafactory(…)

Этот фабричный метод возвращает объект CallSite, внутри которого спрятался MethodHandle.

Больше в байткоде нет информации. Понятно, что работа с лямдой - это какое-то перенаправление, а вся магия описана в классе LambdaMetafactory.

Класс лежит в JDK, идём туда и видим:

1️⃣ С помощью ASM библиотечки создаётся внутренний класс. Он реализует интерфейс Predicate и называется как-то так:

class Smth$$Lambda$14/0x0000000800c02460

Класс динамический и существует только в памяти.

2️⃣ Создаётся объект этого класса и привязывается к MethodHandle внутри ConstantCallSite.

3️⃣ Все обращения к лямбде переадресуется этому объекту.

Чем это лучше анонимных классов?

Класс создаётся и загружается динамически. Кажется, что это долго и сложно, но на деле получается быстрее, чем загрузка файла с диска
Есть кэширование
Новый класс наравне со всеми участвует в JIT оптимизациях

Почему нельзя вынести лямбду в отдельный метод и просто вызывать его?

Чтобы встроить решение в остальной java код. Если в коде пишется

Predicate p = i -> x<3;

То ожидается, что p - это объект. Его можно куда-то передать и вызвать его методы.

Логика вроде "назовём это объектом, но на самом деле это метод" - это слишком большое усложнение. Так не работает. Новые фичи должны вписываться в систему, а не обходить её.

Зачем столько переадресаций? Почему из бутстрап метода надо идти в другой класс? Нельзя было сделать встроенную инструкцию?

Текущий код LambdaMetaFactory со временем изменится. Добавится больше кэширования, семантика лямбд расширится в следующих версиях java. Если вся логика описана в JDK, то гораздо легче поддерживать совместимость версий.

Такая вот эпопея. Я весь процесс поняла не с первого раза и даже не со второго. Поэтому решила провести вас маленькими шажками, и описать всё максимально просто.
О простоте.

Недавно один подписчик написал "канал топ, но зачем ты его ведёшь?". Вопрос понятный: хорошие посты действительно отнимают много времени и сил.

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

Быть разработчиком - значит держать в голове тысячи абстракций и десяток технологий. И это количество только растёт.

Поэтому в обучении хорошо заходят принципы хорошего кода: KISS, читаемость, инкапсуляция. Любая тема раскладывается на простые шаги: 1-2-3. Некоторые темы выглядят как 1-2-3-...-99-100, но каждый шаг логичен и понятен.

Этот подход я применяю в постах и в своём любимом курсе по многопоточке.

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

За простыми вещами всегда стоит много работы. Чтобы другие люди оценили её, используются сложные описания. Так в резюме появляется "комплексный рефакторинг ключевого компонента системы интеграции аналитики".

Но чем проще, тем лучше для всех.

Я стараюсь писать посты чётко, понятно и без воды. Радуюсь, когда получаю сообщения с благодарностью, что кто-то наконец увидел суть.

Горжусь своим курсом по java.util.concurrent. У последнего потока доходимость около 80%. У других попсовых IT курсов редко превышает 10%. Чувствую, что сделала что-то на порядок выше рынка.

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

Спасибо, что читаете❤️
Java выходит каждые полгода, а сегодня вышла 17 версия. Если Age - ваш возраст прямо сейчас, через сколько лет ваш возраст и версия java будут совпадать?
Anonymous Poll
27%
Age - 17
33%
(Age - 17) /2
18%
(Age - 17) *2
21%
Age - 17 + Age/2
Ваш прогноз - когда хотя бы один сервис вашего проекта перейдёт на Java 17?
Anonymous Poll
8%
До конца года
10%
В следующем году
21%
Года через 2-3
61%
Грустно думать об этом😔
Вышла Java 17🥳

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

Самые обсуждаемые фичи:

🔸 Новые сборщики мусора: ZGC и Shenandoah
🔸 Текстовые блоки и стрелочки в switch
🔹 Sealed classes
🔹 Records

А теперь ответ на вопрос - через сколько лет ваш возраст совпадёт с версией java:

Пусть Age – возраст в 2021
x – количество лет, через которое версия = возраст.
Age + x = 17 + 2*x
x = Age - 17
Личный опыт: как я изучаю информацию

Периодически получаю вопрос от подписчиков вроде

Что почитать по джаве, чтобы также чётко всё понимать?

Источники знаний давно известны - это интернет, свой опыт и чужой опыт. Интернет доступен всем, поэтому начну не с вопроса ЧТО, а с вопроса КАК. Как из огромного количества информации извлечь знание? Как сформировать свою призму восприятия?

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

История 1: A-Club

В епаме чтобы получить новую должность, нужно пройти assessment. Это внутреннее собеседование раз в полгода, где ты рассказываешь незнакомым коллегам свой опыт и отвечаешь на вопросы. Если всё ок, тебя рекомендуют к повышению.

Это старая и стандартная процедура, список тем и вопросов давным-давно известен. Поэтому надо просто подготовиться.

И вот я и два моих коллеги решили готовиться к ассесменту вместе. Придумали такой формат:

Допустим, я беру себе тему Agile. Обязуюсь изучить тему вдоль и поперёк и к пятнице подготовить докладик на 10-20 минут. Ребята что-то знают про Agile и заранее накидывают мне "глупые вопросы про аджайл":

🔸 А почему скрам-мастер бывает, а канбан-мастера нет?
🔸 Кто-нибудь вообще измеряет задачи НЕ в сторипойнтах?
🔸 А обязательно выделять на ретро целый день?

"Глупые" вопросы принесли больше всего пользы для понимания. Чтобы найти ответ, нужно серьёзно углубиться в кейсы и исходные идеи.

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

История 2: помощь другу

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

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

Нашли несколько пробелов, и я сказала "домашнее задание - к среде ответить на X, Y и почётче рассказать про Z".

Он серьёзно подошёл к делу и в среду на всё ответил. Я задала ещё несколько вопросов, он снова ушёл с ними разбираться.

Мы увлеклись этим процессом на 2.5 месяца. Под конец я специально искала сложные вопросы и пыталась его завалить. И "глупыми" вопросами и серьёзными:

🔹 В чём разница между Kafka и ActiveMQ?
🔹 А в базах данных реально всё в таблицах хранится?
🔹 Почему в джаве нельзя напрямую вызвать деструктор?

Иногда к нам присоединялся коллега джуниор, и я просила объяснить ему простыми словами какой-нибудь паттерн, принцип или идею.

Нам всем эти беседы принесли огромную пользу.

Резюме

Объясняешь другому - сам понимаешь лучше
"Глупые" и неожиданные вопросы помогают увидеть тему глубже
Готовишь доклад - структурируешь знания и видишь пробелы

Всё это помогает перейти от пассивного потребления информации к её осознанию.
Личный опыт: где брать информацию

В прошлом посте я рассказала, КАК лично я изучаю информацию. А сейчас расскажу свой подход к тому, ГДЕ найти классные знания.

Пункт 1: жёсткий отбор

В интернете есть всё. Но не всё, что есть в интернете, нужно вам.

Чем ближе к первоисточнику, тем точнее информация и ярче видна идея. Архитектор джава правдивее всех расскажет о джаве. Автор фреймворка чётко расскажет о его принципах. Такие ребята часто пишут статьи и выступают на конференциях, их материалы легко найти.

Второй способ разобраться в теме - найти эксперта и попросить порекомендовать книгу, статью, ютуб/телеграм канал, etc. Любой профессионал за много лет пропустил через себя тонны материалов и знает, где вода, а где интеллектуальная феерия.

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

Пункт 2: опыт - это самое ценное

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

Нужно перенимать чужой опыт и много общаться.

Когда кто-то готов делиться знаниями и деталями по интересной теме - это шанс, который нельзя упускать. Подход к проблеме, решение, нюансы - всё это важно. Это расширяет картину мира и кругозор, и ценнее любых книг и абстрактных рекомендаций.

Бонус: чужой опыт сыграет вам на руку на собеседовании. Если вас спросят о технологии, с которой вы не работали, то перескажите чужую историю. Какая была проблема, какое решение, плюсы и минусы для той конкретной ситуации. Такой ответ даст вам больше очков, чем "не знаю, не работал".

Получилось кратко и всего два пункта, но, поверьте, за ними стоит целый мир🙂
Возвращаемся к техническому контенту. Я очень люблю писать про java core, потому что за простыми вещами там стоит много работы, экспериментов и компромиссов. А в итоге получается быстро, удобно и красиво.

На этой неделе посмотрим, что происходит под капотом при сложении строчек:

String str = "a" + "b";

Будет две части:
🔸 Часть 1: как всё работает в java 8
🔸 Часть 2: что изменилось в java 9
Какой код выполнится быстрее и насколько?
Сложение строк, часть 1: java 8

В JDK есть четыре способа слепить строчки между собой:
🔸 Оператор +
🔸 Класс StringBuilder
🔸 Класс StringBuffer (потокобезопасный StringBuilder)
🔸 Метод concat

В чём проблема с объединением строк?

Класс String - неизменяемый, поэтому при каждом сложении строк создаётся новый объект.
Если это просто "a"+"b", то проблемы нет.

Но в типичном java приложении очень много строчек и их взаимодействий. Больше строк - больше промежуточных объектов. Если не принять специальных мер, то "a"+"b"+"c"+"d" приведёт к созданию ab, abc и abcd.

Строки иногда занимают половину памяти, поэтому оптимизация очень важна.

StringBuilder - специальный класс, который снижает накладные расходы при соединении строк. Выглядит это так:

StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b");
sb.append("c");
String res = sb.toString();

Что происходит: StringBuilder выделяет байтовый массив и с каждым append добавляет туда строчки. При вызове toString из этого массива создаётся неизменяемая строка. Так количество лишних объектов существенно сокращается.

Единственный минус StringBuilder - плохая читаемость. Половину кода занимает append.

Через + работать приятнее, поэтому у компилятора есть набор оптимизаций при использовании плюсика. Что делает компилятор:

🔸 Соединяет явно обозначенные строки:

String s = "a" + "b" + "c";

Сразу компилируется в abc.

🔸 Сразу соединяет final строки:

final String s1 = "a";
final String s2 = "b";
final String s3 = "с";
String s4 = s1 + s2 + s3;

Это тоже компилируется в abc, без промежуточных объектов.

🔸 Если строки НЕ final, то код выше компилируется в создание объекта StringBuilder, 3 вызова метода append и метод toString

Тут мы подходим к популярному мифу. Помните, вначале я говорила, что "a"+"b"+"c" приводит к созданию объектов ab и abc. Многие понимают это буквально, что это оператор + приводит к созданию лишних объектов. Поэтому лучше плюсик не использовать, а использовать StringBuilder.

Так вот, это неправда. StringBuilder используется под капотом плюсика со времён java 5. А это 2004 год, 17 лет уже прошло.

У такого подхода есть серьёзный недостаток. StringBuilder создаётся даже при сложении двух строк:

String s1 = "b";
String s2 = "с";
String s3 = s1 + s2;

В данном случае это не рационально.

Чтобы склеить две строки без лишних StringBuilder, используйте метод concat:

String s3 = s1.concat(s2);

Итого, как соединить две строки:
🔹 final строчки - через плюсик
🔹 Две строки - через concat
🔹 Несколько строк, но один раз - через плюсик, внутри работает StringBuilder
🔹 Сложные соединения, например, в цикле - явно объявить StringBuilder и использовать его

Ответ на вопрос перед постом

В первом варианте строка-результат соберётся на этапе компиляции, а во втором - во время работы программы. Поэтому первый вариант выполнится быстрее в 20-30 раз.
Сложение строк, часть 2: java 9

Возьмём код из прошлого поста

String s = s1 + s2 + s3;

Для него компилятор java 8 соберёт байткод:

// Method StringBuilder."(init)":()
// Method StringBuilder.append:(String)
// Method StringBuilder.append:(String)
// Method StringBuilder.append:(String)
// Method StringBuilder.toString:()

Компилятор java 9 сделает по-другому:

invokedynamic #13

По ссылкам мы придём в метод StringConcatFactory#makeConcatWithConstants

(Подробно про invokedynamic я писала
в этом посте, сейчас не будем углубляться)

В классе StringConcatFactory лежит та же логика со стринг билдером. Но очень параметризуемая: через JVM параметры доступны немного разные реализации и стратегии вычисления исходного размера StringBuilder.

Это слишком глубокие дебри, поэтому не буду расписывать подробно. Почитать про все опции и посмотреть бенчмарки можно тут.

У любой задачи есть цель, поэтому давайте разберёмся, зачем это всё затевалось. Первый вариант, приходящий на ум - увеличение производительности.

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

Второй вариант - большая гибкость. Да, параметров стало больше, но не думаю, что это будет востребовано в большинстве проектов.

Основная цель изменений - это "причёсывание" кода. Что получилось после рефакторинга:

Логичность. Я воспринимаю + как сокращённую форму метода add для строк и ожидаю реализацию этого метода внутри JDK.

Чёткое разделение обязанностей. Работа компилятора - преобразовать конструкции языка в инструкции для JVM. Например, превратить + в вызов метода.

Оптимизации на этом уровне могут быть, но не на уровне "бизнес-логики". Соединить две строчки на этапе компиляции - это одно. Прописать создание объектов и тд - совсем другое.

Легче сделать сложную логику обработки. В классе StringConcatFactory 900 строк с кучей параметров и проверок. Представляете, если такое будет в байткоде? Написал в коде плюсик, а при компиляции получил чудовище.

Легче менять реализацию. Для изменения конкатенации в будущем достаточно поменять код в JDK, и не нужно трогать компилятор. Это снижает количество работы и возможных ошибок.

Если совсем просто, то перенос логики соединения строк из байткода в JDK - это как перенос логики из хранимых процедур в основной код🙂
Чем отличаются кодировки?

Продолжаем тему прошлой недели и снова поговорим о классе String. В java 9 вышло интересное обновление - компактные строки. Чтобы лучше понять, в чём была проблема и как она решена, разберём в этом посте отличия между кодировками. А в четверг обсудим уже сами компактные строки.

Итак, символы хранятся в памяти в виде чисел. Кодировки отвечают за формат хранения и правила перевода символов в числа и обратно. Кодировки условно делятся на две группы:
🔹 ASCII-based
🔹 Unicode-based

1️⃣ ASCII
В большинстве ASCII кодировок символ занимает 1 байт и содержит число от 0 до 256. Первые 128 значений транслируются одинаково во всех кодировках:
▫️0-31: управляющие последовательности - перенос строки, конец файла и т.д.
▫️32-127: латинский алфавит, цифры, знаки препинания

Значения 128-255 отображаются на специфичные символы языков. Разные кодировки - разные наборы символов:
🔸Кириллица: ISO-8859-5, Windows-1251
🔸Греческий алфавит: ISO-8859-7, Windows-1253
🔸Исландские символы: OEM 861

200 может стать Ш, Θ, È или чем-то ещё. Фраза Я люблю Java в другой кодировке отобразится как Ď ŰîŃŰî Java.

Компактный формат: 1 символ — 1 байт
Всего 256 значений — нет места для эмодзи
Неоднозначность трактовки
Нельзя использовать украинский и норвежский язык в одном тексте

2️⃣ Unicode
В основе преобразований лежит гигантская таблица с большинством символов, которые используются в мире. Первые 127 символов полностью совпадают с ASCII.

Но ASCII-кодировки не умеют читать символы больше 255. Если подсунуть им юникод, то непонятные значения превратятся в и текст Я люблю Java отобразится как ? ????? Java.

Как хранится юникод.

Ранние версии используют отображение 1 к 1. Все символы хранятся одинаково и занимают одинаковое количество памяти.

Одна из первых кодировок UCS-2 хранила один символ в двух байтах. После ASCII с их диапазоном 0-256 казалось, что диапазона 0-65536 хватит навсегда.

Со временем в таблицу добавилось больше символов и встал вопрос об эффективном хранении данных. Сегодня, чтобы однозначно представить символ юникода нужно 32 бита — так символы хранятся в UTF-32:
Прямое отображение
Простота обработки
Очень много символов
Неэффективный расход памяти — если использовать только латиницу с кодами типа 0..045 и 0...077, три четверти памяти будет занято нулями.

Чтобы решить проблему выше, появились кодировки с переменной длиной: UTF-8 и UTF-16.

Они используют не отображение 1 к 1, а схему с флажками и вычислениями.

В UTF-8 символ занимает 1, 2, 3 или 4 байта. В UTF-16 популярные символы занимают 2 байта, а непопулярные - 4.

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

Если в структуре 2 или больше байтов, то одни процессоры быстрее считывают их в прямом порядке, а другие — в обратном. Поэтому у UTF-16 и UTF-32 могут быть приставки LE или BE: Little/Big endian.

Что в java. В ранних версиях для char использовалась UCS-2, один символ занимал 2 байта. Затем java перешла на UTF-16 с переменной длиной.

Ну а что с этим не так, и как обстоят дела в java 9 - поговорим в следующем посте.
Compact String в java 9

По данным OpenJDK более 25% объектов в памяти занимают строки. По той же статистике 95% строк содержат только латинские символы и цифры, числовое значение которых умещается в 1 байт, а до 15% памяти заняты бесполезными нулями.

В java 8 символы хранятся в типе char и кодировке UTF-16. Занимают 8 или 16 бит. Экземпляр String содержит массив символов char[].

В java 9 вышло обновление Compact Strings. Символы теперь лежат в byte[] и хранятся в одной из двух кодировок. Кодировка записана в новом поле coder.

Это может быть:
1️⃣ ASCII кодировка Latin-1. Используется, если все символы строки умещаются в 1 байт. Т.е в тексте только латиница и цифры.
2️⃣ UTF-16 - если хотя бы один символ требует 16 бит.

Строки в разных кодировках по-разному лежат в памяти, и работать с ними нужно по-разному. Каждый метод в классе String начинается с проверки кодировки и разделяется на две ветки — для Latin-1 и UTF-16. Их код вынесен в отдельные классы StringLatin1 и StringUTF16.

Почему не UTF-8? Он же занимает ещё меньше места.

— UTF-8 действительно занимает меньше памяти, но работает он медленнее. Если символы в массиве одной длины, то адрес символа в памяти быстро ищется по индексу. Если элементы с переменной длиной - адрес вычисляется на основе предыдущих элементов, а это долго. Все методы класса String работали бы дольше.

Память сэкономили, кода стало в 3 раза больше. Стало ли быстрее?

— Любая дополнительная проверка снижает скорость работы, особенно при работе с маленькими строками. Поэтому на уровне JVM много оптимизаций по проверкам кодировки и сравнению строк, изменён механизм конкатенации и других операций. Именно за счёт внутренних оптимизаций компактные строки работают в среднем на 20% быстрее и создают на 30% меньше промежуточных объектов.

Самое главное - эти изменения никак не отразились на интерфейсе String. I/O классы, StringBuilder, StringBuffer тоже адаптированы без внешних изменений.

Нужно просто перейти на java 9 и приложение будет занимать на 5-15% меньше памяти.
Мини-новость: стартовал 3 поток курса по многопоточке. Кто записался, но пропустил - проверьте почту! Если не нашли письмо, напишите мне.

Кто хотел, но не успел - запишитесь в список, и вы первыми получите новость о старте нового потока. Когда он будет - не знаю:)

Что по постам.

На этой неделе поговорим о том, как перейти в айти из других сфер. Я получаю много таких вопросов, будут теперь отвечать ссылкой на посты.
Как войти в айти, часть 1

Сразу начну с горячей темы - эффективны ли java курсы за 100-200к?

Вообще учиться на курсах очень комфортно:
🔸 Чёткая программа и расписание
🔸 Группа единомышленников
🔸 Обратная связь преподавателя

Но минусы текущих предложений настолько сильные, что затмевают плюсы:

Завышенные обещания

Реклама обещает, что после курса за 100к легко найти работу за 100к. По факту рекрутеры часто отбрасывают резюме, если в нём только прохождение курсов. Потому что знаний у таких кандидатов обычно недостаточно.

Группы 30-40 человек, слабая обратная связь
Программа везде одна и та же, часто устаревшая

Если вы отметили пункт "Устроился на работу сразу после курсов", то напишите мне! Вдруг я неправа, и хорошие варианты тоже встречаются.

Что же делать?

Для начала рассмотрите другие пути.

Любая профессия при переходе в IT оплачивается больше. Зарплата IT рекрутёра может быть в 2 раза больше, чем у коллеги из другой сферы.

В IT полно других профессий: аналитик, тестировщик, automation тестировщик или UX/UI дизайнер. У многих входной порог ниже, и можно больше общаться с людьми.

Если вас привлекает именно программирование, то в следующем посте я дам список бесплатных и недорогих материалов.

Но просмотреть список недостаточно, их нужно изучить:) Построить план и встроить учёбу в привычное расписание.

Что может помочь:

🔹 Напарник. Учитесь вместе, обсуждайте проблемы, мотивируйте друг друга. Дело пойдёт быстрее 100%
🔹 Ментор. Самый эффективный путь. Ментор - это человек, который уже дошёл до вашей точки Б и готов помочь вам пройти этот путь. Как найти ментора и договориться с ним - отдельный вопрос. Когда-нибудь напишу об этом пост.

Сколько времени займёт подготовка?
— Зависит от многих факторов, но ориентируйтесь на регулярные занятия 30-60 минут в день в течение года.

Я юрист/пекарь/бухгалтер, мне 30/40/50 лет, могу ли я стать программистом?
— Сможете, если у вас есть:
▫️ Интерес к самому программированию, а не только к деньгам в IT
▫️ Английский язык на уровне чтения технической документации

А меня возьмут? Я юрист/пекарь/бухгалтер, мне 30/40/50
— Если вы хорошо знаете основы java, приятны в общении и сообразительны, то вам будут рады. Последние два пункта не менее важны, чем первый!
2025/07/06 18:55:53
Back to Top
HTML Embed Code: