Garbage Collection invalidates FileDescriptor?

486 views Asked by At

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

There are 1 answers

0
Ted Hopp On BEST ANSWER

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.