#java #performance #file #memory #io
#java #Производительность #файл #память #io
Вопрос:
В настоящее время в моем приложении есть массивный файл журнала, который мне нужно отправить в конечную точку. Я периодически запускаю метод, который будет считывать весь файл в список, выполнять некоторое форматирование, чтобы конечная точка приняла его, а затем преобразовала строку с помощью StringBuilder, возвращает эту строку, а затем отправляет ее в мою конечную точку. О, я забыл упомянуть, я собираю данные порциями по X символов. Я вижу некоторые проблемы с памятью в своем приложении и пытаюсь справиться с этим.
Итак, вот как я разделяю данные на временный список
if (logFile.exists()) {
try (BufferedReader br = new BufferedReader(new FileReader(logFile.getPath()))) {
String line;
while ((line = br.readLine()) != null) {
if (isJSONValid(line)) {
temp.add(line);
tempCharCount = line.length();
}
if (tempCharCount >= LOG_PARTITION_CHAR_COUNT) {
// Formatting for the backend
String tempString = postFormat(temp);
// Send
sendLogs(tempString);
// Refresh
temp = new ArrayList<>();
tempCharCount = 0;
}
}
// Send "dangling" data
// Formatting for the backend
String tempString = postFormat(temp);
// Send
sendLogs(tempString);
} catch (FileNotFoundException e) {
Timber.e(new Exception(e));
} catch (IOException e) {
Timber.e(new Exception(e));
}
Итак, когда мы достигаем нашего предела раздела для количества символов, вы можете видеть, что мы запускаем
String tempString = postFormat(temp);
Здесь мы удостоверяемся, что наши данные отформатированы в строку данных json, которую примет конечная точка.
private String postFormat(ArrayList<String> list) {
list.add(0, LOG_ARRAY_START);
list.add(LOG_ARRAY_END);
StringBuilder sb = new StringBuilder();
for (int stringCount = 0; stringCount < list.size(); stringCount ) {
sb.append(list.get(stringCount));
// Only add comma separators after initial element, but never add to final element and
// its preceding element to match the expected backend input
if (stringCount > 0 amp;amp; stringCount < list.size() - 2) {
sb.append(",");
}
}
return sb.toString();
}
Как вы можете себе представить, если у вас большой файл журнала, и эти запросы выполняются асинхронно, тогда мы будем использовать много памяти. После завершения работы нашего Stringbuilder мы возвращаем строку, которая в конечном итоге будет сжата в gzip и отправлена в конечную точку.
Я ищу способы уменьшить использование памяти этим. Я немного профилировал его сбоку и мог видеть, насколько он явно неэффективен, но не уверен, как я могу сделать это лучше. Приветствуются любые идеи.
Комментарии:
1. Я должен упомянуть, что я уменьшил лимит символов и начал чаще отправлять журналы, но я думаю, что это обходные пути в хороший день для плохого управления памятью. Я вижу, что мое приложение здесь неэффективно, но я ищу идеи по его улучшению.
2. Этот процесс звучит довольно неприятно — рассмотрите совершенно другой подход после прочтения этого .
Ответ №1:
У меня есть одно предложение для вас.
Форматированный вывод во временный файл — вы можете записать форматированный вывод во временный файл. После завершения преобразования вы можете читать временные файлы и отправлять в конечную точку. Если у вас нет проблем с последовательностью, вы можете использовать несколько потоков для добавления одного и того же файла. При таком подходе вы не штурмуете какие-либо данные в памяти во время преобразования, что сэкономит много памяти.
Комментарии:
1. Хороший совет, даст этому шанс 🙂