ITEM 7: Eliminate Object Reference
์๋ฐ๋ ๊ฐ๋น์ง ์ปฌ๋ ํฐ๊ฐ ๋ค ์ด ๊ฐ์ฒด๋ฅผ ์์์ ํ์ํด๊ฐ๋ค๊ณ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ๋ ์ด์ ์ ๊ฒฝ ์ฐ์ง ์์๋ ๋๋ ๊ฒ์ ์๋๋ค.
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = 0;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
return elements[--size];
}
// ์์๋ฅผ ์ํ ๊ณต๊ฐ์ ์ ์ด๋ ํ๋ ์ด์ ์ฌ์ ๋ฅผ ๋๋ฉฐ, ๋๋ ค์ผํ๋ ๊ฒฝ์ฐ ๋๋ฐฐ ์ด์ ๋๋ฆฐ๋ค.
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
์ ์คํ ์ฝ๋์๋ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฌธ์ ์ ์ด ์๋ค. ์์ ์คํ์ ์ฌ์ฉํ๋ ํ๋ก๊ทธ๋จ์ ์ค๋ ์คํํ๋ฉด, ์ ์ฐจ GC ํ๋๊ณผ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๋์ด๋ ๊ฒฐ๊ตญ ์ฑ๋ฅ์ด ์ ํ๋ ๊ฒ์ด๋ค. ์๋์ ์ผ๋ก ๋๋ฌธ ๊ฒฝ์ฐ์ด์ง๋ง ์ฌํ ๋๋ ๋์คํฌ ํ์ด์ง์ด๋ OutOfMemoryError
๋ฅผ ์ผ์ผ์ผ ์๊ธฐ์น ์๊ฒ ์ข
๋ฃ๋๊ธฐ๋ ํ๋ค.
์ ์คํ์ ์คํ์ด ๋์๋ค๊ฐ ์ฃผ๋ ๊ฒฝ์ฐ์ ์คํ์์ ๊บผ๋ด์ง ๊ฐ์ฒด๋ค์ ๊ฐ๋น์ง ์ปฌ๋ ํฐ๊ฐ ํ์ ํ์ง ์๋๋ค. ์ด ์คํ์ด ๊ทธ ๊ฐ์ฒด๋ค์ ๋ค ์ด ์ฐธ์กฐ(obsolete reference: ์์ผ๋ก ๋ค์ ์ฐ์ง ์์ ์ฐธ์กฐ)๋ฅผ ์ฌ์ ํ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฐ์ฒด ์ฐธ์กฐ ํ๋๋ฅผ ์ด๋ ค๋๋ฉด GC๋ ๊ทธ ๊ฐ์ฒด๋ฟ๋ง ์๋๋ผ ๊ทธ ๊ฐ์ฒด๊ฐ ์ฐธ์กฐํ๋ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ํ์ํด๊ฐ์ง ๋ชปํ๋ค. ๊ทธ๋์ ๋จ ๋ช ๊ฐ์ ๊ฐ์ฒด๊ฐ ๋งค์ฐ ๋ง์ ๊ฐ์ฒด๋ฅผ ํ์๋์ง ๋ชปํ๊ฒ ํ ์ ์๊ณ , ์ ์ฌ์ ์ผ๋ก ์ฑ๋ฅ์ ์ ์ํฅ์ ์ค ์ ์๋ค.
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
์ด๋ ๋ค์๊ณผ ๊ฐ์ด null ์ฒ๋ฆฌ ๋ฅผ ํตํด ํด๋น ์ฐธ์กฐ ํด์ ์ฒ๋ฆฌ๋ฅผ ํ์ฌ ํด๊ฒฐํ ์ ์๋ค. ๋ค ์ด ์ฐธ์กฐ๋ฅผ null
์ฒ๋ฆฌํ์ฌ ์ค์๋ก ํด๋น ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด ์์คํ
์ ์ฆ์ NullPointException
์ ๋์ง๋ฉฐ ์ข
๋ฃ๋๋ฉฐ, ํ๋ก๊ทธ๋จ ์ค๋ฅ๋ ๊ฐ๋ฅํ ์ด๋ฐ์ ๋ฐ๊ฒฌํ๋ ๊ฒ์ด ์ข๋ค.
ํ์ง๋ง ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๋ค ์ฐ์๋ง์ null
์ฒ๋ฆฌ๋ฅผ ํ ํ์๋ ์๊ณ , ์ด๋ ํ์ ์ด์์ผ๋ก ํ๋ก๊ทธ๋จ์ ์ง์ ๋ถํ๊ฒ ๋ง๋ค ์ ์๋ค. ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ null ์ฒ๋ฆฌํ๋ ์ผ์ ์์ธ์ ์ธ ๊ฒฝ์ฐ์ฌ์ผ ํ๋ค. ๋ค์ด ์ฐธ์กฐ๋ฅผ ํด์ ํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ๊ทธ ์ฐธ์กฐ๋ฅผ ๋ด์ ๋ณ์๋ฅผ ์ ํจ ๋ฒ์(scope) ๋ฐ์ผ๋ก ๋ฐ์ด๋ด๋ ๊ฒ์ด๋ค.
์ Stack class๋ ์๊ธฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ง์ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ๋์์ ์ทจ์ฝํ๋ค. ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๋ด๋ elements ๋ฐฐ์ด๋ก ์ ์ฅ์ ํ์ ๋ง๋ค์ด ์์๋ฅผ ๊ด๋ฆฌํ๋๋ฐ, GC๋ ํด๋น ๋ฐฐ์ด์์ ๋นํ์ฑํ ์์ญ์ ์ฐธ์กฐํ๋ ๊ฐ์ฒด๋ฅผ ๋๊ฐ์ด ์ ์ํ ๊ฐ์ฒด๋ก ๋ณด๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๋นํ์ฑ ์์ญ์ด ๋๋ ์๊ฐ null ์ฒ๋ฆฌ๋ฅผ ํตํด ํด๋น ๊ฐ์ฒด๋ฅผ ๋ ์ด์ ์ฌ์ฉํ์ง ์๋๊ฒ์ GC์ ์๋ ค์ผํ๋ค. ์ด๋ ๋จ์ํ ์ stack class๋ง์ ๋งํ๋ ๊ฒ์ด ์๋๋ฉฐ, Collection
ํด๋์ค๋ ๋ชจ๋ ์ฃผ์ํด์ผํ๋ค.
์บ์ ๋ํ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ์ผ์ผํค๋ ์์์ด๋ค. ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ์บ์์ ๋ฃ์ด๋๊ณ , ์ด ์ฌ์ค์ ์์ ์ฑ ๊ทธ ๊ฐ์ฒด๋ฅผ ๊ณ์ํด์ ๋๋๋ ๊ฒฝ์ฐ๋ฅผ ํํ ๋ณผ ์ ์๋ค. ์ด๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ง์ด๋ค.
WeakHashMap
: ์บ์ ์ธ๋ถ์์ ํค๋ฅผ ์ฐธ์กฐํ๋ ๋์๋ง ์ํธ๋ฆฌ๊ฐ ์ด์ ์๋ ์บ์๊ฐ ํ์ํ ์ํฉ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋(
ScheduledThreadPoolExecutor
)๋ฅผ ํ์ฉํ๊ฑฐ๋, ์บ์์ ์ ์ํธ๋ฆฌ ์ถ๊ฐ์ ๋ถ์ ์์ ์ผ๋ก ์ฐ์ง ์๋ ์ํธ๋ฆฌ๋ฅผ ์ฒญ์ํ๋ ๋ฐฉ๋ฒ// LinkedHashMap์ ๋ค์ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉํ์ง ์๋ ์ํธ๋ฆฌ๋ฅผ ์ฒ๋ฆฌ void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } }
๋ฆฌ์ค๋ ํน์ ์ฝ๋ฐฑ ๋ํ ๋ฉ๋ชจ๋ฆฌ ๋์์ ์์์ด๋ค. ํด๋ผ์ด์ธํธ๊ฐ ์ฝ๋ฐฑ๋ง ๋ฑ๋กํ๊ณ ๋ช
ํํ ํด์งํ์ง ์๋ ๊ฒฝ์ฐ ์ฝ๋ฐฑ์ ๊ณ์ ์์ฌ๋ง ๊ฐ ๊ฒ์ด๋ค. ์ด๋ด ๋ ์ฝ๋ฐฑ์ ์ฝํ ์ฐธ์กฐ๋ก ์ ์ฅํ๋ฉด, GC๊ฐ ์ฆ์ ์๊ฑฐํด๊ฐ๋ค.(ex) WeakHashMap
์ ํค๋ก ์ ์ฅํ๋ ๋ฐฉ๋ฒ
WeakHashMap
์๋ฐ ์ฐธ์กฐ ํ์ ์ ๋ํด ์ ํ์ด ํ์ํ๋ค.
List
, Map
, Set
๊ฐ์ ์๋ฐ Collection
ํด๋์ค๋ค์ ์ฌ์ฉํ ๋๋ ํญ์ ์ฃผ์๊ฐ ํ์ํ๋ค. Collection
ํด๋์ค ์์ ๋ด๊ฒจ์๋ ์ธ์คํด์ค๋ ํ๋ก๊ทธ๋จ์์ ์ฌ์ฉ์ฌ๋ถ์ ๊ด๊ณ ์์ด ๋ชจ๋ ์ฌ์ฉ๋๋ ๊ฒ์ผ๋ก ํ๋จ๋์ด GC์ ๋์์ด ๋์ง ์์ ๋ฉ๋ชจ๋ฆฌ ๋์์ ํํ ์์ธ์ด ๋๋ค.
์ผ๋ฐ์ ์ธ HashMap
์ ๊ฒฝ์ฐ Map
์์ Key/Value๊ฐ ๋ค์ด๊ฐ๊ฒ ๋๋ฉด ์ฌ์ฉ์ฌ๋ถ์ ๊ด๊ณ ์์ด ํด๋น ์ฐธ์กฐ๋ ์ง์์ง์ง ์๋๋ค. Key์ ํด๋นํ๋ ๊ฐ์ฒด๊ฐ ๋ ์ด์ ์กด์ฌํ์ง ์๊ฒ๋์ด null
์ด ๋์์ ๊ฒฝ์ฐ HashMap
์์๋ ๋ ์ด์ ๊บผ๋ผ ์ผ์ด ์๋ ๊ฒฝ์ฐ๋ฅผ ์๋ก ๋ค์ด๋ณด์. HashMap
์ ๊ฒฝ์ฐ ํด๋น ๊ฐ์ฒด๊ฐ ์ฌ๋ผ์ง๋๋ผ๋ GC๋์์ผ๋ก ์ก์ง ๋ชปํ์ฌ ์ปฌ๋ ์
์ ์์ฌ, ๋ฉ๋ชจ๋ฆฌ ๋์์ ์์ธ์ด ๋๋ค.
์ด๋ WeakHashMap
์WeakReference
๋ฅผ ์ด์ฉํ์ฌ HashMap
์ Key๋ฅผ ๊ตฌํํ ๊ฒ์ด๋ค.WeakHashMap
์ ์๋ Key๊ฐ์ด ๋์ด์ ์ฌ์ฉ๋์ง ์๋๋ค๊ณ ํ๋จ๋๋ฉด ๋ค์ GC๋ ํด๋น Key, Value ์์ ์ ๊ฑฐํ๋ค. ์์๋ก ์ ๊ฑฐ๋์ด๋ ์๊ด์๋ ๋ฐ์ดํฐ๋ค์ ์ํด ์ฃผ๋ก ์ฌ์ฉ๋๋ค.
/**
* The entries in this hash table extend WeakReference, using its main ref
* field as the key.
*/
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
@SuppressWarnings("unchecked")
public K getKey() {
return (K) WeakHashMap.unmaskNull(get());
}
public V getValue() {
return value;
}
public V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public class ReferenceTest {
public static void main(String[] args){
HashMap<Integer, String> hashMap = new HashMap<>();
Integer key1 = 1000;
Integer key2 = 2000;
Integer key3 = 3000;
hashMap.put(key3, "test c");
hashMap.put(key2, "test b");
key3 = null;
System.out.println("HashMap GC ์ํ ์ด์ ");
hashMap.entrySet().stream().forEach(el -> System.out.println(el));
System.gc();
System.out.println("HashMap GC ์ํ ์ดํ");
hashMap.entrySet().stream().forEach(el -> System.out.println(el));
WeakHashMap<Integer, String> map = new WeakHashMap<>();
map.put(key1, "test a");
map.put(key2, "test b");
key1 = null;
System.out.println("WeakHashMap GC ์ํ ์ด์ ");
map.entrySet().stream().forEach(el -> System.out.println(el));
System.gc();
System.out.println("WeakHashMap GC ์ํ ์ดํ");
map.entrySet().stream().forEach(el -> System.out.println(el));
}
}
HashMap GC ์ํ ์ด์
2000=test b
3000=test c
HashMap GC ์ํ ์ดํ
2000=test b
3000=test c
WeakHashMap GC ์ํ ์ด์
1000=test a
2000=test b
WeakHashMap GC ์ํ ์ดํ
2000=test b
WeakHashMap
์ Value๋ ๊ฐํ ์ฐธ์กฐ์ ์ํด ๋ณด๊ด ์ ์ง๋๋ค. Value ๊ฐ์ฒด๊ฐ ์ง๊ฐ์ ์ ์ผ๋ก ์์ ์ Key๋ฅผ ๊ฐํ์ฐธ์กฐํ์ง ์๋๋ก ์ฃผ์ํด์ผํ๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ์๋ Key๊ฐ ์ญ์ ๋์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ง์ฝ Key๋ฅผ ์ฐธ์กฐํ๋ Value๋ฅผ ์ฌ์ฉํด WeakHashMap
๋ํ ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ๊ธธ ๋ฐ๋๋ค๋ฉด WeakReference
๋ก ๋ํํด์ฃผ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด ๋๋ค.
weakHashMap.put(key, new WeakReference(value));
GC ํ์ธํ๊ธฐ
๋ง์ฝ GC๊ฐ ์ํ๋๊ณ ์๋ ๊ฒ์ ํ์ธํ๊ณ ์ถ๋ค๋ฉด ์๋ ์ต์ ์ ์ถ๊ฐํด์ฃผ๋ฉด๋๋ค.
Intellj์ Edit Configurations -> VM options
์ -verbose:gc -XX:+PrintCommandLineFlags
๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ํ์ฌ GC๊ฐ ์ํ์ค์ธ์ง ํ์ธํ ์ ์๋ค.
public static void main(String[] args) throws InterruptedException {
List<Integer> li = IntStream.range(1, 100).boxed().collect(Collectors.toList());
for (int i=1; true; i++) {
if (i % 100 == 0) {
li = new ArrayList<>();
Thread.sleep(100);
}
IntStream.range(0, 100).forEach(li::add);
}
}
[GC (Allocation Failure) 33246K->1292K(125952K), 0.0035248 secs]
[GC (Allocation Failure) 34572K->1284K(125952K), 0.0028950 secs]
[GC (Allocation Failure) 34564K->1200K(125952K), 0.0033107 secs]
[GC (Allocation Failure) 34480K->1268K(125952K), 0.0045468 secs]
...
์ฐธ๊ณ
Last updated
Was this helpful?