hashMap을 사용하는 곳에서 cpu를 많이 소요하는 부분이 발견되었다.


1. freemarker 예제

 

"TP-Processor215" daemon prio=10 tid=0x55476000 nid=0xfab runnable [0x5495c000]

   java.lang.Thread.State: RUNNABLE

    at java.util.HashMap.get(HashMap.java:303)

    at freemarker.core.RegexBuiltins.getPattern(RegexBuiltins.java:77)

    at freemarker.core.RegexBuiltins$MatcherBuilder.exec(RegexBuiltins.java:277)

    at freemarker.core.MethodCall._getAsTemplateModel(MethodCall.java:93)

    at freemarker.core.Expression.getAsTemplateModel(Expression.java:89)

    at freemarker.core.Expression.isTrue(Expression.java:138)

 

"TP-Processor61" daemon prio=10 tid=0x08fe5800 nid=0x35f9 runnable [0x52575000]

   java.lang.Thread.State: RUNNABLE

    at java.util.HashMap.get(HashMap.java:303)

    at freemarker.core.RegexBuiltins.getPattern(RegexBuiltins.java:77)

    at freemarker.core.RegexBuiltins$MatcherBuilder.exec(RegexBuiltins.java:277)

    at freemarker.core.MethodCall._getAsTemplateModel(MethodCall.java:93)

    at freemarker.core.Expression.getAsTemplateModel(Expression.java:89)

    at freemarker.core.Expression.isTrue(Expression.java:138)

    at freemarker.core.NotExpression.isTrue(NotExpression.java:66)

    at freemarker.core.ParentheticalExpression.isTrue(ParentheticalExpression.java:66)

    at freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:77)

    at freemarker.core.Environment.visit(Environment.java:208)

    at freemarker.core.MixedContent.accept(MixedContent.java:92)

    at freemarker.core.Environment.visit(Environment.java:208)

    at freemarker.core.IfBlock.accept(IfBlock.java:82)


2. 사용자 개발 코드에서 hashmap 사용

"TP-Processor16" daemon prio=10 tid=0x6b2f9400 nid=0x2264 runnable [0x6a45b000]
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap.put(HashMap.java:374)



둘다 HashMap을 사용한 것인데..
아래 beautiful race condtion이라는 블로그에서 hashmap을 사용하면서 문제가 있을만한 부분을 소개했다.

http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html

간단하게 말하면..

Thread1과 Thread2가 resize() 메소드로 들어왔는데..Thread1 이 한 중간에 멈추어졌고, Thread2는 resize를 한 경우에 생길 수 있는 부분을 설명하였다..

1:  // Transfer method in java.util.HashMap -
2:  // called to resize the hashmap
3:  
4:  for (int j = 0; j < src.length; j++) {
5:    Entry e = src[j];
6:    if (e != null) {
7:      src[j] = null;
8:      do {
9:      Entry next = e.next;
     // Thread1 STOPS RIGHT HERE
10:
     int i = indexFor(e.hash, newCapacity);
11:     e.next = newTable[i];
12:     newTable[i] = e;
13:     e = next;
14:   } while (e != null);
15:   }
16: }





Thread 1이 resize 메소드의 9줄까지 실행했다고 가정.

Thread2는 resize 메소드를 다 실행하였다.


즉, A-> B를 가르키고, 있고, B->A를 가르키는 상태이다.

Thread1이 처리를 완료하면서.A-><-B가 되면서..

map에 저장되던 데이터들이 cross refererence되어서 무한 루프에 걸리는 현상이 발견되었다.



HashMap의  get, put 메소드를 본다.
즉, entry가 null일 때까지 for문을 돌릴 수 밖에 없는 구조이기 때문에, 계속 돌아가는 구조이다.


    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }


    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }



따라서, HashMap은 동기화 문제가 발생될 수 있는 곳에 사용해서는 안된다. 차라리 ConcurrentHashMap을 쓰는 것이 맞다.  그게 싫다면, HashMap을 쓸 떄, 프레임웍을 쓰든, 잘 사용하면 좋을 수도 있겠다.


Synchronized hashmap 과 ConcurrentHashMap의 성능에 대한 블로그가 있다.
http://unserializableone.blogspot.com/2007/04/performance-comparision-between.html


요즘에. Apache Common 개발자들이 synchronized HashMap보다는 ConcurrentHashMap을 더 많이 사용하려는 움직임이 있다.

https://issues.apache.org/jira/browse/HTTPCLIENT-903
http://www.mail-archive.com/dev@commons.apache.org/msg18926.html


@comment
ConcurrentHashMap이 SynchronizedHashMap보다 성능이 훨씬 좋거나. 비슷하다. 따라서, ConcurrentHashMap 에 대한 성능차이가 거의 없다면 써도 무방하지 않을까 생각이 든다.

'java core' 카테고리의 다른 글

jdk contant value 모음  (0) 2011.02.08
jdbc specification 보기  (0) 2011.02.07
OOME 검출 방법  (0) 2011.01.06
vebose:gc 를 파일 로그로 남기기  (0) 2010.11.22
안드로이드 코딩 가이드 중 Designing for Performance  (0) 2010.10.04
Posted by '김용환'
,