C#에서는 문자열 연결이 안전하지 않습니다. StringBuilder를 사용해야 합니까?

StackOverflow https://stackoverflow.com/questions/779051

문제

내 질문은 이것입니다: C#의 문자열 연결은 안전한가요?문자열 연결로 인해 예기치 않은 오류가 발생하고 StringBuilder를 사용하여 해당 문자열 연결을 바꾸면 해당 오류가 사라지는 경우 이는 무엇을 의미할까요?

배경: 저는 작은 명령줄 C# 응용 프로그램을 개발 중입니다.명령줄 인수를 사용하고 약간 복잡한 SQL 쿼리를 수행하며 약 1300행의 데이터를 형식화된 XML 파일로 출력합니다.

내 초기 프로그램은 디버그 모드에서 항상 잘 실행됩니다.그러나 릴리스 모드에서는 약 750번째 SQL 결과에 도달한 다음 오류와 함께 종료됩니다.오류는 SqlDataReader 개체의 Read() 메서드가 방금 true를 반환했음에도 불구하고 특정 데이터 열을 읽을 수 없다는 것입니다.

이 문제는 이전에 "string1 + string2"가 있었던 코드의 모든 작업에 StringBuilder를 사용하여 해결되었습니다.StringBuilder가 이미 사용 중인 SQL 쿼리 루프 내부의 문자열 연결에 대해 말하는 것이 아닙니다.나는 코드 앞부분에서 2~3개의 짧은 문자열 변수 사이의 간단한 연결에 대해 이야기하고 있습니다.

나는 C#이 몇 개의 문자열을 함께 추가하기 위한 메모리 관리를 처리할 만큼 똑똑하다는 인상을 받았습니다.내가 잘못?아니면 다른 종류의 코드 문제를 나타냅니까?

도움이 되었습니까?

해결책

당신이하는 일과는 별도로 문자열이나 문자열 빌더 대신 XML API로 가장 잘 수행 될 수 있습니다. StringBuilder로 전환하는 것은 오류를 가리거나 우아하게 넘어 갔지만 문자열을 사용하는 것이 실제로 원인이었을 것입니다.

다른 팁

귀하의 질문에 답변하려면:C#(및 일반적으로 .NET)의 문자열 연결 ~이다 "안전"하지만 설명대로 긴밀한 루프에서 수행하면 심각한 메모리 압박이 발생하고 가비지 수집기에 부담을 줄 수 있습니다.

당신이 말하는 오류는 일종의 자원 고갈과 관련이 있을 것이라고 추측할 수 있지만, 더 자세한 내용을 제공할 수 있다면 도움이 될 것입니다. 예를 들어 예외를 받았습니까?애플리케이션이 비정상적으로 종료되었나요?

배경:.NET 문자열은 변경할 수 없으므로 다음과 같이 연결을 수행할 때:

