2013-06-12 3 views
14

서버 구성에서 제어하지 않는 코드에서 사용자 컬렉션이 있고 각 사용자의 배열은 byte[]입니다.바이트 배열의 캐시를 만드는

때때로 이러한 byte[] 배열은 사용자에게 고유합니다. 하지만 종종 동일한 byte[] 배열을 가진 많은 수의 사용자가있을 것입니다.

내 서버의 RAM 소비를 줄이려고합니다.

나는 내 byte[] 배열을 문자열로 변환하고 인턴을 시도했지만, 종종 PERM-GEN 메모리 부족 오류가 발생합니다. 또한 사용자를 위해 byte[] 배열에 액세스하려고 할 때 인코딩/디코딩의 성능이 크게 저하되는 것을 볼 수 있습니다. 그리고 나는 많은 경우 최악의 경우 메모리 사용량이 증가하는 것을 볼 수 있습니다. 미리 문자열은 배열보다 훨씬 큽니다.

Java 배열이 해시 가능하지 않고 SoftReferences가 해당 포인트의 해시를 래핑하지 않을 때 어떻게하면 Set<SoftReference<byte[]>> 조회가 가능합니까? Map<byte[],SoftReference<byte[]>>은 분명히 그 자체가 열쇠이기 때문에 스스로를 물리 치고 수집을 방해합니다. 그리고 Set은 내부적으로 Map의 관점에서 구현됩니다.

어떻게하면 인턴byte[] 배열이 가능합니까?

+0

플라이급 패턴이 떠오른다. 또한 봐 http://stackoverflow.com/questions/1058149/using-a-byby-array-as-hashmap-key-java –

+0

나는 당신이 당신의 바이트 배열을 포장해야한다고 생각합니다. 'new ByteArray (byte [] theBytes)'를 사용하고'ByteArray' 그 밖의'theBytes'에 대한 추가 long-lived 참조를 결코 만들지 않습니다. 그런 다음 'ByteArray'에 대한 소프트 참조가 올바르게 작동합니다. 애플리케이션에 대해서도 WeakHashMap 를 볼 수 있습니다. – Gene

+0

이 바이트 배열의 크기는 얼마나됩니까? 사용자는 어떻게 보류합니까? – fge

답변

5

실제로 많은 동일한 배열이있는 경우 HashSet<ByteBuffer>을 캐시로 사용하십시오. 메소드 array()으로 ByteBuffer 배열을 가져올 수 있고 ByteBuffer 클래스에는 hashCodeequals 메쏘드가 있습니다. 물론 배열이 변경되지 않는 것이 좋습니다.

EDIT2 @Will에서 코멘트는, 배열을 다시 얻을 WeakHashMap<ByteBuffer,WeakReference<ByteBuffer>>를 사용하고 그런 일을 할 수 있도록, 정확 : 내 바이트 [터닝 시도했습니다

public byte[] internalize(byte[] bytes) { 
ByteBuffer wrapped = ByteBuffer.wrap(bytes); 
if(cache.containsKey(wrapped)) { 
    wrapped = cache.get(wrapped).get(); 
} 
else { 
    cache.put(wrapped, new WeakReference<ByteBuffer>(wrapped); 
} 
return wrapped.array(); 
} 
+0

시스템에서 메모리가 필요할 때 해당 캐시를 수집하려면 어떻게해야합니까? 그 당시에는 사용자가 참조하지 않았습니까? – Will

+0

@Will : 내 편집보기 – gma

+1

WeakHashMap '는 의도를 더욱 명확하게 전달할 수 있습니다. 값으로 널을 저장할 수 있습니다. –

2

] 배열을 문자열에 삽입하고 인턴을 수행하지만 종종 PERM-GEN 메모리 부족 오류가 발생합니다.

난 당신이 String.intern() 같은 필요하다고 동의하지만, 표준 구현은 native, 그렇게하지 많은 기쁨입니다.

바이트 배열의 해시 코드를 Map 키로 사용하면 Map<Integer,Collection<SoftReference<byte[]>>> 일 수 있습니다. 그런 다음 intern 메서드는 지정된 바이트 배열과 동일한 코드가있는 기존 바이트 배열 집합을 조회 할 수 있습니다. 일치하는 배열을 검사 할 수있는 작은 배열 집합을 제공해야하는 좋은 해시 코드.


편집 : 명확히하기 위해이 같은

뭔가 :

class ByteArrayCache 
{ 
     private final Map<Integer,Collection<SoftReference<byte[]>> map = new ...; 

     public final byte[] intern(byte[] byteArray) 
     { 
      final int hash = Arrays.hashCode(byteArray); 
      final Collection<SoftReference<byte[]>> arrays = map.get(hash); 
      if (arrays != null) { 
       // Search through arrays for a match, and return the match. 
       // If no match found, add byteArray to the collection and return it 
      } else { 
       // create a new map entry, add byteArray to it, and return byte array 
      } 
     } 
} 
+0

'Integer'와'Set'을 사용하지 않을 때 어떻게 고아가 될 수 있습니까? 그리고 중복이 많지 않은 최악의 상황에서 복싱의 RAM 오버 헤드는 무엇입니까? – Will

+0

배열이 크고 스파 스 (sparse) 한 경우 md5의 첫 번째 바이트가 유용한 해시가 될 수 있습니다. 크기가 작 으면 롤오버가 충분할 것입니다. – tucuxi

+0

@tucuxi'Arrays.hashCode (byte [])'를 구출하기 :) 그러나 근본적인 문제는 – Will

1

내가 구아바 약한 값 맵에 기초하여 캐시를 구현하는 것입니다. 바이트 배열에 대한 더 강력한 참조가 없으면 항목이 자동으로 제거됩니다.

class Cache { 
    private final ConcurrentMap<Key, byte[]> map = new MapMaker().weakValues().makeMap(); 

    private static class Key { 
     byte[] a; 
     int hash; 

     Key(byte[] a) { 
      this.a = a; 
      hash = Arrays.hashCode(a); 
     } 

     @Override 
     public int hashCode() { 
      return hash; 
     } 

     @Override 
     public boolean equals(Object obj) { 
      if (obj instanceof Key) { 
       return Arrays.equals(a, ((Key) obj).a); 
      } 
      return false; 
     } 
    } 

    public byte[] intern(byte[] a) { 
     byte[] a1 = map.putIfAbsent(new Key(a), a); 
     if (a1 != null) { 
      return a1; 
     } 
     return a; 
    } 
} 
관련 문제