Использование SoftReference для статических данных для предотвращения нехватки памяти в Java - PullRequest
2 голосов
/ 12 июля 2009

У меня есть класс со статическим членом, подобным этому:

class C
{
  static Map m=new HashMap();
  {
    ... initialize the map with some values ...
  }
}

AFAIK, это будет занимать память практически до конца программы. Мне было интересно, если бы я мог решить это с помощью мягких ссылок, как это:

class C
{
  static volatile SoftReference<Map> m=null;
  static Map getM() {
    Map ret;
    if(m == null || (ret = m.get()) == null) {
      ret=new HashMap();
      ... initialize the map ...
      m=new SoftReference(ret);
    }
    return ret;
  }
}

Вопрос

  1. Является ли этот подход (и реализация) правильным?
  2. если да, то окупается ли в реальных ситуациях?

Ответы [ 7 ]

4 голосов
/ 12 июля 2009

Во-первых, приведенный выше код не является потокобезопасным.

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

  1. достаточно большой, чтобы их использование памяти было релевантным
  2. Возможность воссоздания на лету без недопустимых задержек
  3. Используется только в тех случаях, когда другим частям программы требуется меньше памяти - в противном случае максимальный требуемый объем памяти будет таким же, только среднее будет меньше, и вы, вероятно, даже не увидите этого за пределами JVM, так как он возвращает куча памяти для ОС очень неохотно.

Здесь 1. и 2. противоречивы - создание больших объектов также занимает больше времени.

2 голосов
/ 12 июля 2009

Это нормально, если ваш доступ к getM является однопоточным и он действует только как кеш. Лучшая альтернатива - иметь кэш фиксированного размера, поскольку это обеспечивает постоянное преимущество.

1 голос
/ 12 июля 2009

getM() должно быть synchronized, чтобы избежать одновременной инициализации m разными потоками.

0 голосов
/ 12 июля 2009

Первое, что я заметил в коде, это то, что он смешивает универсальные с необработанными типами. Это просто приведет к беспорядку. Javac в JDK7 имеет -Xlint:rawtypes, чтобы быстро обнаружить ошибку такого рода, прежде чем начнутся проблемы.

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

Проблема с использованием SoftReference для всего кэша заключается в том, что вы будете вызывать пики при очистке ссылки. В некоторых случаях может быть лучше иметь ThreadLocal<SoftReference<Map<K,V>>>, который бы распространял шипы и безопасность вспомогательных потоков за счет отсутствия разделения между потоками.

Однако создать более умный кеш сложнее. Часто вы получаете значения, ссылающиеся на ключи. Есть способы обойти это немного, это беспорядок. Я не думаю, что эфемероны (по сути, пара связанных Reference s) собираются создать JDK7. Вы можете найти Коллекции Google, на которые стоит обратить внимание (хотя я этого не сделал).

java.util.LinkedHashMap дает простой способ ограничить количество кэшированных записей, но не очень полезен, если вы не можете быть уверены, насколько велики записи, и может вызвать проблемы, если он останавливает сбор больших объектных систем, таких как ClassLoader s. Некоторые люди говорят, что вы не должны оставлять вытеснение из кэша до прихотей сборщика мусора, но тогда некоторые люди говорят, что вы не должны использовать GC.

0 голосов
/ 12 июля 2009

Мне было интересно, смогу ли я решить это с мягкими ссылками

Что такое это , которую вы пытаетесь решить? У вас проблемы с памятью или вы преждевременно оптимизируете?

В любом случае

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

  2. Причина использования SoftReferences состоит в том, чтобы избежать нехватки памяти, поскольку нет другого контракта, кроме того, что они будут очищаться до того, как виртуальная машина выдает OutOfMemoryError. Следовательно, этот подход не гарантирует никаких преимуществ, за исключением того, что он не создает кеш до его первого использования.

0 голосов
/ 12 июля 2009

Может быть, вы ищете WeakHashMap ? Тогда записи на карте можно будет собирать мусором отдельно.

Хотя по моему опыту это не очень помогло, поэтому я вместо этого построил LRU-кеш, используя LinkedHashMap . Преимущество заключается в том, что я могу контролировать размер, чтобы он не был слишком большим и все же полезным.

0 голосов
/ 12 июля 2009

Насколько большой будет эта карта? Стоит ли усилий, чтобы справиться с этим? Вы измерили потребление памяти этим (хотя оно и стоит, я думаю, что вышеупомянутое в целом нормально, но мой первый вопрос с оптимизацией - «что меня действительно спасет»)

Вы возвращаете ссылку на карту, поэтому вам нужно убедиться, что ваши клиенты не держат эту ссылку (и не предотвращают сбор мусора). Возможно, ваш класс может содержать ссылку и предоставлять метод getKey () для доступа к содержимому карты от имени клиентов? Таким образом вы сохраните контроль над ссылкой на карту в одном месте.

Я бы синхронизировал вышеупомянутое, в случае, если карта будет собирать мусор и два потока одновременно нажмут getMap(). В противном случае вы собираетесь создать две карты одновременно!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...