问题
I'm opening an asset using a FileDescriptor on Android. It appears that garbage collection changes the FileDescriptor's internal descriptor to -1. Attempting to use the FileDescriptor after that throws an exception.
As a sanity check, I added this code to a blank project:
try{
fd = getAssets().openFd("greensleeves.wav").getFileDescriptor();
}catch(IOException e) {
}
System.out.println("file descriptor before gc" + fd);
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("file descriptor before gc" + fd);
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("file descriptor before gc" + fd);
System.gc();
System.out.println("file descriptor after gc" + fd);
System.out.println("file descriptor after gc" + fd);
System.out.println("file descriptor after gc" + fd);
This is the output
System.out I file descriptor before gcFileDescriptor[45]
System.out I file descriptor before gcFileDescriptor[45]
System.out I file descriptor before gcFileDescriptor[45]
dalvikvm D GC_EXPLICIT freed 176K, 3% free 9108K/9316K, paused 2ms+2ms, total 24ms
System.out I file descriptor after gcFileDescriptor[-1]
System.out I file descriptor after gcFileDescriptor[-1]
System.out I file descriptor after gcFileDescriptor[-1]
Why does this happen? How can I safely use a FileDescriptor without worrying about racing against the garbage collector?
回答1:
You aren't retaining a reference to the AssetFileDescriptor
created by openFd()
. When it gets GC'ed, it has an internal ParcelFileDescriptor
that is also eventually GC'ed. That, in turn, has the reference to the FileDescriptor
that you are retrieving in the call to getFileDescriptor()
. It also happens to have a finalize()
method that, when called, will close the file descriptor (by calling IoUtils.closeQuietly(mFd);
). This finalize()
method will be called by the GC when the ParcelFileDescriptor
is collected. This is how the behavior you are observing happens.
My guess as to why sleeping doesn't trigger these events is that there's no pressure on the GC to clean things up.
To test this (and, if I'm right, to prevent the invalidation), maintain a reference to the object returned by openFd()
for the duration of your experiment.
来源:https://stackoverflow.com/questions/30879831/garbage-collection-invalidates-filedescriptor