LinkedList에서 삭제된 노드의 next의 null 처리 할까 말까?
학습을 위해 작성된 포스팅 입니다.
잘못된 내용이 있으면, 지적해주세요!
Linked List 미니미션
우테코 레벨1 사다리 미션 진행 중, LinkedList
를 직접 구현해보는 미니미션이 하나 추가되었다...ㅎ
(와.. 신난다...)
자료구조에 대한 선수지식이 없어서 제법 애를 먹었는데,
아주 멋진 크루 마코 에게 이런저런 질문을 하며, 얼레벌레 구현을 하는데는 성공하였다.
그런데 구현을 하던도중 한가지 의문점이 떠올랐다.
null 처리 해줘야 하나..?
의문이 든 시점은 remove
기능을 구현하는 도중이었다.
LinkedList 의 remove 는 위의 그림과 같은 방식으로 구현되는데,
삭제된 노드(C)의 next는 여전히 D를 가리키고 있다.
C의 next를 null로 처리를 해줘야 할까..?
참조되지 않는 값은 GC가 어차피 처리할거니까 신경안써도 되는건가..?
이부분에 대한 의문을 해소하기 위해,실제로 우리가 사용하는 LinkedList
에서는 어떻게 처리하고 있는지 확인해보았다.
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
연결을 해제하는 unlink
메소드를 통째로 긁어왔다.
(간만에 else
를 만나서 좀 낯설긴한데..)
대충 보면 연결을 해제할 Node의 prev 와 next를 모두 null로 처리해주는 과정을 거치고 있다.
GC 일 안하나..?
null 처리에 대한 의문을 가지고 있긴 했지만,
'연결이 해제되면, 더이상 참조가 되지 않고, 그렇다면 GC가 콜렉팅을 할거기 때문에 굳이 null 처리를 해주지 않아도 되지않을까..?' 라는 생각이 지배적이었다.
근데 실제 사용하는 LinkedList에서 null처리를 해주는 것을 보고,
GC가 일을 안하나..? 라는 생각을 하게 되었다.
다른 값에서 참조가 되고있는 값을 참조하는 값은 참조가되고있지 않더라도 GC가 처리를 안하는 건가..?
(간장공장 공장장..)
라는 생각도 해봤다.
그니까.. C가 참조가 되고 있지는 않지만, D는 B에 의해 참조 되고 있고, C는 D를 참조하고 있으니까
이런 경우는 GC가 처리를 안한다는 진짜 말도 안되는 생각이 들어서 바로 구글링을 해봤다.
GC 일 하는데?!
GC에 대한걸 통째로 공부하면 좋겠지만 물리적으로 불가능하다고 판단해서,
항상 정답은 아니지만, 집단지성의 힘을 보여주는 stackOverFlow를 뒤져봤다.
어렵지 않게, stackOverFlow에 나와 같은 고민을 하고 있는 질문을 발견했다!
하나같이 입을 모아서 null 처리를 해주지 않아도, GC에 의해 자동적으로 수집될거라고 말을 해주고 있다.
흠... 근데 왜 LinkedList는 null 처리를 굳이굳이 하고있는 걸까?
The garbage collector will see when there's no references left **to** `temp`.
Therefore you don't have to care about nulling outgoing references
- if you can't reach temp anymore it will be garbage collected (eventually).
위에 대한 의문의 실마리가 된 답변이다.
다른 답변들과 내용이 비슷하지만, 마지막 괄호쳐진 eventually에 자꾸 눈이간다.
결국에는 수집된다라.. 결국에는..
이걸 조금 해석해보면, "되긴 되는데 좀 늦게 될수도 있음ㅎ" 과 동일하게 해석된다.
혹시, 삭제된 노드가 next에 대한 참조를 유지하고 있으면, 콜렉팅이 늦게 되는걸까?
정답은 YES
킹리적갓심이 들어맞았다.
연결된 목록에서 노드를 제거하면 GC는 next가 null로 설정되었는지 여부에 관계없이
제거된 노드를 가비지로 수집한다.
그러나 제거된 Node의 next를 null로 설정하면,
GC가 노드에서 사용하는 메모리를 더 빨리 수집할 수 있다.
이는 GC가 동작하는 방식을 알아야 하는데, 내가 알고있던 내용과 이해한 내용을 정리하자면 다음과 같다.
GC는 루트 집합에서 아직 reachable한 개체(이하 도달가능한 개체)와 가비지로 안전하게 수집할 수 있는 개체를 결정하기 위해 개체 간의 참조를 추적한다.
위의 그림을 다시 보면, B의 next가 D를 참조하게끔 변경을 하였기에, 리스트에서 제거된 C의 참조경로가 끊겼다고 생각할 수 있지만,
여전히 C의 next가 D를 가리키고 있기에 GC는 C를 도달가능한 개체로 판단하고 콜렉팅 우선순위에서 제외한다.
하지만, 리스트에서 제거된 C를 가리키는 참조가 없으므로, 결국에는 콜렉팅의 대상으로 등록된다.
반면, 제거된 node의 next를 null로 변경해준다면,
C의 참조경로는 완전하게 끊겼기 때문에 GC가 좀더 빠르게 콜렉팅 대상으로 C를 등록한다.
정리를 하자면, 제거된 노드의 next가 null로 설정되지 않은 경우, 제거된 노드의 수명을 연장시킬 수 있다.
그럼 꼭 null 처리 꼭 해야겠네!
사실, next를 null 처리를 해주는게 필수는 아니다.
위에서 말로한걸 보면, 오오.. 해야겠다 싶지만, 현대 컴퓨터 입장에선 별일 아닌 일로 치부되는 듯하다.
정말 극단적으로 가면 null처리를 안해줘서 OutOfMemoryError
가 발생할 수도 있다곤 하는데..
흠.. 글쎄 그닥...
java는 극단적인 경우까지 대비하여 null처리를 해줬나 보다.
덕분에 공부하고 갑니다... 하핳...
작은 의문점에서 시작해서 너무 깊게 온건가 싶긴했는데, 멈출수가 없었다.
조금만 더 알아보면 될 것 같은데, 방치해두기 싫은 느낌이라 여기까지 와버렸다.
뭐 알아둬서 나쁜건 아니지 않을까 싶다.