Альтернативы интернированию строк Java

Поскольку интернирование строк по умолчанию в Java получило много негативных отзывов в прессе, я ищу альтернативу.

Можете ли вы предложить API, который является хорошей альтернативой интернированию строк Java? В моем приложении используется Java 6. Мое требование в основном состоит в том, чтобы избежать дублирования строк с помощью интернирования.

По поводу плохой прессы:

  • Строковый стажер реализован с помощью нативного метода. И реализация C использует фиксированный размер некоторых записей 1k и очень плохо масштабируется для большого количества строк.
  • Java 6 хранит интернированные строки в Perm gen. И, следовательно, не GC'd и, возможно, привести к ошибкам perm gen. Я знаю, что это исправлено в java 7, но я не могу перейти на java 7.

Зачем мне нужно использовать intering?

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

person MoveFast    schedule 09.10.2012    source источник
comment
Часть меня уважает требования, которые вы публикуете, но если вам достаточно плохой прессы, чтобы избежать их, тогда мне действительно нужно спросить, как вы профилировали свое приложение (если вообще), чтобы определить, что строки Java не подходят.   -  person djechlin    schedule 09.10.2012
comment
Вы заметили проблему в своем приложении, связанную с этими вопросами? Если нет, то я бы не беспокоился об этом.   -  person Keppil    schedule 09.10.2012
comment
@Keppil, в моем приложении сотни тысяч повторяющихся строк. Поэтому стажировка для меня обязательна.   -  person MoveFast    schedule 09.10.2012
comment
@pst Надеюсь, я ответил на твой вопрос. Я предполагаю, что вы имеете в виду Map, а не Set. Мне нужно что-то, что является потокобезопасным и будет GC строк, как только на них больше не ссылаются. что-то вроде параллельной слабой хеш-карты.   -  person MoveFast    schedule 09.10.2012
comment
@Chandra Пожалуйста, воздержитесь от ненужных правок.   -  person NullUserException    schedule 09.10.2012
comment
@pst, если я использую Set, я смогу проверить, является ли эта строка дубликатом, но не смогу получить ссылку на исходную строку, чтобы не использовать повторяющуюся строку.   -  person MoveFast    schedule 09.10.2012
comment
@ManojGumber stackoverflow. com/questions/8853515/ (импл с картой), stackoverflow.com/questions/3972841/ (упоминает Guava Interner)   -  person    schedule 09.10.2012
comment
@pst Я ценю идею использования префикса, но я хочу не делать все тяжелое самостоятельно и захочу повторно использовать доступный API.   -  person MoveFast    schedule 09.10.2012
comment
Стажер @pst Guava выглядит многообещающе.   -  person MoveFast    schedule 09.10.2012


Ответы (1)


Строковый стажер реализован с помощью нативного метода. И реализация C использует фиксированный размер некоторых записей 1k и очень плохо масштабируется для большого количества строк.

Он плохо масштабируется для многих тысяч строк.

Java 6 хранит интернированные строки в Perm gen. И поэтому не GC'd

Он будет очищен, когда будет очищен перманентный генератор, что случается не часто, но это может означать, что вы достигнете максимума этого пространства, если не увеличите его.

Мое приложение представляет собой серверное приложение с размером кучи 10-20G для разных развертываний.

Я предлагаю вам рассмотреть возможность использования памяти вне кучи. У меня 500 ГБ в памяти вне кучи и около 1 ГБ в куче в одном приложении. Это полезно не во всех случаях, но заслуживает внимания.

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

Для этого я использовал простой массив String. Это очень легкий вес, и вы можете легко контролировать верхнюю границу хранимых строк.


Вот пример универсального интернера.

class Interner<T> {
    private final T[] cache;

    @SuppressWarnings("unchecked")
    public Interner(int primeSize) {
        cache = (T[]) new Object[primeSize];
    }

    public T intern(T t) {
        int hash = Math.abs(t.hashCode() % cache.length);
        T t2 = cache[hash];
        if (t2 != null && t.equals(t2))
            return t2;
        cache[hash] = t;
        return t;
    }
}

Интересным свойством этого кеша является то, что он не является потокобезопасным.

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

person Peter Lawrey    schedule 09.10.2012
comment
Для подхода с массивом строк это была просто неупорядоченная коллекция? - person ; 09.10.2012
comment
@peter Lawrey, как он справится со столкновением. то есть когда две строки с разными хэш-кодами указывают на один и тот же индекс кеша? Есть ли предположение, что вы делаете Interner размера, который вы ожидаете от количества разных строк? - person MoveFast; 10.10.2012
comment
Если есть коллизия, он заменяет значение там. Размер должен быть в 2-3 раза больше, чем количество строк, которое вы можете считать оптимальным, поскольку он не пытается очень умно обрабатывать коллизии. Кстати, даже HashMap будет в 1,4-2,8 раза больше записей. Вы можете использовать primes.utm.edu/curios, чтобы найти интересные простые числа любого размера. - person Peter Lawrey; 10.10.2012