Я использую библиотеку поиска, которая советует держать объект дескриптора поиска открытым, так как это может принести пользу кешу запросов. Со временем я заметил, что кеш имеет тенденцию раздуваться (несколько сотен мегабайт и продолжает расти), и начали появляться OOM. Невозможно установить ограничения для этого кеша или спланировать, сколько памяти он может использовать. Поэтому я увеличил лимит Xmx, но это лишь временное решение проблемы.
В конце концов я думаю сделать этот объект референтом для java.lang.ref.SoftReference
. Таким образом, если в системе заканчивается свободная память, она отпустит объект, а новый будет создан по требованию. Это немного снизит скорость после нового старта, но это гораздо лучшая альтернатива, чем нажатие OOM.
Единственная проблема, которую я вижу в SoftReferences, заключается в том, что нет чистого способа окончательного оформления их референтов. В моем случае, прежде чем уничтожить дескриптор поиска, мне нужно закрыть его, иначе в системе могут закончиться файловые дескрипторы. Очевидно, я могу обернуть этот дескриптор в другой объект, написать на него финализатор (или зацепиться за ReferenceQueue/PhantomReference) и отпустить. Но эй, каждая статья на этой планете советует не использовать финализаторы, и особенно - финализаторы для освобождения файловых дескрипторов (например, Effective Java ed. II, page 27.).
Так что я несколько озадачен. Стоит ли мне тщательно игнорировать все эти советы и идти дальше. В противном случае, есть ли другие жизнеспособные альтернативы? Заранее спасибо.
РЕДАКТИРОВАТЬ № 1: текст ниже был добавлен после тестирования кода, предложенного Томом Хотином. Мне кажется, что либо предложение не работает, либо я что-то упускаю. Вот код:
class Bloat { // just a heap filler really
private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
private final int ii;
public Bloat(final int ii) {
this.ii = ii;
}
}
// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
private final T hardRef;
MyReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.hardRef = referent;
}
}
//...meanwhile, somewhere in the neighbouring galaxy...
{
ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
int i=0;
while(i<50000) {
// set.add(new MyReference<Bloat>(new Bloat(i), rq));
set.add(new SoftReference<Bloat>(new Bloat(i), rq));
// MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll();
SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll();
if (polled != null) {
Bloat polledBloat = polled.get();
if (polledBloat == null) {
System.out.println("is null :(");
} else {
System.out.println("is not null!");
}
}
i++;
}
}
Если я запущу приведенный выше фрагмент с -Xmx10m
и SoftReferences (как в приведенном выше коде), я напечатаю тонны is null :(
. Но если я заменю код на MyReference
(раскомментируя две строки с помощью MyReference и закомментировав их с помощью SoftReference), я всегда получаю OOM.
Как я понял из совета, наличие жесткой ссылки внутри MyReference
не должно препятствовать попаданию объекта в ReferenceQueue
, верно?