Я пытаюсь найти примеры из реальной жизни, которые я видел в дикой природе (возможно, слегка измененные, но основные проблемы были очень реальными). Я также пытался объединить их в одном сценарии, чтобы вы могли легко создать сеанс.
Сценарий: у вас есть трудоемкая функция, которую вы хотите выполнять много раз для разных значений, но одни и те же значения могут появляться снова (в идеале, не слишком долго после того, как они были созданы). Хороший и простой пример - это пары URL-страниц и веб-страниц, которые необходимо загрузить и обработать (для упражнения это, вероятно, должно быть смоделировано).
Петли:
Вы хотите проверить, появляется ли какое-либо из набора слов на страницах. Используйте вашу функцию в цикле, но с тем же значением, псевдокод:
for (word : words) {
checkWord(download(url))
}
Одно из решений довольно простое, просто скачайте страницу перед циклом.
Другое решение ниже.
Утечка памяти:
- простой: вы также можете решить свою проблему с помощью своего рода кеша. В простейшем случае вы можете просто поместить результаты в (статическую) карту. Но если вы не предотвратите это, его размер будет бесконечно увеличиваться -> утечка памяти.
Возможное решение: используйте карту LRU. Скорее всего, производительность не сильно снизится, но утечка памяти должна исчезнуть.
- хитрее: скажем, вы реализуете предыдущий кеш, используя
WeakHashMap
, где ключами являются URL (НЕ как строки, см. Позже), значения - это экземпляры класса, которые содержат URL, загруженную страницу и что-то еще , Вы можете предположить, что все должно быть в порядке, но на самом деле это не так: поскольку значение (на которое нет слабой ссылки) имеет ссылку на ключ (URL), ключ никогда не сможет очиститься -> хорошая утечка памяти .
Решение: удалите URL из значения.
- То же, что и раньше, но URL-адреса являются внутренними строками («чтобы сэкономить память, если у нас снова будут те же строки»), значение не относится к этому. Я не пробовал, но мне кажется, что это также приведет к утечке, потому что интернированные строки не могут быть GC-ed.
Решение: не проходите стажировку, что также приведет к совету, который вы не должны пропускать: не делайте преждевременную оптимизацию, так как это корень всего зла .
Создание объектов и строки:
- говорят, что вы хотите отображать только текст страниц (~ удалите HTML-теги). Напишите функцию, которая делает это построчно, и добавляет ее к растущему результату. Сначала результатом должна быть строка, поэтому добавление займет много времени и выделение объекта. Вы можете обнаружить эту проблему с точки зрения производительности (почему добавления выполняются так медленно) и с точки зрения создания объектов (почему мы создали так много строк, StringBuffers, массивов и т. Д.).
Решение: используйте StringBuilder для результата.
Параллелизм:
Вы хотите ускорить весь процесс, выполняя загрузку / фильтрацию параллельно. Создайте несколько потоков и запустите ваш код, используя их, но делайте все внутри большого синхронизированного блока (на основе кеша), просто «чтобы защитить кеш от проблем параллелизма». Эффект должен состоять в том, что вы эффективно используете только один поток, так как все остальные ожидают получения блокировки кеша.
Решение: синхронизировать только вокруг операций кэша (например, использовать `java.util.collections.synchronizedMap ())
Синхронизируйте все маленькие кусочки кода. Это должно снизить производительность, возможно, помешать нормальному параллельному выполнению. Если вам повезет / вы будете достаточно умны, вы также можете придумать мертвый замок.
Мораль этого: синхронизация не должна быть специальной, на основе принципа «это не повредит», а должна быть продумана хорошо.
Бонусное упражнение:
Заполните ваш кеш в начале и не делайте слишком много выделений потом, но все равно где-то будет небольшая утечка. Обычно этот шаблон не так легко поймать. Вы можете использовать функцию «закладки» или «водяной знак» профилировщика, которая должна быть создана сразу после завершения кэширования.