Ответ Томса правильный, однако код, добавленный к вопросу, не совпадает с предложенным Томом. То, что предлагал Том, больше похоже на это:
class Bloat { // just a heap filler really
public Reader res;
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, Reader res) {
this.ii = ii;
this.res = res;
}
}
// as recommended by Tom Hawtin
class MySoftBloatReference extends SoftReference<Bloat> {
public final Reader hardRef;
MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) {
super(referent, q);
this.hardRef = referent.res;
}
}
//...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 MySoftBloatReference(new Bloat(i, new StringReader("test")), rq));
MySoftBloatReference polled = (MySoftBloatReference) rq.poll();
if (polled != null) {
// close the reference that we are holding on to
try {
polled.hardRef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
i++;
}
}
Обратите внимание, что большая разница в том, что жесткая ссылка на объект, который должен быть закрыт. Окружающий объект может и будет собирать мусор, поэтому вы не попадете в OOM, однако у вас все еще есть шанс закрыть ссылку. Как только вы выйдете из цикла, это также будет сбор мусора. Конечно, в реальном мире вы, вероятно, не сделаете res
членом публичного экземпляра.
Тем не менее, если вы держите открытые ссылки на файлы, вы рискуете исчерпать их, прежде чем исчерпаете память. Вы, вероятно, также захотите иметь кэш LRU, чтобы держать не более 10000 * 500 открытых файлов. Они также могут относиться к типу MyReference, чтобы при необходимости их можно было собирать мусором.
Чтобы немного пояснить, как работает MySoftBloatReference, базовый класс, то есть SoftReference, все еще содержит ссылку на объект, занимающий всю память. Это объект, который необходимо освободить, чтобы предотвратить появление OOM. Однако, если объект освобожден, вам все равно нужно освободить ресурсы, которые использует Bloat, то есть Bloat использует два типа ресурсов, память и дескриптор файла, оба эти ресурса должны быть освобождены, или вы запускаете из одного или другого ресурса. SoftReference справляется с нагрузкой на ресурс памяти, освобождая этот объект, однако вам также необходимо освободить другой ресурс, дескриптор файла. Поскольку Bloat уже был освобожден, мы не можем использовать его для освобождения соответствующего ресурса, поэтому MySoftBloatReference хранит жесткую ссылку на внутренний ресурс, который необходимо закрыть. Как только информация о том, что Bloat освобожден, то есть, когда ссылка появляется в ReferenceQueue, MySoftBloatReference также может закрыть связанный ресурс через жесткую ссылку, которая у него есть.
РЕДАКТИРОВАТЬ: Обновлен код, чтобы он компилировался при броске в класс. Он использует StringReader, чтобы проиллюстрировать концепцию закрытия Reader, который используется для представления внешнего ресурса, который необходимо освободить. В этом конкретном случае закрытие этого потока по сути является запретом и поэтому не требуется, но показывает, как это сделать, если это необходимо.