인프런 커뮤니티 질문&답변

민타님의 프로필 이미지
민타

작성한 질문수

코딩테스트 [ ALL IN ONE ]

DLinked List를 활용한 insert에서 메모리 할당해제관련해서 질문이 있습니다. (BrowserHistory)

해결된 질문

작성

·

240

1

class Node {
    let value: String 
    var prev: Node?
    var next: Node?

    init(value: String, prev: Node? = nil, next: Node? = nil) {
        self.value = value
        self.prev = prev
        self.next = next
    }
}

class BrowserHistory {

    var head: Node?
    var current: Node?

    init(_ homepage: String) {
        let newNode = Node(value: homepage)
        self.head = newNode
        self.current = newNode
    }
    
    func visit(_ url: String) {
        self.current?.next = Node(value: url, prev: self.current)
        self.current = self.current?.next
    }
    //..생략
}

강의를 듣던중 의문이 생겨서 질문남깁니다. visit시에 참조가 새로운 노드로 변경되기 때문에 그 이전에 current뒤에 존재하는 Node들은 뒤에 몇만개가 있더라도 맨앞에 있던 노드의 참조를 가르키는 곳이 없기 때문에 모두 메모리에서 가비지 컬렉터에 의해 메모리에서 지워진다라고 말씀하셨는데, 뒤에 1개의 Node가 있는 경우는 그렇지만 Doubly Linked List의 경우 그 뒤에 Node들의 prev로 그 이전 Node들을 가르키고 있어 참조하는 곳이 최소 1개 이상은 존재하게 되어 메모리에서 할당해제가 되지 않을 것이라고 생각했습니다.

 

따라서 visit당시에 뒤에 있는 Node들도 메모리에서 할당해제가 되게끔 리스트의 끝까지 돌면서 Node의 next만 지워주고 테스트해보자고 생각해서 아래와 같이 코드를 수정하였습니다.

 

func visit(_ url: String) {
        var tmp = self.current
        while tmp?.next != nil {
            var before = tmp
            tmp = tmp?.next
            before?.next = nil
        }

        self.current?.next = Node(value: url, prev: self.current)
        self.current = self.current?.next
    }

이를 LeetCode의 결과로 확인해보니 물론 속도는 5ms 줄었지만, 메모리 4MB정도 줄일 수 있었습니다.

Swift의 경우 가비지 컬렉터가 아닌 ARC가 컴파일 타임에 Reference Count를 확인하기 때문에 그 동작 방식에서의 차이에서 비롯된 차이인지 궁금합니다.

가비지 컬렉터에서는 실제로 위의 같은 경우에도 메모리 할당 해제가 되는건가요??

 

답변 1

0

개발남노씨님의 프로필 이미지
개발남노씨
지식공유자

안녕하세요 민구몽님.

엄청 좋은 질문해주셔서 감사합니다.

일단 제가 답을 드릴건데, 이 답변에 대하여 다른 의견을 내주셔도 좋을 것 같습니다.

 

일단. ARC도 가비지컬렉터와 마찬가지로 런타임에 동작한다고 검색이 됐는데, 맞을까요?

 

그리고 본 질문에 대한 답을드리면,

민구몽님이 말씀하신대로 제가 작성한 코드에서는 prev 참조로 인해 가비지 컬렉터를 통한 메모리 해제가 안되네요. doubly linked list의 특성을 고려하지 못했네요.

다만, 모든 next를 nil로 설정하지 않더라도, 즉 current의 바로 다음 노드의 prev만 제거해도 우리가 원하는 방식으로 동작할 것 같습니다.

A = B = C = D 이렇게 연결되어 있던걸

A - B = C = D 이렇게 current.next만 nil로 변경해서 발생했던 문제인데

A B = C = D 이런식으로 prev도 하나 제거해주면 순환참조가 발생해서 추후에 가비지컬렉터가 메모리를 해제할 수 있습니다.

다만 시간이 좀 걸릴 수 있다고 하네요.

 

혹시 다른 의견 있으시면 활발히 남겨주세요 :)

다시 한 번 좋은 질문 감사합니다.

민타님의 프로필 이미지
민타
질문자

우선 Swift의 ARC는 컴파일 타임에서 동작합니다! 그리고 순환참조의 경우도 자동으로 메모리 할당해제가 되지 않아서 개발자가 순환참조가 생기지 않게끔 주의해서 개발해야하는 부분인데, 가비지컬렉터는 이를 알아서 해제시켜주나보네요..!! 신기하네요

Swift ARC기준으로 순환참조의 경우 메모리 할당해제가 자동으로 되지 않아 next를 전부 초기화 시켜주는 과정을 거쳤었습니다!

개발남노씨님의 프로필 이미지
개발남노씨
지식공유자

그러네요 컴파일 타임에서 동작하는군요. 가비지 컬렉터도 순환참조를 찾기 위해서 시간이 좀 걸린다고 합니다.

ARC에 맞게 코드를 잘 작성하셨네요!!

혹시 더 논의할 사항 있으면 언제든 질문 남겨주세요 :)

민타님의 프로필 이미지
민타
질문자

넵 감사합니다!

민타님의 프로필 이미지
민타

작성한 질문수

질문하기