[c#] 루프 중에 TextBox.Text에 추가하면 반복 할 때마다 더 많은 메모리를 차지하는 이유는 무엇입니까?

짧은 질문

180,000 번 실행되는 루프가 있습니다. 각 반복이 끝날 때 실시간으로 업데이트되는 TextBox에 결과를 추가해야합니다.

를 사용 MyTextBox.Text += someValue하면 응용 프로그램이 엄청난 양의 메모리를 사용하게되며 수천 개의 레코드를 사용하면 사용 가능한 메모리가 부족합니다.

TextBox.Text180,000 번에 텍스트를 추가하는 더 효율적인 방법이 있습니까?

편집 나는이 특정 경우의 결과에 대해 정말로 신경 쓰지 않지만 이것이 메모리 돼지 인 것처럼 보이는 이유와 TextBox에 텍스트를 추가하는 더 효율적인 방법이 있는지 알고 싶습니다.


긴 (원본) 질문

CSV 파일의 ID 번호 목록을 읽고 각각에 대한 PDF 보고서를 생성하는 작은 앱이 있습니다. 각 pdf 파일이 생성 된 후에 ResultsTextBox.Text는 처리되고 성공적으로 처리 된 보고서의 ID 번호가 추가됩니다. 프로세스는 백그라운드 스레드에서 실행되므로 ResultsTextBox는 항목이 처리 될 때 실시간으로 업데이트됩니다.

현재 180,000 개의 ID 번호에 대해 앱을 실행하고 있지만 시간이 지남에 따라 애플리케이션이 차지하는 메모리는 기하 급수적으로 증가하고 있습니다. 약 90K에서 시작하지만 약 3000 개 레코드가 약 250MB를 차지하고 4000 개 레코드가 애플리케이션이 약 500MB의 메모리를 차지합니다.

Results TextBox에 대한 업데이트를 주석 처리하면 메모리가 약 90K에서 상대적으로 고정되어 있으므로 쓰기 ResultsText.Text += someValue가 메모리를 먹는 원인 이라고 가정 할 수 있습니다 .

제 질문은 왜 이래요? 메모리를 차지하지 않는 TextBox.Text에 데이터를 추가하는 더 좋은 방법은 무엇입니까?

내 코드는 다음과 같습니다.

try
{
    report.SetParameterValue("Id", id);

    report.ExportToDisk(ExportFormatType.PortableDocFormat,
        string.Format(@"{0}\{1}.pdf", new object[] { outputLocation, id}));

    // ResultsText.Text += string.Format("Exported {0}\r\n", id);
}
catch (Exception ex)
{
    ErrorsText.Text += string.Format("Failed to export {0}: {1}\r\n",
        new object[] { id, ex.Message });
}

또한 앱이 일회성이며 모든 보고서를 생성하는 데 몇 시간 (또는 며칠)이 걸리는 것은 중요하지 않다는 점도 언급 할 가치가 있습니다. 내 주요 관심사는 시스템 메모리 제한에 도달하면 실행이 중지된다는 것입니다.

이 작업을 실행하기 위해 주석 처리 된 결과 TextBox를 업데이트하는 줄을 그대로 두어도 괜찮지 만 TextBox.Text향후 프로젝트를 위해에 데이터를 추가하는 메모리 효율적인 방법이 있는지 알고 싶습니다 .



답변

메모리 사용량이 너무 큰 이유는 텍스트 상자가 스택을 유지하여 사용자가 텍스트를 실행 취소 / 다시 실행할 수 있기 때문이라고 생각합니다. 해당 기능은 귀하의 경우에 필요하지 않은 것 같으므로 IsUndoEnabledfalse로 설정하십시오 .


답변

사용 TextBox.AppendText(someValue)대신에 TextBox.Text += someValue. TextBox.Text가 아닌 TextBox에 있기 때문에 놓치기 쉽습니다. StringBuilder와 마찬가지로 이것은 무언가를 추가 할 때마다 전체 텍스트의 복사본을 만드는 것을 방지합니다.

이것이 IsUndoEnabledkeyboardP의 답변 의 플래그와 어떻게 비교되는지 보는 것은 흥미로울 것입니다.


답변

text 속성에 직접 추가하지 마십시오. 추가를 위해 StringBuilder를 사용하고 완료되면 .text를 stringbuilder의 완성 된 문자열로 설정합니다.


답변

텍스트 상자를 사용하는 대신 다음을 수행합니다.

  1. 만일을 대비하여 텍스트 파일을 열고 오류를 로그 파일로 스트리밍하십시오.
  2. 목록 상자 컨트롤을 사용하여 오류를 표시하여 잠재적으로 대량의 문자열을 복사하지 않도록합니다.


답변

개인적으로 저는 항상 string.Concat*를 사용 합니다. 나는 일반적으로 사용되는 방법을 비교하는 프로파일 링 통계를 가지고있는 Stack Overflow에서 몇 년 전에 여기에서 질문을 읽은 것을 기억 string.Concat합니다.

그럼에도 불구하고 내가 찾을 수있는 최선은 이 참조 질문 과 내부적으로 를 사용하는 것을 언급하는 이 특정 String.FormatStringBuilder 질문 입니다. 이것은 당신의 기억력이 다른 곳에 있는지 궁금합니다.String.FormatStringBuilder

** James의 의견에 따르면 웹 기반 개발에 중점을두기 때문에 무거운 문자열 형식을 지정하지 않는다는 점을 언급해야합니다. *


답변

TextBox를 재고 할 수 있습니까? String Items를 보유한 ListBox는 아마도 더 잘 수행 될 것입니다.

그러나 주된 문제는 요구 사항 인 것 같습니다. 180,000 개의 항목을 표시하는 것은 (인간) 사용자를 겨냥 할 수 없으며 “실시간”에서 변경하는 것도 아닙니다.

바람직한 방법은 데이터 샘플 또는 진행률 표시기를 표시하는 것입니다.

가난한 사용자에게 덤프하고 싶을 때 배치 문자열이 업데이트됩니다. 어떤 사용자도 초당 2 ~ 3 개 이상의 변경 내용을 설명 할 수 없습니다. 따라서 초당 100 개를 생산한다면 50 개 그룹을 만드십시오.


답변

일부 응답은 이에 대해 암시했지만 아무도 놀라움을 표명하지 않았습니다. 문자열은 변경 불가능합니다. 즉, 생성 된 후에는 문자열을 수정할 수 없습니다. 따라서 기존 문자열에 연결할 때마다 새 문자열 개체를 만들어야합니다. 해당 문자열 개체와 관련된 메모리도 분명히 만들어야하는데, 문자열이 점점 커질수록 비용이 많이들 수 있습니다. 대학에서 저는 한때 Huffman 코딩 압축을 수행하는 Java 프로그램에서 문자열을 연결하는 아마추어 실수를 저질렀습니다. 매우 많은 양의 텍스트를 연결하는 경우 여기에 언급 된 일부에서 언급했듯이 단순히 StringBuilder를 사용할 수 있었을 때 문자열 연결이 실제로 당신을 해칠 수 있습니다.