#prog #java #article
Как мы запустили программу на Java без JavaVM, или немного про практический опыт применения GraalVM native image.
(thanks @bapho_bush)
Как мы запустили программу на Java без JavaVM, или немного про практический опыт применения GraalVM native image.
(thanks @bapho_bush)
Хабр
Как мы запустили программу на Java без JavaVM
Всем привет! В этой статье мы расскажем о том, как технология GraalVM Native Image помогла нам решить ряд задач в одном из наших новых продуктов, написанном на Java, расскажем о проблемах, с...
#prog #java #python #article
Ладно бы система типов Java полна по Тьюрингу. Оказывается, к системе типов, задаваемых аннотациями типов Python, это тоже относится!
Ладно бы система типов Java полна по Тьюрингу. Оказывается, к системе типов, задаваемых аннотациями типов Python, это тоже относится!
😱6👎1
#prog #rust #java #article
How We Migrated Our Static Analyzer From Java To Rust
Меня, правда, смущает, что правила анализа почему-то написаны на JavaScript.
<...>
We observed that the migration tripled our performance and resulted in a tenfold reduction in memory usage, <...>
To our surprise, we gained a firm grasp of the language and a clear idea of how our codebase would be mapped onto Rust within 10 days. <...> Within a month, the entire code analysis infrastructure was migrated from Java to Rust, and all customers were running on the new Rust analyzer.
<...>
Removing our dependency on the JVM and speeding up the analysis enabled us to embed the analyzer directly into the IDE. The very same lightweight and fast analyzer that runs in your CI/CD pipelines simultaneously reports coding errors and suggests fixes in your IDE in real time, <...>
How We Migrated Our Static Analyzer From Java To Rust
Меня, правда, смущает, что правила анализа почему-то написаны на JavaScript.
<...>
We observed that the migration tripled our performance and resulted in a tenfold reduction in memory usage, <...>
To our surprise, we gained a firm grasp of the language and a clear idea of how our codebase would be mapped onto Rust within 10 days. <...> Within a month, the entire code analysis infrastructure was migrated from Java to Rust, and all customers were running on the new Rust analyzer.
<...>
Removing our dependency on the JVM and speeding up the analysis enabled us to embed the analyzer directly into the IDE. The very same lightweight and fast analyzer that runs in your CI/CD pipelines simultaneously reports coding errors and suggests fixes in your IDE in real time, <...>
❤1👍1🤔1🌚1🤨1
#prog #go #java #article
NilAway: Practical Nil Panic Detection for Go
Инструмент для статического анализа кода на Go для обнаружения потенциальных разыменований nil-указателей. В отличие от прочих решений, решающих эту проблему, NilAway быстр, анализирует код между функциями (и даже между пакетами) и поддерживает инкрементальный анализ. При всём этом он не требует никаких аннотаций в коде.
А ранее Uber сделали аналогичный инструмент для Java, NullAway.
NilAway: Practical Nil Panic Detection for Go
Инструмент для статического анализа кода на Go для обнаружения потенциальных разыменований nil-указателей. В отличие от прочих решений, решающих эту проблему, NilAway быстр, анализирует код между функциями (и даже между пакетами) и поддерживает инкрементальный анализ. При всём этом он не требует никаких аннотаций в коде.
А ранее Uber сделали аналогичный инструмент для Java, NullAway.
👍3❤1
#prog #java #article
3,200% CPU Utilization
TL;DR: несинхронизированный доступ к TreeMap привёл к тому, что связи между её внутренними узлами, которые обычно имеют древовидную структуру, образовали цикл, из-за чего треды зависали во время лукапа.
Автору также удалось воспроизвести этот спецэффект в C++ и в Go.
3,200% CPU Utilization
TL;DR: несинхронизированный доступ к TreeMap привёл к тому, что связи между её внутренними узлами, которые обычно имеют древовидную структуру, образовали цикл, из-за чего треды зависали во время лукапа.
Автору также удалось воспроизвести этот спецэффект в C++ и в Go.
Joseph Mate
3,200% CPU Utilization
A while back my machine was so messed up that I could barely ssh onto it. 3,200% CPU utilization - all 32 cores on the host were fully utilized! Compare that to my last bug where it only used 1 core, 100% Fortunately, it was using Java 17 runtime which...
🤯12👍4🤷3🥴1🌚1
#prog
Для #java есть JEP 401: Value Classes and Objects (Preview). Value-объекты в данном случае — это объекты, у которых отсутствует идентичность. Это полезно, поскольку для многих классов, которые просто объединяют несколько полей вместе для удобства (например, LocalDateTime, или условной Point в графическом движке), наличие идентичности, отличной от совокупности значений, не имеет большого смысла.
На практике идентичность у объектов Java существует из-за того, что под них выделяется память в куче и, соответственно, у них есть уникальный адрес. Отсутствие такой идентичности позволяет генерировать более разумную реализацию оператора
Всё это звучит хорошо, но, к сожалению, данная идея страдает от существующих элементов дизайна Java.
Первая — это повальная нуллабельность. Даже value-классы должны иметь возможность быть null, и это означает, что даже для сжатого представления один бит в ссылке должен отводиться под null-флаг. Как пишут сами авторы, массив из Integer, например, может хранить значения прямо в ссылках, но так как численные значения Integer занимают 32 бита и ещё один бит должен отводиться под null-флаг, на практике значения элементов массива будут занимать минимум по 64 бита. Это всё ещё выигрыш по сравнению с тем, что есть сейчас, поскольку это позволяет избежать индирекции на указателях и выделения 64 бита на заголовок каждого объекта в куче, но это всё ещё расточительно. Авторы явно признают эту проблему, и в разделе про дальнейшую работу есть ссылка на Null-restricted value class types JEP, но это пока лишь черновик.
Вторая проблема (которая, справедливости ради, была неочевидна индустрии на момент создания Java) — это отсутствие отслеживания алиасинга/перекрытия ссылок. В JEP авторы пишут:
Иными словами, выгоды от избегания аллокаций коснуться только очень маленьких объектов — особенно с учётом обязательного null-флага и паддинга под него. Но тут вообще стоит задать вопрос: зачем в принципе стоит требование атомарности обновлений ссылок? Атомарность нужна для того, чтобы избежать разрыва значений при одновременных обновлениях. Соответственно, если одновременных доступов нет, атомарность не требуется! Если бы в Java был механизм, который позволяет удостоверять, что доступ к значению уникален и остаётся уникальным в течение всего времени, пока значение остаётся достижимым (по крайней мере, для записи — многопоточное чтение безопасно и без атомарных операций), можно было бы использовать неатомарные операции для обновлений и избежать таким образом ограничений на размеры value-объектов. Можно считать это ещё одним примером к Fixing the next 10000 aliasing bugs, где инвариантом выступает целостность данных, и плюсом в копилку преимуществ отслеживания алиасинга для различных языков программирования.
Для #java есть JEP 401: Value Classes and Objects (Preview). Value-объекты в данном случае — это объекты, у которых отсутствует идентичность. Это полезно, поскольку для многих классов, которые просто объединяют несколько полей вместе для удобства (например, LocalDateTime, или условной Point в графическом движке), наличие идентичности, отличной от совокупности значений, не имеет большого смысла.
На практике идентичность у объектов Java существует из-за того, что под них выделяется память в куче и, соответственно, у них есть уникальный адрес. Отсутствие такой идентичности позволяет генерировать более разумную реализацию оператора
==, а также делать то, что в JEP называется "heap flattening": изменение представление объекта, ссылки на который вместо хранения адреса выделенной памяти хранят значения полей объекта.Всё это звучит хорошо, но, к сожалению, данная идея страдает от существующих элементов дизайна Java.
Первая — это повальная нуллабельность. Даже value-классы должны иметь возможность быть null, и это означает, что даже для сжатого представления один бит в ссылке должен отводиться под null-флаг. Как пишут сами авторы, массив из Integer, например, может хранить значения прямо в ссылках, но так как численные значения Integer занимают 32 бита и ещё один бит должен отводиться под null-флаг, на практике значения элементов массива будут занимать минимум по 64 бита. Это всё ещё выигрыш по сравнению с тем, что есть сейчас, поскольку это позволяет избежать индирекции на указателях и выделения 64 бита на заголовок каждого объекта в куче, но это всё ещё расточительно. Авторы явно признают эту проблему, и в разделе про дальнейшую работу есть ссылка на Null-restricted value class types JEP, но это пока лишь черновик.
Вторая проблема (которая, справедливости ради, была неочевидна индустрии на момент создания Java) — это отсутствие отслеживания алиасинга/перекрытия ссылок. В JEP авторы пишут:
Heap flattening must maintain the integrity of data. A flattened reference must always be read and written atomically, or it could become corrupted. On common platforms, this limits the size of most flattened references to no more than 64 bits.
Иными словами, выгоды от избегания аллокаций коснуться только очень маленьких объектов — особенно с учётом обязательного null-флага и паддинга под него. Но тут вообще стоит задать вопрос: зачем в принципе стоит требование атомарности обновлений ссылок? Атомарность нужна для того, чтобы избежать разрыва значений при одновременных обновлениях. Соответственно, если одновременных доступов нет, атомарность не требуется! Если бы в Java был механизм, который позволяет удостоверять, что доступ к значению уникален и остаётся уникальным в течение всего времени, пока значение остаётся достижимым (по крайней мере, для записи — многопоточное чтение безопасно и без атомарных операций), можно было бы использовать неатомарные операции для обновлений и избежать таким образом ограничений на размеры value-объектов. Можно считать это ещё одним примером к Fixing the next 10000 aliasing bugs, где инвариантом выступает целостность данных, и плюсом в копилку преимуществ отслеживания алиасинга для различных языков программирования.
🔥7😁4🤡3❤1🤔1
Блог*
А ключевые преимущества по производительности перед Java, возможно, станут неактуальны после претворения в жизнь Project Valhalla.
Telegram
Блог*
#prog
Для #java есть JEP 401: Value Classes and Objects (Preview). Value-объекты в данном случае — это объекты, у которых отсутствует идентичность. Это полезно, поскольку для многих классов, которые просто объединяют несколько полей вместе для удобства (например…
Для #java есть JEP 401: Value Classes and Objects (Preview). Value-объекты в данном случае — это объекты, у которых отсутствует идентичность. Это полезно, поскольку для многих классов, которые просто объединяют несколько полей вместе для удобства (например…
😁3😢3🤔1
Блог*
#prog #csharp #suckassstory C# — пример того, как не надо делать язык программирования. C# очень странно относится к затенению переменных. Обычно он это запрещает. Вот такой код, например, не компилируется: string x = "Hi"; if (true) { string x = "Bye";…
#prog #csharp #suckassstory
Блин, спасибо, очень полезно.
Для сравнения, #java:
using System;
using System.Collections.Generic;
var arr = new int[] {1, 2, 3};
Console.WriteLine(arr); // System.Int32[]
var list = new List<int>{1, 2, 3};
Console.WriteLine(list); // System.Collections.Generic.List`1[System.Int32]
Блин, спасибо, очень полезно.
Для сравнения, #java:
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
class Main {
public static void main() {
var arr = new int[] {1, 2, 3};
System.out.println(arr); // [I@251a69d7
System.out.println(Arrays.toString(arr)); // [1, 2, 3]
var list = new ArrayList<>(List.of(1, 2, 3));
System.out.println(list); // [1, 2, 3]
}
}
😁5🍌5
PVS-Studio: поиск ошибок в коде
Проверили Elasticsearch и нашли ошибку. Сможете отыскать ее в этом фрагменте? #java #ошибка
#prog #java #бомбёжкипост
В статье от PVS-Studio пишут про ошибку, найденную анализатором. Именно, в строке 8 исключение создаётся, но не выбрасывается. Я, признаться честно, это пропустил, но заметил (как и другой комментатор в оригинальном канале) другую ошибку: в строке 6 записывается индекс символа
Я решил было написать код на Rust и продемонстрировать, насколько сложнее там допустить такую ошибку банально из-за более вменяемого API, но потом я решил глянуть на этот метод немного более широко. Именно, что этот код вообще делает? Он на каждый вызов разбирает строку от начала до конца, чтобы найти нужную подстроку. Он делает лишнюю работу, если вызывается больше хотя бы раза. Разумно было бы сделать этот разбор только один раз и сложить результаты в словарь. Это убрало бы оверхед разбора и заодно переместило бы валидацию значений с каждого вызова метода на единственное создание мапы.
Я решил посмотреть, как этот метод используется. И после этого у меня начало бомбить.
Так как это приватный метод, все его использования должны быть в этом же файле. И действительно, этот метод вызывается два раза, в package-private геттере getStorageEndpoint. И знаете, с чем он вызывается? С константами
А вот и нет! Именно, строка, разбираемая в
Но и это ещё не всё. В методе
P. S.: посылаю лучи ненависти разработчикам нового UI GitHub, где визуальное отображения выделения текста не совпадает с реально выделенным текстом, а ссылки на результаты поиска символа не всегда переходят к нужной строке.
В статье от PVS-Studio пишут про ошибку, найденную анализатором. Именно, в строке 8 исключение создаётся, но не выбрасывается. Я, признаться честно, это пропустил, но заметил (как и другой комментатор в оригинальном канале) другую ошибку: в строке 6 записывается индекс символа
= в строке setting, которая является обрезанной settings[i], но в условии в строке 7 этот индекс проверяется в settings[i], изначальной, необрезанной строке. Намерение этого кода в данном блоке заключается в том, чтобы разбить строку на две половины до и после = и убедиться, что обе части непустые, однако из-за ошибки в условии проверка может пропустить пустую строку после =.Я решил было написать код на Rust и продемонстрировать, насколько сложнее там допустить такую ошибку банально из-за более вменяемого API, но потом я решил глянуть на этот метод немного более широко. Именно, что этот код вообще делает? Он на каждый вызов разбирает строку от начала до конца, чтобы найти нужную подстроку. Он делает лишнюю работу, если вызывается больше хотя бы раза. Разумно было бы сделать этот разбор только один раз и сложить результаты в словарь. Это убрало бы оверхед разбора и заодно переместило бы валидацию значений с каждого вызова метода на единственное создание мапы.
Я решил посмотреть, как этот метод используется. И после этого у меня начало бомбить.
Так как это приватный метод, все его использования должны быть в этом же файле. И действительно, этот метод вызывается два раза, в package-private геттере getStorageEndpoint. И знаете, с чем он вызывается? С константами
BLOB_ENDPOINT_NAME и BLOB_SECONDARY_ENDPOINT_NAME, определёнными несколькими строками выше, как "BlobEndpoint" и "BlobSecondaryEndpoint"! Окей, два вызова — это не настолько плохо (при условии, что сам getStorageEndpoint вызывается один раз), но, вероятно, строка для разбора передаётся извне, поэтому сам разбор имеет смысл?А вот и нет! Именно, строка, разбираемая в
getProperty — это результат вызова getConnectionString, тривиального геттера, возвращающего поле connectString. Значение этого поля, ожидаемо, выставляется в конструкторе (типа AzureStorageSettings), через вызов статического метода buildConnectString. Этот метод составляет строку вида key=value;key=value;... из переданных аргументов, и в число этих аргументов входят endpoint (который записывается после BlobEndpoint=) и secondaryEndpoint (который записывается после BlobSecondaryEndpoint=). Знакомые строки? Да, это именно те константы, которые используются для лукапа в getStorageEndpoint. Иными словами, в getStorageEndpoint вызывается код, который разбирает строку, чтобы получить оттуда значения, которые были переданы в конструктор типа напрямую! Просто сохранив значения аргументов, можно было бы выкинуть весь метод getProperty целиком и заменить его вызовы на два доступа к полям.Но и это ещё не всё. В методе
getStorageEndpoint возвращается запись StorageEndpoint, которая является просто парой строк. Даже имена полей дают понять, что эти два значения — URI. Хранятся ли они в виде URI (этот тип даже импортируется в начале файла)? Нет, они хранятся в типе String. Как тогда код валидирует, что это действительно URI? Никак... Точнее, не всегда. Именно, StorageEndpoint создаётся в двух местах в этом файле, как раз в методе getStorageEndpoint. В первом случае строки передаются напрямую, а во втором передаются результаты вызова метода deriveURIFromSettings. Этот метод конструирует строку из полей AzureStorageSettings, а в конце там вот такой восхитительный код: try {
return new URI(uriString).toString(); // validates the URI
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}URI создаётся только для того, чтобы немедленно перевести его в строку и потерять гарантии валидности!P. S.: посылаю лучи ненависти разработчикам нового UI GitHub, где визуальное отображения выделения текста не совпадает с реально выделенным текстом, а ссылки на результаты поиска символа не всегда переходят к нужной строке.
🔥11😁4👍2❤1🫡1