2012-03-24 6 views
4

У меня есть клиент/серверное приложение, в котором клиентское приложение откроет файлы. Эти файлы разбиваются на куски и отправляются на сервер.Как создать: Избегайте утечки ресурсов при случайном доступе к файлам

Клиент не только отправляет куски файлов, но также отправляет другие данные. Каждое сообщение (data или filechunk) имеет уровень приоритета.

Все сообщения буферизуются и записываются в поток TCP в соответствии с их приоритетом. В настоящее время потоки ввода и вывода закрываются с обеих сторон (клиент/сервер) всякий раз, когда файл полностью отправлен или получен. Это означает, что потоки остаются открытыми до тех пор, пока требуется отправить файлы.

Соединение TCP разрешено сбой, так как оно будет повторно подключаться, и отправка сообщения будет возобновлена, поэтому потоки будут закрыты в какой-то момент.

НО, если и когда JVM будет убит, например, потоки не будут очищены.

Моей первой мыслью по решению этого было добавить код очистки в финализаторы, но я понимаю, что они могут не работать, когда JVM убивается (или вызывается System.exit).

Моей второй мыслью является переписать часть приложения и использовать потоки только до тех пор, пока требуется чтение/запись одного фрагмента. Поэтому я бы в конечном итоге открывал/закрывал файлы столько раз, сколько есть кусков. Преимущество этого метода заключается в том, что я могу использовать конструкцию try-catch-finally, но у меня есть ощущение, что вы открываете и закрываете файлы, что часто подразумевает справедливую часть накладных расходов.

Итак, как очистить ресурсы, когда дизайн не позволяет использовать finally {} блоки? Или следует избегать такой конструкции, возможно, таким же образом, как я описал?

Я также обеспокоен возможностью иметь как можно больше файлов, так как есть приоритеты (которые теоретически неограничены).

Большинство файлов, как правило, представляют собой несколько больших KiB, но в некоторых случаях они могут достигать нескольких GB.

Заранее благодарим за ввод.

EDIT: Добавлено изображение

File transfer as it is

+0

Возможно, я не был чист: потоки, на которые я ссылаюсь, это FileInputStream/FileOutputStreams. TCP-соединение, с другой стороны, постоянно хранится в сети (если оно не выполняется, происходит повторное подключение). –

+0

Я не уверен, что понял ваш вопрос. Очищая поток, вы хотите удалить частично перенесенные файлы? – WilQu

+0

Нет, я имею в виду вызов close() в FileInputStream/FileOutputStream разных файлов. Я попытаюсь сделать что-то, чтобы сделать этот вопрос менее загадочным. Через несколько минут я вернусь с редактированием. –

ответ

2

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

Хотя это общая проблема, в вашем конкретном случае я думаю, что не о чем беспокоиться. Вы только открываете свои файлы для чтения, поэтому нет постоянного состояния, которое может испортить ваше приложение (как я понимаю, с другой стороны TCP-соединения можно корректно обрабатывать разъединения). Открываемый файл - это своеобразное состояние, которое является внутренним для приложения. Если приложение убито, операционная система выполняет очистку всех блокировок или любых структур данных, которые он может использовать внутри, для обработки операций над этим файлом. Это означает, что в ядре ОС не останется «мусора», и хотя он не является ни элегантным, ни рекомендуемым способом очистки, он просто работает (конечно, использовать это только для чрезвычайных ситуаций, а не как обычный способ обработки вещей). Убийство вашего приложения автоматически закрывает открытые файлы и не должно приводить файлы в какое-либо несогласованное состояние.

Конечно, если приложение убито, оно не завершит выполнение каких-либо операций. Это может привести к несогласованному состоянию на уровне приложения или другим проблемам с некоторыми видами логики приложения, но все равно не может повредить какие-либо нововведения на уровне ОС (при условии, что ОС не глючит). Вы можете потерять данные или разорвать состояние своего приложения, но не сможете разбить файловую систему или данные в ядре.

Таким образом, вы можете столкнуться с проблемами, если вы убьете приложение, в котором у вас есть операция в стиле транзакции (которую вы хотите полностью или полностью выполнить, но никакое промежуточное состояние никогда не должно становиться видимым для внешнего мира). Например, если бы у вас был файл, и вам нужно было заменить его более новой версией. Если вы сначала усекаете старый файл, а затем записываете в него новое содержимое (что является очевидным способом сделать это), если ваше приложение убито после обрезания файла, но перед написанием нового содержимого у вас проблемы. Однако, чтобы спровоцировать такие риски, вам нужно изменить состояние, то есть написать что-то. Если вы только читаете материал, вы почти наверняка будете в безопасности.

