удалить память, выделенную байтовому массиву - PullRequest
2 голосов
/ 27 апреля 2011

Я получаю записи в цикле while на сокет-сервере. где каждая запись имеет тип сообщения, за которым следуют длина сообщения и фактическое содержание сообщения.

Проблема в том, что, поскольку я получаю около миллиона записей, и каждая запись имеет размер записи 277 байт. Итак, после примерно 40000 записей я получаю ошибку OutOfMemory. Поток кода выглядит примерно так:

while (true) {              
            msgType = dIn.readByte();

            int msgIntType = msgType & 0xff;

                  // get message length

                  int msgIntLen = dIn.readInt();
                  if (msgIntLen != 0) {

     msgContent = new byte[msgIntLen];
                   switch(msgIntType) {
            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent);
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                }               
                break;

}

Я решил проблему OutOfMemory, явно вызвав System.gc () после отправки ACK после обработки каждых 2000 записей, и теперь он отлично работает и способен обрабатывать 1 миллион записей без каких-либо ошибок менее чем за 10 минут. Модифицированный код для оператора case для вызова System.gc () выглядит следующим образом:

            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent);
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                             System.gc();
                }               
                break;

Но я читал здесь на некоторых других постах, что вызов System.gc () не является хорошим подходом к дизайну? Это так ? Если да, не могли бы вы, ребята, предложить мне другой способ избавиться от этой ошибки OutOfMemory?

Заранее спасибо -JJ

РЕДАКТИРОВАТЬ: логика для processData ():

public void processData(byte[] msgContent) throws Exception {

    InputStreamReader inp = new InputStreamReader(

            new ByteArrayInputStream(msgContent));

    BufferedReader br = null;
    try {

        br = new BufferedReader(inp);
                             String line;
        while ((line = br.readLine()) != null) {

                             process each line
                             .
                             }
                  } catch (Exception e) {
        logger.error("exception in " + Utils.getExecutingMethodName(e) 
                + " :" + e.getMessage());
    } finally {
        try {
            if (br != null)
                br.close();
        } catch (IOException e) {
            logger.error("Error: " + e);
        }
    }
}

Ответы [ 4 ]

2 голосов
/ 27 апреля 2011

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

Единственное реальное решение описанной проблемы - убедиться, что вы удалили все ссылки на объекты, которые больше не нужны. Как если бы вы сказали:

byte[] ba=new byte[bignumber];
process(ba);

и затем вы продолжаете и занимаетесь другими делами, ба все еще сидит там, копаясь в памяти. Вы хотите либо выйти из функции, в которой она была определена, либо установить ba = null, чтобы потерять ссылку. Затем gc может перезапустить память.

2 голосов
/ 27 апреля 2011

Не удается закрыть какой-либо ресурс и полагаться на поток финализатора, чтобы забрать их? Или вы только что добавили финализатор (возможно, ненужный), который препятствует быстрому освобождению значительного объема памяти.

0 голосов
/ 27 апреля 2011

Каков тип вашей переменной dIN?Может быть, я неправильно понимаю, но вам действительно нужно прочитать входные данные в байтовом массиве, затем рассмотреть байтовый массив как поток, а затем прочитать поток построчно?Если вы уже знаете структуру своего контента, зачем создавать все промежуточные шаги.С таким же успехом вы могли бы process(dIn).

Кроме того, просто чтобы подтвердить, работает ли он в многопоточной среде ??

0 голосов
/ 27 апреля 2011

Существует ли максимальный размер данных, которые вы получаете (или можете ли вы применить их)? В этом случае вы можете объявить свой байтовый массив как устаревший и использовать его на каждой итерации без выделения дополнительной памяти:

...
private static final int BUFFER_SIZE = 102400; //start with a buffer big enough to lower the chances of resizing it -- e.g. 100K
...
msgContent = new byte[BUFFER_SIZE];
while (true) {              
            msgType = dIn.readByte();

            int msgIntType = msgType & 0xff;

                  // get message length

                  int msgIntLen = dIn.readInt();
                  if (msgIntLen != 0) {
                   if( msgIntLen > msgContent.length ) //only resize when needed otherwise reuse
                     msgContent = new byte[msgIntLen];

                   switch(msgIntType) {
            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent, msgIntLen); //need to change your function to also pass in the size of the message read!
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                }               
                break;

}
...