JAVA

[JAVA] 다 쓴 객체 참조를 해제하라

코카멍멍 2023. 12. 20. 21:48
반응형

아이템 7. 다 쓴 객체 참조를 해제해라

다 쓴 객체 참조를 해제하라

JDK 8버전 코드

public class Stack{
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;

        public Stack(){
            elements = new Object [DEFAULT_INITIAL_CAPACITY];
        }

        public void push(Object e){
            ensureCapacity();
            elements[size++] = e;
        }

        public Object pop(){
            if (size == 0)
                throw new EmptyStackException();
            return elements[--size];
        }

        private void ensureCapacity(){
            if(elements.length == size)
                elements = Array.copyOf(elements, 2 * size + 1);
        }
    }

JDK 8버전에서는 현재 index를 변경해서 stack의 top을 관리하지만 pop()을 할때 TOP의 값을 줄이기만 합니다. 이 부분에서 문제가 발생합니다.

참조 무효화

public pop(){
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null; // Eliminate obsolete references.
        return result;
    }

기존 stack 방식

객체를 스택에 추가

객체를 스택에 추가

기존 stack 방식

스택은 객체를 삭제하는게 아닌 참조는 계속한 상태로 top idx만 변경
이렇게 사용하지 않는 객체를 계속해서 보관하고 있다면 메모리 누수 가 발생합니다.

null 처리

null처리를 해주게 되면 GC가 해당 객체를 정리함

2023.11.09 - [JAVA] - [JAVA] GC란?

 

[JAVA] GC란?

GC란? 프로그램이 동적으로 할당했던(heap 영역) 메모리 영역 중 필요 없게 된 영역을 여러 알고리즘을 통해 해제하는 것입니다. 장점 - 메모리 누수 방지 - 해제된 메모리에 접근 방지 - 해제한 메

zmfpdl64.tistory.com

메모리 낭비를 줄이는 방법

스코프 밖으로 밀어내라

public void test() {
    if(true){
        Integer a = 10; // if문의 종료되면 변수가 해제
    }
}

JDK 11 실제 Stack구현부

public synchronized void removeElementAt(int index) {
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        modCount++;
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }

실제 Stack의 구현부 입니다. JDK 11버전 에서는 null 처리를 통해 객체 참조 해제를 해줘서 Stack을 사용해도 메모리 누수의 문제로부터 안전합니다.

캐시 메모리 누수

  • 객체 참조를 캐시에 넣고 제거 안하면 지속적인 참조 때문에 메모리 누수가 발생
  • Map<Object, String> map = new HashMap<>();

HashMap은 강한결합 객체의 한종류로 직접적으로 key를 해제하지 않으면 메모리 누수가 발생합니다.

해결 방안:

  • WeakHashMap을 사용하여 외부에서 해당 key 객체가 살아있는 동안은 key: value가 살아있다.(약한 참조) key가 상수 풀에 저장돼있으면 적용 안됩니다.(Ex: primitive type, int, char...)
Map<Object, String> map = new WeakHashMap<>();

Java – Collection – Map – WeakHashMap (약한 참조 해시맵)

예시코드

HashMap

public static void hashMap() {
        HashMap<Integer, String> map = new HashMap<>();
        Integer num1 = new Integer(10);
        String str1 = new String("str1");
        Integer num2 = new Integer(20);
        String str2 = new String("str2");
        map.put(num1, str1);
        map.put(num2, str2);

        System.out.println(map.toString());
        num1 = null;
        System.gc();
        System.out.println(map.toString());
    }

WeakHashMap

public static void weakHashMap(){
        WeakHashMap<Integer, String> map = new WeakHashMap<>();
        Integer num1 = new Integer(10);
        String str1 = new String("str1");
        Integer num2 = new Integer(20);
        String str2 = new String("str2");
        map.put(num1, str1);
        map.put(num2, str2);

        System.out.println(map.toString());
        num1 = null;
        System.gc();
        System.out.println(map.toString());
    }

약한 참조를 하는 WeakHashMap에서는 key를 담당하는 객체가 null로 참조해제를 하면 WeakHashMap은 해당 key를 제거하게 됩니다.

결과

WeakHashMap은 key를 참조해제 했을 때 key가 사라진것을 볼 수 있으며 HashMap은 강한결합 때문에 key로 가지고 있어 메모리 누수가 발생할 수 있습니다

참조

이펙티브 자바

 
 

 

 

반응형

'JAVA' 카테고리의 다른 글

[Java] Relection 메소드 필드명 가져오기 arg0, arg1  (0) 2024.04.28
[Java] TreeSet의 구조와 주의사항  (0) 2024.02.29
[Java] 동적 프록시에 대해  (0) 2023.11.28
[JAVA] GC란?  (0) 2023.11.09
정적 팩토리 메소드의 장단점  (0) 2023.07.07