Если вы столкнулись с таким случаем, вы можете воспользоваться несколькими путями. Один пытается сделать приложение пуленепробиваемым и гарантировать, что он всегда хорошо очищается. На практике это очень сложно. Вы можете добавить крючки завершения к Java-приложению, которое будет выполняться при закрытии приложения, но оно работает только для управляемого выключения (например, обычного kill (SIGTERM) в Linux). Но это не защитит вас от приложения, которое будет насильно убито администратором (kill -9 на Linux), OOM-killer (также Linux) и т. Д. Конечно, другие операционные системы имеют эквиваленты этих ситуаций или другие случаи, когда приложение закрывается таким образом, что он не может управлять. Если вы не пишете приложение реального времени, работающее в контролируемой аппаратной и программной среде, практически невозможно предотвратить все попытки приложения принудительно прекратить работу и предотвратить выполнение его процедур очистки.

Таким образом, разумный компромисс часто принимает только простые меры предосторожности в приложении (например, крюки отключения), но следует помнить, что вы не можете предотвратить все и, следовательно, сделать ручную очистку возможной и легкой. Например, решением для перезаписи файла было бы разделение операции на первое перемещение старого файла на новое имя для сохранения в качестве резервной копии (эта операция обычно является атомарной на уровне ОС и поэтому безопасна), а затем записывая новое содержимое файла для старого имени, а затем после проверки того, что новый файл был правильно написан, удалите резервную копию. Таким образом, если приложение убито между операциями, существует простая процедура очистки: перенос файла резервной копии на исходное имя и, таким образом, возврат к более старому, но согласованному состоянию. Это может быть выполнено вручную (но должно быть документировано), или вы можете добавить скрипт очистки или специальную команду «очистка» для вашего приложения, чтобы сделать эту операцию простой, видимой и удалить возможность человеческой ошибки во время процедуры.Предполагая, что ваше приложение убито редко, это обычно более эффективно, чем тратить много времени на попытку сделать приложение пуленепробиваемым только для того, чтобы понять, что это действительно невозможно. Обмануть отказ часто лучше, чем пытаться его предотвратить (не только в программировании).

Вы все еще можете сгореть на OS и аппаратном уровне, но это действительно сложно предотвратить с помощью аппаратного обеспечения и ОС. Даже если ваше приложение увидит новый файл в правильном месте, он, возможно, не был физически записан на диск, и если он будет находиться только в кеше, он не потеряется, если ваше приложение будет убито, но потеряется, если кто-то вытащит мощность подключите машину. Но дело в таком провале - совершенно другая история.

Короче говоря, в вашем конкретном случае, когда вы читаете только файлы, ОС должна выполнять всю очистку, если ваше приложение убито, а в других случаях некоторые советы упоминаются выше.

+0

В ваших словах много мудрости. На уровне приложения/бизнес-логики все разрешено терпеть неудачу, как вы полагаете, нужно делать. –

+0

Я рассмотрю добавление крючков остановки, спасибо за этот совет! Это облегчение, чтобы кто-то подтвердил, что я не повредит ОС и ее дескрипторы/дескрипторы файлов. Но я должен упомянуть, что у I * do * есть файлы, открытые для записи (связь двунаправленная), я знаю, что это не ясно из моего сообщения, поэтому я принимаю это как правильный ответ. Знаете ли вы, что если много ручек файлов открыты в течение длительного времени, это может привести к утечке производительности? Я считаю, что количество дескрипторов файлов ограничено на уровне ОС. –

+0

И чтобы быть полным, я должен добавить, что я изучаю это, потому что я был жертвой описанной здесь ошибки (http://stackoverflow.com/questions/2614774/what-can-cause-java-to-keep-running -after-system-exit) на Win2008R2. Решение, предложенное там, не работает. –

1

Посмотрите java.lang.Runtime.addShutdownHook(). Я бы попытался добавить крючок, который закрывает все открытые потоки, список которых вы должны поддерживать для этого случая. Однако обратите внимание на следующее:

В редких случаях виртуальная машина может прервать работу, то есть прекратить работу, не закрывая ее полностью. Это происходит, когда виртуальная машина завершается извне, например, с сигналом SIGKILL в Unix или вызовом TerminateProcess в Microsoft Windows. Кроме того, виртуальная машина может прервать работу, если родной метод пойдет наперекосяк, например, повреждая внутренние структуры данных или пытаясь получить доступ к несуществующей памяти. Если виртуальная машина прерывается, то не может быть гарантирована, будут ли выполняться какие-либо выключения.

+0

Спасибо, я посмотрю! –

Смежные вопросы