blazor 메모리 관련 문의

안녕하세요.
항상 winform프로젝트만 하다가 최근에 blazor 강의를 보고 있긴했는데
처음으로 blazor 프로젝트를 진행하게 되었습니다.
웹쪽 경험이나 지식이 거의 없는 상태로 진행하게되었어요…

근데 실제 현장에서 하루 정도 구동 시켜놓으면 브라우저가 메모리 오류로 뻗는 현상이 나와서 보니까
개발자 모드로 메모리 스냅샷을 찍어서 보니까 페이지 이동시마다 메모리가 계속 오르는것을 확인했습니다.

페이지는 총 2개인데 한개는 그래프(echart)가 있는페이지고 한개는 데이터를 테이블로 보여주고 이미지를 보여주는 페이지입니다.
echart는 div테그 id를 js로 넘겨서 js에서 해당 테그에 랜더링해 주는 방식으로 만들었습니다.

처음에는 echart때문에 증가 하는것으로 보고 echart를 걷어내고 페이지 이동을 계속 해 봤는데 그래도 메모리가 0.1[mb]정도씩 오르는게 보였습니다.
그래서 검색을 하다보니까 js에서는 변수의 참조를 해제 하지 않으면 가비지가 해당 메모리를 해제하지 않는다는것을 보게 되었습니다.

그래서 혹시나 해서 원래 static으로 razor페이지에 bind해둔 변수를 내부 변수로 모두 바꿔보니
메모리 증가가 완하된것으로 보였습니다.

기존

static A abc ;
예로

@abc.abc

이런식으로 사용하고 있었습니다.

근데 변경해본 방식은

@code{
private A _abc = abc.DeepCopy(); // 깊은복사
}
<p> @_abc .abc </p>

여기서 질문은 전역으로 선언된 변수를 바인딩 하는 경우 페이지가 이동해서 해당 테그가 사용 하지 않게 되어도 참조가 남아있게 되어 가비지가 메모리를 회수 하지 않다는 결론을 내리게 됬는데 이게 맞는걸까요?
그렇다면 페이지에 데이터를 바인딩시에는 무조건 내부 변수에 깊은복사를 한후 그 변수를 사용해야 하는걸까요?
얇은 복사로 값 참조를 하면 안되는걸까요?.. 웹쪽 경험도 없어서 확신이 서지 않네요 …

확인해보니 echart도 메모리증가 원인이 맞았습니다.

window.StackedBar = function (id, data, xAxisData, datazoom = null) {
    var dom = document.getElementById(id);
    var myChart = echarts.getInstanceByDom(dom);
myChart = echarts.init(dom, null, {
    renderer: "canvas",
    useDirtyRect: false
})
}

처음에는 이런식으로 데이터가 갱신될때도 계속 echarts.init을 했는데 찾아보니 dispose를 해 주지 않으면 기존 인스턴스한 객체가 남아있고 가비지에서 메모리를 해제 하지 않는다고 하여 현재는 이미 인스턴스된 객체가 있을경우 재 사용하고 페이지 이동시에는 dispose를 해주니 메모리증가가 아주 없는것은 아니지만 완화되고 어느정도 이상안올라가는것 같습니다.

아래가 변경 코드입니다.

window.StackedBar = function (id, data, xAxisData, datazoom = null) {
    var dom = document.getElementById(id);
    //기존에 연결된 echarts 인스턴스가 있는지 확인하고 있으면 해당 인스턴스를 재사용, 없으면 새로 생성.

    var myChart = echarts.getInstanceByDom(dom);

    if (myChart) {
        window.UpdateChartData(id, data);
    }
    else {
        myChart = echarts.init(dom, null, {
            renderer: "canvas",
            useDirtyRect: false
        })
        var seriesData = JSON.parse(data); // JSON 문자열을 JavaScript 객체로 파싱
        var xAis = JSON.parse(xAxisData); // JSON 문자열을 JavaScript 객체로 파싱
        var DataZoom = JSON.parse(datazoom);

        //console.log(data);

        var option = {
            legend: {
                //범례설정.
                top: '5%',
                textStyle: {
                    color: "white",// 글씨 색상을 빨간색으로 설정,
                    fontSize: 17 // 폰트사이즈
                    // 여기에 추가적인 글꼴 스타일 속성을 추가할 수 있습니다.
                },
                icon: "rect" //심볼 모양 변경.
                // 가능한 값: 'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'
            },
            tooltip: {},
            xAxis: {
                data: xAis,
            },
            dataZoom: DataZoom,
            yAxis: {
                type: "value"
            },
            series: seriesData
        };


        if (option && typeof option === "object") {
            myChart.setOption(option);
        }

        window.addEventListener("resize", myChart.resize);

    }
}


window.UpdateChartData = function (id, newData) {
    var dom = document.getElementById(id);
    var myChart = echarts.getInstanceByDom(dom);
    if (!myChart) {
        console.error("Chart instance not found");
        return;
    }

    // 기존 시리즈 데이터를 가져와서 새 데이터로 업데이트
    var currentOption = myChart.getOption();
    var seriesData = JSON.parse(newData);
    for (var i = 0; i < currentOption.series.length; i++) {
        currentOption.series[i].data = seriesData[i].data;
    }

    myChart.setOption(currentOption);
};



window.DisposeChart = function (chartId) {
    var dom = document.getElementById(chartId);
    if (dom) {
        var myChart = echarts.getInstanceByDom(dom);
        if (myChart) {
            myChart.dispose();
        }
    }
}

먼저 질문에 답글 달아주신분들 감사합니다.

