Я использую артефакт (собственный) и буду использовать примерно 25% его функциональности.
Остальные неиспользуемые 75% — это много кода и заранее выделенной памяти.
Эта библиотека содержит такие вещи, как статические конечные экземпляры, которые служат состояниями по умолчанию (для оптимизации памяти), вещи, которые в дальнейшем будут использоваться в других проектах.
Некоторые структуры классов спроектированы как индексированные/категоризированные компоненты, например:
class Utils {
static class OfArrays {}
static class OfCollections {}
}
Другие структуры наследуются:
class Parent {
static class ClassToBeUsedInArtifact extends Parent {}
static class IgnoredClass extends Parent {}
}
Поскольку компоненты из этой библиотеки, которые я буду использовать, очень важны, мне интересно, смогу ли я вырезать все, что не будет использоваться, из опубликованного двоичного файла.
В случае, если это действительно возможно (либо из-за какой-то конфигурации Javac, либо из-за помощи других инструментов сборки), у меня есть некоторые опасения.
Меня больше всего беспокоит то, что если эти зависимости будут вырезаны из опубликованного двоичного файла (артефакта), будет ли вообще возможно разрешение последующих зависимостей, когда другие нижестоящие ветви снова обнаружат зависимость (которая была обрезана)?
пример:
//Gradle keyword = `implementation` on all artifacts built.
SomeProject
├──(implementation) ArtifactC (built with pruned ArtifactA and B)
│ ├ (implementation)─ ArtifactB:1.0.0
│ └ (implementation)─ ArtifactA:1.0.0
├──(implementation) ArtifactB:1.0.0 (built with pruned ArtifactA)
│ └ (implementation)─ ArtifactA:1.0.1
└──(implementation) ArtifactA:1.0.2 (full artifact)
Если используются специализированные инструменты сборки для удаления мертвого (двоичного) кода:
Сможет ли компилятор (стандартный или нет):
выполнить разрешение зависимостей для каждого экземпляра ArtifactA, чтобы все три использовали одну и ту же версию? (Я предполагаю, что компилятор выберет последнюю версию ArtifactA:1.0.2)
Сможет ли компилятор SomeProject сделать вывод, что ArtifactC должен использовать ArtifactB вместо использования его сокращенной версии?
Я предполагаю, что ArtifactC можно использовать без необходимости реализации ArtifactB и B. Правильно ли это?
Теперь сокращение кода — это одно. Я предполагаю, что если будут использоваться другие более специализированные инструменты сборки, инструменты, которые принудительно встраивают и переупорядочивают в большей степени, то будет ли что-либо из этого препятствовать способности компилятора SomeProject выполнять разрешение зависимостей между этими артефактами?
Да, вы будете возиться с проектами, которые зависят от вашего дела. Ничего здесь не делайте; только последующие проекты должны принимать решения о сокращении — в том крайне маловероятном случае, когда возникнет какая-либо причина для сокращения.
Если вы никогда не загружаете класс, ресурсы, которые он затрачивает во время выполнения, минимальны. Разделите свой код на классы в зависимости от использования.
Знаете ли вы, что это действительно проблема? Как говорит Торбьорн, если вы не обращаетесь к классу в своем коде, он не загружается, поэтому он и его код не занимают никакой памяти (за исключением, возможно, его имени в индексе загрузчика классов). Единственное, что «тратится впустую», — это дисковое пространство для JAR.
Да @MarkRotteveel, я согласен со всеми здесь, спасибо, речь идет о размере дискового пространства jar. В некоторых случаях эта зависимость содержит размер кода, в 4–5 раз превышающий размер реализующего ее артефакта. Что касается ресурсов памяти, они распределены по разным классам, которые служат не только для категорий/индексации, но и для предотвращения ненужного предварительного выделения. Этот артефакт будет артефактом «основополагающего» типа, который в совокупности становится очень важным. Но меня беспокоит размер, так как из-за этого артефакт, реализующий его... (не знаю, правильная ли формулировка) "раздут".
Спасибо @ThorbjørnRavnAndersen, я тестировал несколько структурных шаблонов, чтобы проверить это. Я нашел в случае с родственными классами; один не вызывает классовую нагрузку другого. Теперь мне интересно, вызывают ли внутренние классы (не наследники) загрузку класса своего родителя, даже если загруженный дочерний класс имел статический характер. Что касается моего беспокойства по поводу размера JAR, я не хочу быть человеком, который доставляет «раздутые» артефакты (я не знаю, правильный ли это термин). И если да, то что я должен порекомендовать потребителям моего артефакта?
Если у вас нет большого опыта и у вас нет действительно веской причины, не пишите вложенные классы.
Правильное использование интерфейсов и имен пакетов и отказ от использования предварительно инициализированных полей будут иметь большое значение для достижения желаемой изоляции, но без нежелательной связи. Кроме того, тщательные модульные тесты действительно помогают обнаружить вещи, которые вам не нужны.
Не умничай. Пишите как можно более понятный код. Вы оцените это позже.
Спасибо, Торбьёрн, чтобы исправить проблему с распределением памяти, я решил использовать ленивые синглтоны, полученные с помощью синхронизации загрузки классов (с использованием записей). Что касается проблемы «раздутости», я решил, что пытаться решить проблему в промежуточном программном обеспечении — это то, чего делать не следует (особенно в Java), и ответственность полностью лежит на конечном продукте, поэтому компилятор конечного продукта выполняет любое необходимое разрешение.
Ленивые одиночки? Что, черт возьми, это делает?
Преждевременная оптимизация — корень всех зол. Вы делаете слишком много предположений. Крайне маловероятно, что мертвый код потребляет значительную часть памяти или процессорного времени. Измерьте, прежде чем пытаться оптимизировать. Не предполагайте.