MySQL использование памяти (RAM) увеличивается при использовании ResultSet? - PullRequest
4 голосов
/ 04 августа 2011

Я использую MySQL и Java, чтобы выбрать около 50000 записей.Странно то, что когда я использую ResultSet и метод next () для чтения данных, я вижу, что использование RAM моего приложения Java увеличивается во время выборки.он начинается с 255 МБ и увеличивается до 379 МБ!код, который я использую здесь:

try {
    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/#mysql50#crawler - used in report?" + "user=root&password=&useUnicode=true&characterEncoding=UTF-8");
    Statement st = conn.createStatement();
    ResultSet rsDBReader = st.executeQuery("SELECT Id, Content FROM DocsArchive");
    while (rsDBReader.next()) {
        int docId = rsDBReader.getInt(1);
        String content = rsDBReader.getString(2);
        . . .
        }
    rsDBReader.close();
    st.close();
    conn.close();
} catch (Exception e) {
    System.out.println("Exception in reading data: " + e);
}

Я уверен, что использование памяти для ResultSet, а не для других частей программы.В этой программе мне не нужно обновлять записи, поэтому я хочу удалить каждую запись после окончания работы.Я предполагаю, что прочитанные записи не будут удалены, и программа не освободит их память.поэтому я использовал некоторые уловки, чтобы избежать этого, например, используя следующий код:

Statement st = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);

st.setFetchSize(500);
rsDBReader.setFetchSize(500);

, но они ничего не изменили.: (

Поэтому мне нужен какой-то метод, который удаляет (освобождает) память из строк, которые были прочитаны.

Еще один интересный момент заключается в том, что даже после завершения функции и закрытия ResultSet, Statement и Connectionи, переходя к другой части программы, использование памяти программы не уменьшается! Спасибо

Ответы [ 4 ]

6 голосов
/ 04 августа 2011

Используйте Statement.setFetchSize () , чтобы дать подсказку драйверу о том, что он должен передавать ResultSet для тех, которые содержат определенное количество строк.Насколько я знаю, драйвер MySQL Connector-J понимает подсказку и потоков ResultSet s (но в случае MySQL это ограничено строкой за раз).

Значение по умолчанию является0, гарантирует, что драйвер Connector-J получит полный ResultSet без потоковой передачи.Вот почему вам нужно будет предоставить явное значение - Integer.MIN_VALUE в случае MySQL.

Оператор:

Statement st = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);

не приводит к потоковой передаче ResultSet (по крайней мере,не по собственному желанию).Это просто гарантирует, что результирующий набор не «прокручиваемый» (то есть может быть пройден только в прямом направлении) и не «обновляемый», и базовый курсор будет закрыт при фиксации транзакции.

Как отмечено в Замечания по реализации JDBC для MySQL , приведенный выше оператор (без параметра ResultSet.CLOSE_CURSORS_AT_COMMIT) должен вызываться вместе с вызовом Statement.setFetchSize(Integer.MIN_VALUE), чтобы потоковая передача происходила строка за строкой.Соответствующие предостережения, связанные с таким сценарием, также были задокументированы.

Обратите внимание, что удерживаемость курсора не указана в примере, упомянутом в документации MySQL.Если вам нужно значение, отличное от значения, предоставленного Connection.getHoldability(), то, опять же, этот совет может не применяться.

2 голосов
/ 04 августа 2011

Я бы предложил ограничить количество строк, которые вы получаете в своем запросе.50000 - это много, так почему бы не иметь цикл, который, скажем, каждый раз выбирает, скажем, 1000 строк?

Этого можно добиться с помощью оператора limit, как описано здесь .Всегда лучше быть прагматичным в отношении количества данных, которые вы обрабатываете.Ваш текущий выбор может вернуть 50000 строк сегодня, но что если он вырастет до миллиона завтра?Ваше приложение захлебнется.Итак, делайте вашу обработку шаг за шагом.

0 голосов
/ 15 июля 2016

Обратите внимание, что с последними выпусками Postgres существует аналогичная проблема. Чтобы добиться обработки курсора *, вам необходимо отключить автоматическую фиксацию при подключении connection.setAutoCommit(false) и использовать один оператор в вашем операторе SQL (т.е. оператор, который содержит только одну точку с запятой). У меня это сработало.

Документация Postgres JDBC

0 голосов
/ 04 августа 2011

То, что вы видите, на самом деле является ожидаемым поведением и не обязательно должно указывать на утечку памяти.Экземпляры объектов в Java не собираются сборщиком мусора сразу после того, как они стали недоступны, и большинство виртуальных машин Java очень неохотно возвращают выделенную память обратно операционной системе.

Если вы используете последнюю версию Oracle VM VM идействительно нужен более агрессивный сборщик мусора, вы можете попробовать реализацию G1GC, добавив следующие аргументы в команду java:

-XX: + UnlockExperimentalVMOptions -XX: + UseG1GC

Сборщик мусора G1GCобычно освобождает объекты быстрее, чем сборщик мусора по умолчанию, и неиспользуемая память также освобождается процессом.

...