JDK 1.7: "Слишком много открытых файлов" из-за семафоров POSIX?
Я просмотрел другие подобные вопросы по SO, но они, похоже, вызваны другими проблемами.
Сначала я убедился, что я разумно закрыл все мои файлы, а затем использовал lsof -p <pid of java>
, чтобы посмотреть на список файлов.
Он остается довольно постоянным во время моей работы, но затем периодически я получаю около 10 000 записей, перечисленных в lsof
следующим образом:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
java 36809 smm *235r PSXSEM 0t0 kcms00008FC901624000
java 36809 smm *236r PSXSEM 0t0 kcms00008FC901624000
java 36809 smm *237r PSXSEM 0t0 kcms00008FC901624000
java 36809 smm *238r PSXSEM 0t0 kcms00008FC901624000
java 36809 smm *239r PSXSEM 0t0 kcms00008FC901624000
В man-странице указано, что тип PSXSEM
- это POSIX-семафор. Вы знаете, для чего JDK использует POSIX-семафоры? BTW, приложение представляет собой однопоточное приложение командной строки на данный момент.
Потенциально полезный фон: я впервые заметил это после перехода на JDK 1.7 на Mac OS X 10.7.3:
java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b21)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)
Обновление: repointing $JAVA_HOME
в JDK 1.6, похоже, является обходным решением проблемы.
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04-415-11M3635)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01-415, mixed mode)
Что делает JDK 1.7 по-другому?
Ответы
Ответ 1
Мне удалось проследить его до этого блока кода:
BufferedImage image = null;
ImageInputStream stream = null;
try {
stream = new FileImageInputStream(file);
image = ImageIO.read(stream);
} catch (Exception ex) {
log.error("Image could not be read: "+file.getPath());
} finally {
// ImageIO closes input stream unless null is returned
// http://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageIO.html#read(javax.imageio.stream.ImageInputStream)
if (stream != null && image == null) {
try {
stream.close();
} catch (IOException ex) {
log.error("ERROR closing image input stream: "+ex.getMessage(), ex);
}
}
}
В JavaDocs говорится, что этот метод (в отличие от других) автоматически закрывает поток. Фактически, когда вы пытаетесь вручную закрыть его, он выдает исключение, говорящее "закрыто". Я использовал эту перегрузку, так как другой говорит, что она все равно завершает ее в ImageInputStream
, поэтому я думал, что я сохраню некоторую работу.
Изменение блока для использования обычного FileInputStream
устраняет утечку:
BufferedImage image = null;
InputStream stream = null;
try {
stream = new FileInputStream(file);
image = ImageIO.read(stream);
} catch (Exception ex) {
log.error("Image could not be read: "+file);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException ex) {
log.error("ERROR closing image input stream: "+ex.getMessage(), ex);
}
}
}
Мне кажется, что я ошибся в JDK 1.7, так как здесь 1.6 работал нормально.
Обновление: Я просто отправил отчет об ошибке в Oracle для этой проблемы.
Ответ 2
Я нашел другую причину. Кажется, что метод toRGB() ColorSpace пропускает семафоры. Выполнение следующего кода:
import java.awt.color.ColorSpace;
public class Test
{
public static void main(String[] args) throws Throwable {
final ColorSpace CIEXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
for(int i = 0; i < 10000000; i++) {
CIEXYZ.toRGB(new float[] {80f, 100, 100});
}
}
}
С
java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b21)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)
Выйдет из системных файлов.
EDIT: уже отправил в Oracle
Ответ 3
Обновление: Как отмечали другие пользователи, ImageIO просачивал семафоры в версиях 1.7.0_04 и 1.7.0_05. Сообщения об ошибках от пользователя juancn и пользователь mckamey с тех пор были отмечены как фиксированные и закрытые (спасибо, ребята!). Объяснение:
Это исправление сообщает об утечке обработчиков файлов на macosx. Он упомянул два способа утечки обработчиков: через ImageIO.read(ImageInputStream) и через семафоры.
Я не вижу первой утечки: мы явно закрываем входной поток, если нашли подходящий читатель, и этого достаточно (по крайней мере, на 1.7.4), чтобы освободить дескрипторы файлов.
Однако в случае семафоров мы пропускаем тонны ручек: мы выполняем преобразование цвета для каждой строки jpeg-изображения, и каждый раз мы создаем семафор (потому что мы видим 2 или более CPU, установленный в системе), то мы уменьшаем количество отдельных задач до 1 (потому что у нас есть одна линия сканирования для обработки), и из-за этого никогда не отключайте семафор.
Такая же проблема присутствует и в Linux-системах, но в меньшей степени, потому что мы занимаем одно дескриптор файла для имени семафора, тогда как на macosx мы всегда занимаем новый дескриптор файла.
Предлагаемое исправление просто откладывает создание именованного семафора, пока мы не уточним количество отдельные задачи, поэтому теперь мы не создаем семафоры для чтения изображений и простого цвета конверсий (например, ColorSpace.toRGB()). Кроме того, теперь мы используем указатель pSem как триггер для разрушения семафора.
Несмотря на то, что в их отчетах указано, что исправление находится в версии 8, отчет о backport указывает, что он был исправлен в 1.7.0_06.
Итак, если вы видите это на 1.7.0_04 или 05, обновление по крайней мере 1.7.0_06 позаботится об этой проблеме.