질문 이후 좀 찾아보고 수정했는데 아래 수정 사항이 문제가 없을지는 좀 지켜봐야 할 것 같아
모니터링중입니다.

아래 정리 내용은 제가 서치해서 찾아본 건데 확실하지는 않지만 신빙성 있어보여 정리 해보려고 합니다.

  1. static으로 선언된 변수를 컴포넌트에 직접 바인딩 할 경우 메모리가 상승할 수 있는 이유
  • static으로 선언된 변수의 생명주기는 프로그램의 생명주기와 일치하여 컴포넌트가 파괴되더라도 참조가 살아 있어 가비지가 해당 참조의 메모리를 해제 하지 않아 유지됨.
  • static으로 된 변수를 사용해야 할 경우 컴포넌트 내부에 변수를 할당 하고 바인딩한다.
    해당 변수에 static변수의 값또는 참조를 함.
    이때 참조 변수를 굳이 깊은복사가 아닌 값참조를 해도되는 이유는 컴포넌트 내부에서 선언된 변수의 생명주기는 컴포넌트의 생명주기와 일치해서 컴포넌트가 파괴되면 해당 변수의 참조도 해제 되어 가비지가 메모리를 회수함.
  1. echart를 데이터 갱신마다 init을 해서 새로운 객체를 만들 경우 이전 객체가 자동으로 참조가 해제 되지 않아 메모리에 남게됨.
  • 데이터 갱신마다 init을 할 경우 기존 인스턴스된 객체를 dispose하고 새로운 객체 생성 필요
  • 이미 인스턴스된 객체가 있을경우 해당 객체를 재사용함.

제가 이해한 내용으로는 이런데 혹시 제가 이해하는 부분이 맞을까요 … ?

2 Likes

자바 스크립트를 C#코드는 변경하는 것이 어려운지요?

블레이저에서는 자바스크립트가 돔객체를 관리하는 것을 지양하는 게 좋습니다.

1 Like

일단 저는 echarts를 사용해본 적은 없고;;
코드만 보고 답변드려서 죄송합니다…ㅠ

window.addEventListener("resize", myChart.resize);

이 부분에서 차트가 dispose되면 EventListener도 해제가 되는건지 확인해볼 필요가 있을 것 같아요.

dispose코드 호출 직전에

window.removeEventListener("resize", myChart.resize);

이걸 먼저 호출해보시고 테스트해보세요

1 Like

echart가 javascript 라이브러리라 이런 방식으로 사용했는데
혹시 다른방법이 있을까요?

제가 blazor 프로젝트도 처음이고 웹프로젝트도 처음이라 배경지식이 많이 없어서요 …

2 Likes

listener dispose하는 로직을 위에 빠트렸네요 …
현재

window.RemoveResizeListener = function (chartId) {
    var dom = document.getElementById(chartId);
    if (!dom) {
        console.warn("DOM element not found for chart ID:", chartId);
        return;
    }

    var myChart = echarts.getInstanceByDom(dom);
    if (myChart) {
        window.removeEventListener("resize", myChart.resize);
    } else {
        console.warn("ECharts instance not found for DOM element:", chartId);
    }
} 

이렇게 초기화 진행하고 있어요
컴포넌트 dispose될때 차트 dispose전 리스너 dispose하는 로직이 들어가있어요.

확인해 보니까 echart에 의해서 메모리 증가는 처음 echarts.init 하고
데이터 갱신되더라도 동일한 메소드를 호출하는데 그때마다 echarts.init를 해서 기존 인스턴스된 객체는 dispose되지 않아 메모리가 계속 남은거 같아요
우선 현재는 기존 인스턴스된 객체가 있을경우 재사용 하도록 수정 하고 모니터링 중인데 며칠 두고 봐야할거같아요

4 Likes

자바스크립트 라이브러리라면 다른 방법은 없는 것 같습니다.

블레이저 용 차트 라이브러리를 사용하든가, svg 태그로 만들던가 할 수는 있을 것 같습니다.
전자는 여전히 특정 자바스크립트에 종속될 확률이 높고, 후자는 차트의 종류가 다양한 경우, 배보다 배꼽이 커질 수도 있습니다. (선형 차트는 그렇게 어려운게 아닌 것 같기는 합니다)

2 Likes

기술관련된 질문은 알맞는 카테고리를 충분히 찾아보시고 질문해주시기를 부탁드립니다.

이번 스레드는 제가 옮겼습니다.

3 Likes

혹시 단순히 블레이저에 차트를 올리는 기능이 필요하시다면,
머드블레이저를 활용해 보시면 어떨까요? C#만으로 충분히 훌륭하게 머티리얼 디자인으로 웹 표시가 가능합니다.
머드블레이저 링크입니다.

부끄럽지만 제가 테스트성으로 올려둔 샘플 머드블레이저 페이지입니다.
https://countjung.github.io/MudBlazorCountJungLab/
혹시나 도움될까 해서 댓글 달았습니다. ㅎㅎ

3 Likes

감사합니다
svg 테그라는것도 한번 찾아봐야겠네요

2 Likes

네 감사합니다!

2 Likes

미적 감각이 있으시네요
제가 하면 개발 새발 되는데 … ㅎㅎ
회사 내부에서 echart를 사용하기로 해서 어쩔수 없이 사용하게됬어요 …

감사합니다.!

2 Likes

저는 미적 감각이 없습니다. 라이브러리가 했죠 ㅎㅎ
echart를 사용한다고 결정되었다면 React 환경으로 개발하시는게 맞지 않을까요?
제 생각에는 차라리 순수 Javascript로 개발하는게 나아보이네요 환경적 측면에서요

3 Likes