본문 바로가기
Perpomence

Memory leak으로 인한 Full GC Issue

by 루에 2020. 4. 9.
반응형

 문제 해결 과정에 대한 리포트 파일

DRSC-52859879-131020-0811-130.pdf
3.91MB

결과 요약

원인

  1. 비전 property 옵션에 대한 컴포넌트를 그리는 과정에서 메모리 누수 발생

  2. 비전에 관련된 두 개의 클래스(A, B)가 있을 때, B에서 화면 및 옵션을 설정하고 B에서 만든 컴포넌트를 A에 add하여 최종적으로는 A를 화면에 그리는 복잡한 구조

    1. A에서 다른 값을 bind한 몇 개의 변수를 포함한 모델정보에 관련된 객체 선언

    2. 위 변수를 B의 파라미터로 선언하여 B 클래스 호출

    3. B의 컴포넌트(eg : check box)에 A에서 넘어온 파라미터를 bind

    4. A에서 넘어온 파라미터에 대해 B클래스 안에서 Change listener 선언

 

분석

  • 이중 삼중으로 bind 하고 객체가 선언된 곳이 아닌 다른 곳에서 리스너를 선언하며 JavaFx의 객체 해제 과정에서 참조가 풀리지 않는 현상으로 추정(heap분석을 통해 weakHashMap 상태임에도 holding 상태)

  • B의 컴포넌트에 bind된 부분에 대해 해제 혹은 리스너 제거로 이슈 개선에 대한 효과 확인

 

조치

B클래스가 다른 여러 스킬에서도 활용되는 클래스이기 때문에 기존 구조를 건드리지 않으면서 효과적으로 해결하기 위해 리스너의 역할에 대해 분석하였으며, 그 결과 리스너의 역할이 단순히 비전 아이템에 대한 콤보박스 값을 초기화 하는 것으로 확인 되어 그 부분에 대해 개선 조치함.

 

조치 결과

개선 전

개선 후

 

 

활용툴

IBM Heap analyzer - Object에 대한 자세한 정보

Eclipse Memory analyzer - 누수를 발생하는 요소에 대한 용의자 추론 등

VisualVM - 기본적인 메모리 및 쓰레드 정보

 

 

해결 과정

  1. 누수에 대한 상황 인지 및 재현 테스트

  2. 누수를 발생하는 Action에 대한 코드 분석 및 VisualVM을 이용하여 메모리 추적

  3. 누수를 발생하는 코드 추적

  4. 발견된 10개의 오브젝트에 대해 스트레스 테스트를 통해 메모리 변화 모니터링

  5. 특정 용의 오브젝트 선별

  6. 힙덤프를 통하여 어떠한 오브젝트가 문제되는지 파악(본 이슈의 경우 viewModel 및 weakHashMap 임에도 holding되는 경우)

  7. 힘덤프에서 홀딩되는 목록에 근거하여 해당 오브젝트 찾기

  8. 문제를 야기하는 코드 특정함

  9. 사이드이펙트가 나지 않도록 하면서 누수를 막을 수 있도록 코드 개선

 

느낀점

  • Heap 분석툴의 결과는 매우 신뢰할만하다. 하지만 특정 수준까지 분석되지 않으면 너무 방대한 정보가 되어 정보의 바다에서 길을 잃을 가능성이 있다.

  • VisualVM에는 여러가지 유용한 플러그인들이 있다. 해당 플러그인들을 활용하면 메모리 분석에 좀 더 유용하다.

  • JavaFx의 라이프 사이클 관리는 매우 거지같다. 안드로이드 2.x를 보는 것 같았다.

  • tornadoFx에는 이해 못할 구성을 가진 기능이 있었다. bind() 인데 양방향으로 binding되길래 내부 코드를 보니까 특정 조건에 따라 양방향이 걸리기도 하더라. 아래 코드다. 사용자의 함수콜은 internalBind()도 아니고 그냥 bind()로 사용한다.

  • 메모리 이슈를 해결해본건 처음이라 불필요한 과정도 많았고 사소한 실수로 범인을 범인이 아니라고 판단하는 우를 범하기도 했다. 다음에는 좀 더 체계적으로 할 수 있을 것 같다.

private fun <T> Property<T>.internalBind(property: ObservableValue<T>, readonly: Boolean) {
    ViewModel.register(this, property)
    if (readonly || (property !is Property<*>)) bind(property) else bindBidirectional(property as Property<T>)
}

 

반응형

댓글