var stringList = new List<string> {"aaa", "bbb", "ccc", "ddd", //... };
string result = String.Empty;
foreach (var s in stringList)
{
    result = result + s;
}

이는 대략 다음과 동일합니다.

string result = "";
result = "aaa"
string temp1 = result + "bbb";
result = temp1;
string temp2 = temp1 + "ccc";
result = temp2;
string temp3 = temp2 + "ddd";
result = temp3;
// ...
result = tempN + x;

이 예제의 목적은 루프를 돌 때마다 새로운 임시 문자열이 할당된다는 점을 강조하는 것입니다.

문자열은 변경할 수 없으므로 런타임에는 결과 끝에 다른 문자열을 추가할 때마다 새 문자열을 할당하는 것 외에는 다른 옵션이 없습니다.

비록 result 문자열은 최신 및 가장 뛰어난 중간 결과를 가리키도록 지속적으로 업데이트되므로 거의 즉시 가비지 수집 대상이 되는 이름 없는 임시 문자열이 많이 생성됩니다.

이 연결이 끝나면 메모리에 다음 문자열이 저장됩니다(단순화를 위해 가비지 수집기가 아직 실행되지 않았다고 가정).

string a = "aaa";
string b = "bbb";
string c = "ccc";
// ...
string temp1 = "aaabbb";
string temp2 = "aaabbbccc";
string temp3 = "aaabbbcccddd";
string temp4 = "aaabbbcccdddeee";
string temp5 = "aaabbbcccdddeeefff";
string temp6 = "aaabbbcccdddeeefffggg";
// ...

이러한 암시적 임시 변수는 모두 거의 즉시 가비지 수집에 적합하지만 여전히 할당되어야 합니다.긴밀한 루프에서 연결을 수행하면 가비지 수집기에 많은 부담이 가해지며, 그렇지 않으면 코드가 매우 느리게 실행됩니다.저는 이 방법으로 성능에 미치는 영향을 직접 확인했으며, 연결된 문자열이 커질수록 그 효과는 정말 극적으로 변합니다.

권장되는 접근 방식은 항상 StringBuilder 여러 문자열 연결을 수행하는 경우. StringBuilder 문자열을 구성하는 데 필요한 할당 수를 줄이기 위해 변경 가능한 버퍼를 사용합니다.

루프에서 많은 수의 문자열을 연결하는 경우 StringBuilder를 사용하는 것보다 메모리를 더 많이 사용하지만 문자열 연결은 안전합니다.그리고 극단적인 경우에는 메모리가 부족해질 수도 있습니다.

거의 확실하게 코드의 버그입니다.

어쩌면 매우 많은 수의 문자열을 포함하고 있을 수도 있습니다.아니면 완전히 다른 것일 수도 있습니다.

근본 원인에 대한 선입견 없이 디버깅으로 돌아가겠습니다. 여전히 문제가 있는 경우 문제를 재현하고 코드를 게시하는 데 필요한 최소한으로 줄이십시오.

문자열 빌더 버전과 연결 버전이 얼마나 걸리나요? DB에 대한 연결이 닫힐 수 있습니다. 만약 당신이 많은 동의를하고 있다면, 나는 조금 더 효율적이기 때문에 StringBuilder와 함께 갈 것입니다.

한 가지 원인은 .NET에서 문자열이 불변 할 수 있기 때문에 연결과 같은 작업에서 작업을 수행 할 때 실제로 새 문자열을 생성하는 것입니다.

또 다른 가능한 원인은 문자열 길이가 int이므로 가능한 최대 길이는 int32.maxvalue 또는 2,147,483,647입니다.

두 경우 모두 StringBuilder 가이 유형의 작업에 대해 "String1 + String2"보다 낫습니다. 비록 내장 XML 기능을 사용하는 것이 더 나을 것입니다.

string.Concat(string[]) 현악기를 연결하는 가장 빠른 방법입니다. 그것은 흩어져 죽인다 StringBuilder 루프에서 사용될 때의 성능, 특히 StringBuilder 각 반복에서. Google "C# String Format vs StringBuilder"또는 이와 유사한 경우에 많은 참조가 있습니다.http://www.codeproject.com/kb/cs/stringbuilder_vs_string.aspx 타임즈에 대한 아이다이를 제공합니다. 여기서 string.join은 연결 테스트에서 승리하지만 나는 이것이 믿습니다. string.Concat(string, string) 배열을 가져 오는 과부하 버전 대신 사용됩니다. 다른 방법으로 생성 된 MSIL 코드를 살펴보면 후드 아래에서 무슨 일이 일어나는지 알 수 있습니다.

여기 어둠 속에서의 샷이 있습니다 ...

.NET (StringBuilders 아님)의 문자열은 String 인턴 풀에 들어갑니다. 이것은 기본적으로 성능을 향상시키기 위해 문자열을 공유하기 위해 CLR이 관리하는 영역입니다. 그 한계가 무엇인지 모르겠지만 여기에는 약간의 한계가 있어야합니다. 나는 당신이하고있는 모든 연관성이 끈 인턴 풀의 천장에 부딪 치는 것이라고 생각합니다. 그래서 SQL은 예라고 말합니다. 나는 당신에게 가치가 있지만, 어디에도 넣을 수 없으므로 예외를 얻을 수 없습니다.

빠르고 쉬운 테스트는 이루어질 것입니다 ngen 어셈블리를하고 여전히 오류가 발생하는지 확인하십시오. ngen'ing 후에는 응용 프로그램이 더 이상 수영장을 사용하지 않습니다.

실패하면 Microsoft에게 연락하여 어려운 세부 사항을 얻으려고합니다. 내 아이디어가 그럴듯하게 들리지만 디버그 모드에서 왜 작동하는지 모르겠습니다. 아마도 디버그 모드에서 문자열은 인턴되지 않았을 것입니다. 나는 또한 전문가가 아닙니다.

문자열을 함께 합성 할 때 나는 항상 StringBuilder를 사용합니다. 이를 위해 설계되었으며 단순히 "String1 + String2"를 사용하는 것보다 더 효율적입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top