Java/개념

Heap Pollution(힙 오염)

겔로거 2023. 4. 8. 18:27

Heap Pollution이란?

Heap Pollution이란,  JVM의 힙 영역(heap area)이 오염된 상태를 의미합니다. 위키피디아에서는 Heap Pollution에 대해 다음과 같이 정의하고 있습니다. 

In the Java programming language, heap pollution is a situation that arises when a variable of a parameterized type refers to an object that is not of that parameterized type. This situation is normally detected during compilation and indicated with an unchecked warning. Later, during runtime heap pollution will often cause a ClassCastException.

A source of heap pollution in Java arises from the fact that type arguments and variables are not reified at run-time. As a result, different parameterized types are implemented by the same class or interface at run time. All invocations of a given generic type declaration share a single run-time implementation. This results in the possibility of heap pollution.

여기서  when a variable of a parameterized type refers to an object that is not of that parameterized type. 문구에 집중할 필요가 있습니다.

 

파라미터화된 타입이 파라미터화되지 않은 타입에 추론될 경우 Heap Pollution이 발생하며, 컴파일간 unchecked warning으로 인식되기 때문에 런타임간 ClassCastException을 야기할 수 있습니다. 그렇다면 어떤 상황에서 Heap Pollution이 발생할까요?

 

Heap Pullution이 발생할 수 있는 경우

- Generic - Java 5

- varargs parameter - Java 5

 

Generic은 하나 이상의 타입을 받을 수 있도록 만들어진 개념이며, varargs는 인수의 갯수를 개발자가 조절할 수 있게 도와주는 개념입니다. 

varargs 오류 예시

public class Example {
  public static void main(String[] args) {
    List<String> stringList = new ArrayList<>();
    stringList.add("hello");
    stringList.add("world");

    addToList(stringList, 1, 2, 3);
    
    System.out.println(stringList);
  }
  
  public static <T> void addToList(List<T> list, T... elements) {
    for (T element : elements) {
      list.add(element); //String과 Integer를 하나의 리스트에 add하여 오류 발생
    }
  }
}

Generic 오류 예시

import java.util.List;
import java.util.ArrayList;

public class Example {
  public static void main(String[] args) {
    List<String> stringList = new ArrayList<>();
    stringList.add("hello");
    stringList.add("world");
    
    List<Integer> integerList = new ArrayList<>();
    integerList.add(1);
    integerList.add(2);
    
    List<Object> objectList = new ArrayList<>();
    addToList(objectList, stringList);
    addToList(objectList, integerList);
    
    System.out.println(objectList);
  }
  
  public static <T> void addToList(List<T> list, List<? extends T> elements) {
    list.addAll(elements); //컴파일간 이상 없으나 String과 Integer라는 다른 형을 add하는 과정에서 런타임 오류 발생
  }
}

 

그렇다면 Heap Pollution을 방지하는 방법에는 무엇이 있을까요?

Heap Pollution 해결 방법

  • 가능한 타입에 저장, 수정 처리를 하지 않는다. (해야 될 경우, 신중하게 사용할 것을 권장)
  • 와일드 카드를 사용해 동일한 유형인지 검증하도록 구현한다.

varargs 오류 해결

//변경 전
public static <T> void addToList(List<T> list, T... elements) {
  for (T element : elements) {
    list.add(element);
  }
}

//변경 후
public static <T> void addToList(List<T> list, Class<T> type, T... elements) {
  for (T element : elements) {
    if (type.isInstance(element)) {
      list.add(element);
    }
  }
}


addToList(stringList, String.class, "hello", "world");
addToList(stringList, String.class, "foo", "bar");

Generic 오류 해결 

import java.util.List;
import java.util.ArrayList;

public class Example {
  public static void main(String[] args) {
    List<String> stringList = new ArrayList<>();
    stringList.add("hello");
    stringList.add("world");
    
    List<Integer> integerList = new ArrayList<>();
    integerList.add(1);
    integerList.add(2);
    
    List<Object> objectList = new ArrayList<>();
    addToList(objectList, stringList);
    addToList(objectList, integerList);
    
    System.out.println(objectList);
  }
  
  public static <T> void addToList(List<T> list, List<? extends T> elements) {
    list.addAll(elements);
  }
}