힙 오염
•
힙 오염(Heap pollution)은 JVM의 힙 메모리 영역에 저장되어 있는 특정 변수나 객체에 맞지 않는 데이터를 참조하여, 해당 데이터를 꺼내려할 때 런타임 에러가 발생할 수 있는 오염된 상태를 말한다.
•
주로 제네릭을 사용할 때 힙 오염이 발생하는데, 제네릭의 타입 소거 특성과 컴파일러 특성이 합쳐져 힙 오염이 발생한다.
제네릭 힙 오염 과정
•
힙 오염이 발생하는 상황은 두 가지가 있다.
◦
원시 타입과 매개변수 타입을 동시에 사용하는 경우
◦
확인되지 않은 형변환을 수행하는 경우
ArrayList<String> list1 = new ArrayList<>();
list1.add("홍길동");
list1.add("임꺽정");
Object obj = list1;
ArrayList<Double> list2 = (ArrayList<Double>) obj; // 힙 오염 발생!!
list2.add(1.0);
list2.add(2.0);
System.out.println(list2); // [홍길동, 임꺽정, 1.0, 2.0]
for(double n : list2) {
System.out.println(n);
}
Java
복사
•
위 코드에서 ArrayList<String> 타입을 Object로 업캐스팅 후 ArrayList<Double>로 다운캐스팅하여 힙 오염이 발생하고, for문에서 ClassCastException이 발생하게 된다. 이런 현상은 제네릭에서 타입 소거 때문에 발생한다.
•
컴파일러는 타입 캐스팅에 대해서, 형변환 대상 객체가 형변환 되었을 때 대입되는 변수에 저장 할 수 있는가만 확인할 뿐 꼼꼼하게 검사하지 않는다.
•
제네릭 타입 파라미터들은 타입 소거 후 Object로 변환되거나 제거되는데, 위 코드에서는 로 타입이 되어 어떤 타입이든 저장할 수 있기 때문에 컴파일러에서 확인하지 못하는 것이다.
힙 오염 방지책
•
이러한 힙 오염을 방지하기 위해서 자바에서는 Collections 클래스에 checkList()라는 메서드를 지원한다. checkList() 메서드는 해당 객체에 의도하지 않은 타입의 데이터가 들어온다면, 이를 감지하여 예외를 발생시킨다.
List<String> list1 = Collections.checkedList(new ArrayList<>(), String.class);
list1.add("홍길동");
list1.add("임꺽정");
Object obj = list1;
List<Double> list2 = (List<Double>) obj;
list2.add(1.0); // 에러 발생!
list2.add(2.0);
System.out.println(list2);
for(double n : list2) {
System.out.println(n);
}
Java
복사
•
이와 같이 checkList()를 적용하면 실질적으로 다른 타입의 값을 넣는 add(1.0)에서 힙 오염을 감지하고 에러가 발생시키게 된다.