Hadoop Mapper перерасход памяти (кучи)

Я написал простую программу соединения хэшей на карте Hadoop. Идея заключается в следующем:

Небольшая таблица распространяется на каждый сопоставитель с помощью DistributedCache, предоставляемого инфраструктурой Hadoop. Большая таблица распределена по картографам с размером разделения 64M. Код установки картографа создает хэш-карту, считывая каждую строку из этой маленькой таблицы. В коде картографа каждый ключ ищется (получается) в хэш-карте, и если ключ существует в хэш-карте, он записывается. На данный момент нет необходимости в редукторе. Это код, который мы используем:

    public class Map extends Mapper<LongWritable, Text, Text, Text> {
        private HashMap<String, String> joinData = new HashMap<String, String>();

        public void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {

            String textvalue = value.toString();
            String[] tokens;
            tokens = textvalue.split(",");
            if (tokens.length == 2) {
                String joinValue = joinData.get(tokens[0]);
                if (null != joinValue) {
                    context.write(new Text(tokens[0]), new Text(tokens[1] + ","
                            + joinValue));
                }
            }
        }

    public void setup(Context context) {
        try {
            Path[] cacheFiles = DistributedCache.getLocalCacheFiles(context
                    .getConfiguration());
            if (null != cacheFiles && cacheFiles.length > 0) {
                String line;
                String[] tokens;
                BufferedReader br = new BufferedReader(new FileReader(
                        cacheFiles[0].toString()));
                try {
                    while ((line = br.readLine()) != null) {

                        tokens = line.split(",");
                        if (tokens.length == 2) {
                            joinData.put(tokens[0], tokens[1]);
                        }
                    }
                    System.exit(0);
                } finally {
                    br.close();
                }
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

При тестировании этого кода наша маленькая таблица была 32 МБ, а большая — 128 МБ, один главный и 2 подчиненных узла.

Этот код не работает с приведенными выше входными данными, когда у меня есть 256 МБ кучи. Я использую -Xmx256m в файле mapred.child.java.opts в файле mapred-site.xml. Когда я увеличиваю его до 300 м, он работает очень медленно, а при 512 м достигает максимальной пропускной способности.

Я не понимаю, где мой картограф потребляет столько памяти. С входными данными, приведенными выше, и с кодом картографа я не ожидаю, что моя память кучи когда-либо достигнет 256 МБ, но она терпит неудачу с ошибкой пространства кучи java.

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

ИЗМЕНИТЬ:

13/03/11 09:37:33 WARN mapred.JobClient: Use GenericOptionsParser for parsing the arguments. Applications should implement Tool for the same.
13/03/11 09:37:33 INFO input.FileInputFormat: Total input paths to process : 1
13/03/11 09:37:33 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
13/03/11 09:37:33 WARN snappy.LoadSnappy: Snappy native library not loaded
13/03/11 09:37:34 INFO mapred.JobClient: Running job: job_201303110921_0004
13/03/11 09:37:35 INFO mapred.JobClient:  map 0% reduce 0%
13/03/11 09:39:12 INFO mapred.JobClient: Task Id : attempt_201303110921_0004_m_000000_0, Status : FAILED
Error: GC overhead limit exceeded
13/03/11 09:40:43 INFO mapred.JobClient: Task Id : attempt_201303110921_0004_m_000001_0, Status : FAILED
org.apache.hadoop.io.SecureIOUtils$AlreadyExistsException: File /usr/home/hadoop/hadoop-1.0.3/libexec/../logs/userlogs/job_201303110921_0004/attempt_201303110921_0004_m_000001_0/log.tmp already exists
    at org.apache.hadoop.io.SecureIOUtils.insecureCreateForWrite(SecureIOUtils.java:130)
    at org.apache.hadoop.io.SecureIOUtils.createForWrite(SecureIOUtils.java:157)
    at org.apache.hadoop.mapred.TaskLog.writeToIndexFile(TaskLog.java:312)
    at org.apache.hadoop.mapred.TaskLog.syncLogs(TaskLog.java:385)
    at org.apache.hadoop.mapred.Child$4.run(Child.java:257)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:416)
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1121)
    at org.apache.hadoop.mapred.Child.main(Child.java:249)

attempt_201303110921_0004_m_000001_0: Exception in thread "Thread for syncLogs" java.lang.OutOfMemoryError: Java heap space
attempt_201303110921_0004_m_000001_0:   at java.io.BufferedOutputStream.<init>(BufferedOutputStream.java:76)
attempt_201303110921_0004_m_000001_0:   at java.io.BufferedOutputStream.<init>(BufferedOutputStream.java:59)
attempt_201303110921_0004_m_000001_0:   at org.apache.hadoop.mapred.TaskLog.writeToIndexFile(TaskLog.java:312)
attempt_201303110921_0004_m_000001_0:   at org.apache.hadoop.mapred.TaskLog.syncLogs(TaskLog.java:385)
attempt_201303110921_0004_m_000001_0:   at org.apache.hadoop.mapred.Child$3.run(Child.java:141)
attempt_201303110921_0004_m_000001_0: log4j:WARN No appenders could be found for logger (org.apache.hadoop.hdfs.DFSClient).
attempt_201303110921_0004_m_000001_0: log4j:WARN Please initialize the log4j system properly.
13/03/11 09:42:18 INFO mapred.JobClient: Task Id : attempt_201303110921_0004_m_000001_1, Status : FAILED
Error: GC overhead limit exceeded
13/03/11 09:43:48 INFO mapred.JobClient: Task Id : attempt_201303110921_0004_m_000001_2, Status : FAILED
Error: GC overhead limit exceeded
13/03/11 09:45:09 INFO mapred.JobClient: Job complete: job_201303110921_0004
13/03/11 09:45:09 INFO mapred.JobClient: Counters: 7
13/03/11 09:45:09 INFO mapred.JobClient:   Job Counters 
13/03/11 09:45:09 INFO mapred.JobClient:     SLOTS_MILLIS_MAPS=468506
13/03/11 09:45:09 INFO mapred.JobClient:     Total time spent by all reduces waiting after reserving slots (ms)=0
13/03/11 09:45:09 INFO mapred.JobClient:     Total time spent by all maps waiting after reserving slots (ms)=0
13/03/11 09:45:09 INFO mapred.JobClient:     Launched map tasks=6
13/03/11 09:45:09 INFO mapred.JobClient:     Data-local map tasks=6
13/03/11 09:45:09 INFO mapred.JobClient:     SLOTS_MILLIS_REDUCES=0
13/03/11 09:45:09 INFO mapred.JobClient:     Failed map tasks=1

person 0xhacker    schedule 09.03.2013    source источник


Ответы (1)


Трудно сказать наверняка, куда идет потребление памяти, но вот несколько указателей:

  • Вы создаете 2 объекта Text для каждой строки вашего ввода. Вы должны просто использовать 2 объекта Text, которые будут инициализированы один раз в вашем Mapper как переменные класса, а затем для каждой строки просто вызывать text.set(...). Это распространенный шаблон использования для шаблонов Map/Reduce, который может значительно сэкономить память.
  • Вам следует рассмотреть возможность использования формата SequenceFile для ввода, что позволит избежать необходимости анализировать строки с помощью textValue.split, вместо этого вы будете иметь эти данные непосредственно в виде массива. Я несколько раз читал, что такое разбиение строк может быть довольно интенсивным, поэтому вам следует избегать как можно большего, если память действительно является проблемой. Вы также можете подумать об использовании KeyValueTextInputFormat, если, как в вашем примере, вас интересуют только пары ключ/значение.

Если этого недостаточно, я бы посоветовал взглянуть на эта ссылка, особенно часть 7, которая дает вам очень простой способ профилировать ваше приложение и посмотреть, что и где размещается.

person Charles Menguy    schedule 09.03.2013
comment
К сожалению, я не очень хорошо разбираюсь в java, но поскольку разбиение выполняется на небольшой таблице или таблице, распространяемой на каждый маппер через распределенный кеш, а не через обычную структуру mr для хаупа, я не думаю, что смогу использовать KeyValueTextInputFormat, поскольку он работает на большая таблица.. Проблема в том, что весь картограф дает сбой из-за ошибки пространства кучи в самом коде установки.. Таким образом, функция карты даже не вызывается. - person 0xhacker; 11.03.2013
comment
@ mc_87 Не могли бы вы опубликовать трассировку стека, чтобы лучше понять, что происходит? - person Charles Menguy; 11.03.2013
comment
Спасибо за вашу помощь. Я редактировал с ошибкой, вызванной мастер-узлом. - person 0xhacker; 11.03.2013
comment
Трудно поверить, что тебе еще не проголосовали за это, Чарльз. Вот мой. - person rICh; 12.09.2013