Как диагностировать File.delete(), возвращая false/find unclosed streams?

Я работаю со сторонней библиотекой манипуляций JPEG/EXIF ​​(Mediautil), которая вызывает у меня некоторые головные боли. Я хочу изменить данные EXIF ​​изображения. Для этого мне нужно написать обновленную версию во временный файл, удалить оригинал, а затем переименовать файл temp в исходное имя.

Моя проблема заключается в том, что вызов File.delete() завершается с ошибкой и возвращает false, по-видимому, потому что библиотека все еще открыта каким-то образом - но я сделал все, что я могу найти в API, чтобы закрыть все потоки. Еще хуже: проблема, по-видимому, зависит от времени, и тесты Unit, где это происходит, иногда терпят неудачу, а иногда и нет, но код не многопоточен.

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

Есть идеи, как атаковать эту проблему?

Edit: Это в Windows XP, Java 6. И еще одно: я выяснил, что если я вызываю System.gc() перед вызовом File.delete(), он работает - по-видимому, потому что это вызывает некоторый финализатор. Таким образом, это определенно кажется незакрытым потоком.

Ответы

Ответ 1

Я хотел бы помочь с отладчиком здесь. Быстрый поиск материала java.io показывает, что единственный вероятный кандидат finalize() находится в FileOutputStream. Таким образом, удалите точку останова там, запустите свою программу и попробуйте System.gc() запустить FileOutputStream.finalize(), чтобы освободить ваш поток. Это должно дать вам ответ о том, действительно ли ваша проблема.

Как только вы сможете воспроизвести это, вам нужно начать сопоставление экземпляров FileOutputStream с их завершением. Хороший отладчик даст вам внутренние идентификаторы объектов JVM для каждого объекта, поэтому, если вы можете отслеживать идентификаторы OID по мере их создания и отслеживать их по мере их завершения, то, надеюсь, вы сможете связать вызов ключа с finalize с определенным вызовом new new FileOutputStream.

Возможно, это длинный показ, в зависимости от сложности вашего приложения.

Ответ 2

Если вы используете FileOutputStream, его закрытие явно разрешает удаление файла.

например. вместо:

File myFile = new File("test.txt");
myCustomStreamProcess(new FileOutputStream(myFile));
boolean test = myFile.delete(); //May return false

вам следует:

File myFile = new File("test.txt");
FileOutputStream fos = new FileOutputStream(myFile);
myCustomStreamProcess(fos);
fos.close(); //Allow the document to be deleted afterwards
boolean test = myFile.delete(); //Should always return true

Ответ 3

Почему бы вам не переименовать файл, прежде чем вы откроете библиотеку? Тогда, возможно, используйте Java File.deleteOnExit(), чтобы удалить переименованный файл. Например:

 File jpeg = new File("image.jpg");
 File temp = new File(jpeg + ".temp.jpg");
 jpg.renameTo(temp);
 SomeObj result = exifLibrary(temp); // or exifLibrary(new FileInputStream(temp);
 OutputStream jpegStream = new FileOutputStream(jpeg);
 output.write(result.bytes();
 output.close();
 temp.deleteOnExit();
 temp.delete();

Ответ 4

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

Я думаю, вы могли бы определить настоящую проблему; то есть API, с которым вы используете утечки, открываете дескрипторы файлов по дизайну.

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