Как диагностировать 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, с которым вы используете утечки, открываете дескрипторы файлов по дизайну.
Так как это библиотека с открытым исходным кодом, которую вы используете, у вас есть доступ к исходному коду. Поэтому вы должны быть в состоянии подтвердить это, и, если необходимо, исправить это для себя. И чтобы быть хорошим гражданином, внесите исправления в проект, отправив патч.