glenn-syj / more-effective-java

이펙티브 자바를 읽으며 자바를 더 효율적으로 공부합니다
4 stars 5 forks source link

[MEJ-006] 제네릭 소거와 힙 오염에 대해 #137

Closed undeadtimo closed 5 months ago

undeadtimo commented 6 months ago

Based on: @ yngbao97 by #126

yngbao97님의 글 잘 읽었습니다. 하나 하나의 단어에 대해 차근차근 설명하는 과정이 들어있어서 더욱 글을 재밌게 읽으며 이해를 잘 할 수 있었습니다.

yngbao97님께서 글의 마지막에 제네릭 사용 설계에 오류가 있는 경우 문제가 생길 수 있지 않을까 싶다.라 하셨는데,

이와 관련하여 조사해보니 제네릭 타입을 사용함으로써 발생할 수 있는 '힙 오염'이라는 것이 존재하였습니다.

다른 말로 힙 오염을 설명하자면, 특정 변수 또는 객체가 타입과 일치하지 않는 데이터를 참조하고 있는 것을 말합니다.

즉, 특정한 '타입' 데이터를 저장하도록 할당된 '힙' 메모리 공간에 일치하지 않는 '타입' 데이터가 저장되는 것을 힙 오염이라 하는 것입니다.

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);
}

// 이 코드에서는 
ArrayList<Double> list2 = (ArrayList<Double>) obj;
list2.add(1.0);
list2.add(2.0);
에서 잘못된 데이터를 참조하게 되면서 힙 오염이 일어납니다.

--아직 힙 오염에 대해 깊은 탐색을 이루지 못하여 지식은 부족하지만; yngbao님께서 마지막에 제시한 의문점과 연관이 있는 것 같아 공유드립니다.--

직관적으로 보았을 때도 String 타입의 List에서 이미 String 타입의 변수를 넣고, Object 상위 클래스로 List의 속성에 대한 형변환을 해준 이후, ArrayList로 다시 형변환 하여 double 타입의 변수를 넣는 것은 잘못되어 보이지만,

System.out.println(list2)를 실행할 경우, [홍길동, 임꺾정, 1.0, 2.0] 데이터가 정상적으로 출력됩니다.

이는 yngbao97 님께서 설명해주신 제네릭 타입 소거 과정으로 인한 결과입니다.

Reference:

https://www.geeksforgeeks.org/what-is-heap-pollution-in-java-and-how-to-resolve-it/

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%9E%99-%EC%98%A4%EC%97%BC-Heap-Pollution-%EC%9D%B4%EB%9E%80

yngbao97 commented 6 months ago

글을 작성할 당시에는 깊게 고민하지 않고 막연히 뭔가 예외가 있을 것만 같은 생각에 남긴 내용이었는데, 이렇게 직접 찾아보고 부가설명 추가해주셔서 감사합니다!

확실한 설계나 주석없이 상위 객체와의 형변환을 난무하면 실무에서 이런 문제가 생길 가능성이 있겠네요.. 덕분에 힙오염이라는 개념에 대해 알게 되었고, 한번 더 찾아보고 고민해볼 수 있었습니다. 감사합니다◡̈