2010-01-04 4 views
6

Edit2 : 난 그냥 내 질문을 분명히하고 싶습니다 : 왜, 각 반복 AppendToLog(), 응용 프로그램에서 더 많은 15mb를 사용합니까? (원본 로그 파일의 크기)이 함수의 메모리 누수는 어디에서입니까?

나는 HTML 문서의 파일 경로를 받고, 구문 분석을하고 파일에 추가하는 AppendToLog()라는 함수가있다. 이 방법은 다음과 같이 호출됩니다.

this.user_email = uemail; 
string wanted_user = wemail; 

string[] logPaths; 
logPaths = this.getLogPaths(wanted_user); 

foreach (string path in logPaths) 
{    

    this.AppendToLog(path);     

} 

모든 반복에서 RAM 사용량은 15MB 정도 증가합니다. 이 기능입니다 : (긴 보이지만 그것은 간단합니다)

public void AppendToLog(string path) 
{ 

Encoding enc = Encoding.GetEncoding("ISO-8859-2"); 
StringBuilder fb = new StringBuilder(); 
FileStream sourcef; 
string[] messages; 

try 
{ 
    sourcef = new FileStream(path, FileMode.Open); 
} 
catch (IOException) 
{ 
    throw new IOException("The chat log is in use by another process."); ; 
} 
using (StreamReader sreader = new StreamReader(sourcef, enc)) 
{ 

    string file_buffer; 
    while ((file_buffer = sreader.ReadLine()) != null) 
    { 
     fb.Append(file_buffer); 
    }     
} 

//Array of each line's content 
messages = parseMessages(fb.ToString()); 

fb = null; 

string destFileName = String.Format("{0}_log.txt",System.IO.Path.GetFileNameWithoutExtension(path)); 
FileStream destf = new FileStream(destFileName, FileMode.Append); 
using (StreamWriter swriter = new StreamWriter(destf, enc)) 
{ 
    foreach (string message in messages) 
    { 
     if (message != null) 
     { 
      swriter.WriteLine(message); 
     } 
    } 
} 

messages = null; 

sourcef.Dispose(); 
destf.Dispose(); 


sourcef = null; 
destf = null; 
} 

나는이와 일 봤는데 나는 :(

편집 무엇을 해야할지하지 않습니다이 ParseMessages이다하는을 HtmlAgilityPack를 사용하는 기능은 GC는 매우 지적이다.

public string[] parseMessages(string what) 
{ 
StringBuilder sb = new StringBuilder(); 
HtmlDocument doc = new HtmlDocument(); 

doc.LoadHtml(what);    

HtmlNodeCollection messageGroups = doc.DocumentNode.SelectNodes("//body/div[@class='mplsession']"); 
int messageCount = doc.DocumentNode.SelectNodes("//tbody/tr").Count; 

doc = null; 

string[] buffer = new string[messageCount]; 

int i = 0; 

foreach (HtmlNode sessiongroup in messageGroups) 
{ 
    HtmlNode tablegroup = sessiongroup.SelectSingleNode("table/tbody"); 

    string sessiontime = sessiongroup.Attributes["id"].Value; 

    HtmlNodeCollection messages = tablegroup.SelectNodes("tr"); 
    if (messages != null) 
    { 
     foreach (HtmlNode htmlNode in messages) 
     { 
      sb.Append(
        ParseMessageDate(
         sessiontime, 
         htmlNode.ChildNodes[0].ChildNodes[0].InnerText 
        ) 
       ); //Date 
      sb.Append(" "); 

      try 
      { 
       foreach (HtmlTextNode node in htmlNode.ChildNodes[0].SelectNodes("text()")) 
       { 
        sb.Append(node.Text.Trim()); //Name 
       } 
      } 
      catch (NullReferenceException) 
      { 
       /* 
       * We ignore this exception, it just means there's extra text 
       * and that means that it's not a normal message 
       * but a system message instead 
       * (i.e. "John logged off") 
       * Therefore we add the "::" mark for future organizing 
       */ 
       sb.Append("::"); 
      } 
      sb.Append(" "); 

      string message = htmlNode.ChildNodes[1].InnerHtml; 
      message = message.Replace(""", "'"); 
      message = message.Replace(" ", " "); 
      message = RemoveMedia(message); 
      sb.Append(message); //Message 
      buffer[i] = sb.ToString(); 
      sb = new StringBuilder(); 
      i++; 
     } 
    } 
} 
messageGroups = null; 
what = null; 
return buffer; 
} 
+3

parseMessages 란 무엇입니까? – Fredou

+0

거기에 추가했습니다. –

+0

결국'StreamReader'를 사용한다면'FileStream'이 필요 없습니다. 생성자를 확인하십시오. –

답변

5

많은 사람들이 언급했듯이 GC의 예상치 못한 빠른 속도로 메모리 저장소를 정리하지 못하는 아티팩트 일 수 있습니다. C#, Java 등 관리되는 언어에서는 정상입니다. 프로그램에 할당 된 메모리가 사용 가능한지 여부를 확인해야합니다. 사용법에 관심이 있다면 정말 필요합니다. 이와 관련된 질문은 다음과 같습니다.

  1. 프로그램 실행 기간은 얼마나됩니까? 지속적으로 운영되는 서비스 유형 프로그램입니까?
  2. 실행 기간에 걸쳐 OS에서 메모리를 계속 할당합니까, 아니면 정상 상태에 도달합니까? (당신은 그것을 발견 할만큼 충분히 길게 뛰었습니까?)

코드에 "메모리 누수"가있는 것 같지 않습니다. 관리 언어에서는 C/C++ 에서처럼 메모리 누수가 발생하지 않습니다 (안전하지 않은 또는 C/C++ 인 외부 라이브러리를 사용하지 않는 한). 내부 항목의 요소를 null으로 설정하지 않은 항목을 제거하라는 내용의 Collection 클래스와 같이 주위에 있거나 숨겨진 참조를주의해야합니다. 일반적으로 객체/클래스 변수에 객체 참조를 저장하지 않으면 스택에 참조가있는 객체 (지역 및 매개 변수)가 누출 될 수 없습니다. 코드에

일부 의견 :

  1. 당신은 적어도 적당한 크기로 StringBuilder을 미리 할당하여 메모리의 할당/해제를 줄일 수 있습니다. 전체 파일을 메모리에 보유해야 할 필요가 있음을 알고 있기 때문에 파일 크기에 할당하십시오 (실제로는 줄 바꿈 문자 시퀀스를 저장하지 않기 때문에 실제로는 약간 더 큰 버퍼를 제공하지만 파일은 아마도)를 가지고

    FileInfo fi = new FileInfo(path); 
    StringBuilder fb = new StringBuilder((int) fi.Length); 
    

    당신은 파일을 확인 할 수 있습니다

    은 확인하기 위해 fi를 사용하여 길이를 얻기 전에 존재한다. 질문 텍스트를 기반으로 파일 크기가 2GB 미만이므로 오류 검사없이 길이를 int으로 다운 캐스트합니다. 그 경우가 아니라면 파일을 캐스팅하기 전에 길이를 확인해야합니다. 파일이 너무 크면 예외가 발생합니다.

  2. 코드에있는 모든 variable = null 문을 제거하는 것이 좋습니다. 이것들은 스택에 할당 된 변수이기 때문에 필요하지 않습니다. 또한이 컨텍스트에서는 메소드가 오랫동안 지속되지 않으므로 GC에는 도움이되지 않습니다. 따라서 코드를 추가로 작성하면 이해하기가 더 어려워집니다.

  3. ParseMessages 메서드에서 NullReferenceException을 잡아서 텍스트가 아닌 노드라고 가정합니다. 이것은 장래에 혼란스러운 문제로 이어질 수 있습니다.

    if (node.Text != null) 
        sb.Append(node.Text.Trim()); //Name 
    

    예외가 예외적/예기치 않은 조건입니다 : 이것은 당신이 일반적으로 데이터에 존재할 수있는 무언가의 결과로 일어날 것으로 예상 뭔가이기 때문에 당신은 같은 코드의 상태를 확인한다 코드. 동일한 의미의 의미를 NullReferenceException에 할당하는 것은 null 참조가 있었기 때문에 현재 같은 블록의 다른 부분의 오류를 숨길 수 있습니다.

+0

당신이 옳았던 것처럼 보입니다. 메모리 누출은 없습니다. 그리고 내 코드에 대한 의견을 주셔서 감사합니다. 저는 여전히 C#을 이해하고 있습니다. –

1

한 가지는 당신이 일시적으로 각 실행 후 GC.Collect를 강요 시도 할 수 있습니다.있는 HTML 로그의 일부를 제거 할 때까지 메모리를 회수하지 않습니다 컬렉션의 비용을 느끼고있다. 복구 된 메모리의 가치가 있습니다.

편집 : 단지 GC.Collect를 수동으로 호출하는 것이 나쁜 습관 (정상적인 사용 사례의 경우 비정상 == 아마도 게임이나 일부 게임의로드 기능 일 수도 있음)을 이해하는 것이 중요하다는 점을 추가하기를 원했습니다. 일반적으로 가비지 수집기가 무엇을 결정할 지 알려 주어야합니다. 수집 작업을 기반으로하는 시스템 자원 및 기타 관련 정보에 대해 일반적으로 더 많은 정보를 얻을 수 있습니다.

+2

후에 그것을 제거하는 것을 잊지 마세요!, 나쁜 생각 – Fredou

+0

하하, 난 그걸 쓰고 있었는데, 감사합니다 :) – Gregory

0

null로 설정하기 전에 수동으로 메시지 배열과 stringbuilder를 지울 것입니다. 이 HTML 파일을 구문 분석의 너무 늦게 대신이 아니라면 프로세스가, 내가 제안을 얻었 할 것 같다 무엇을보고

편집

.

데이터 세트 스키마를 작성하고이를 사용하여 XML 로그 파일을 읽고 읽은 다음 xsl 파일을 사용하여 html 파일로 변환하십시오.

+0

그 마지막 지점에 대해 자세히 설명해 주시겠습니까? 다른 HTML 파일을 만들고 싶지는 않습니다. 내 응용 프로그램의 목적은 부피가 큰 HTML 로그를 제거하는 것입니다 : P –

0

try-catch 블록은 finally (정리)를 사용할 수 있습니다. using 문이 무엇을하는지 보면 catch를 try try와 동일하게 볼 수 있습니다. 예, GC를 실행하는 것도 좋은 생각입니다. 이 코드를 컴파일하고 ... 그것을 그것을 확실히 말할 어렵다 시도 또한

주지 않고, 적절하게 사용하여이 사람을 처리 :

파일 스트림 destf = 새로운하여 FileStream (destFileName,를 FileMode.APPEND을);

효과적인 C#의 2 판 조회 당신이 parseMessages에 문자열을 전달해야하는 이유에서주의 깊게 볼 것

2

, 즉 fb.ToString().

코드 주석은 각 행 내용의 배열을 반환한다고 말합니다. 그러나 실제로 로그 파일의 모든 줄을 fb로 읽은 다음 문자열로 변환합니다.

parseMessages()에서 큰 파일을 구문 분석하는 경우 StringBuilder 자체 또는 StreamReader를 parseMessages()에 전달하여 훨씬 효율적으로 수행 할 수 있습니다. 이렇게하면 현재 전체 로그 파일을 메모리로 강제 저장하는 ToString()을 사용하는 것과는 달리 언제든지 파일의 일부만 메모리에로드 할 수 있습니다.

가비지 수집으로 인해 .NET 응용 프로그램에서 실제 메모리 누수가 발생할 가능성이 적습니다. 파일과 같은 큰 리소스를 사용하는 것처럼 보이지 않으므로 실제 메모리 누수 가능성은 낮아집니다.

리소스를 삭제 한 것처럼 보입니다.하지만 GC는 다음 반복이 시작되기 전에 대용량 메모리 청크를 할당하고 할당을 해제하는 데 어려움을 겪고 있으므로 메모리 사용량이 늘어납니다.

GC.Collect()를 사용하면 메모리 할당을 강제로 수행 할 수 있지만 GC를 통해 수동으로 메모리를 관리하려는 시도를하기 전에 위의 제안 사항을 검토하는 것이 좋습니다.

[업데이트] parseMessages()와 HtmlAgilityPack (매우 유용한 라이브러리)의 사용을 보면 모든 로그에 대해 수행 할 수있는 크고 많은 메모리 할당이있을 것으로 보입니다.

HtmlAgility는 버퍼 배열 및 주요 함수의 할당과 결합 할 때 다양한 노드에 메모리를 할당합니다. GC가 계속 유지해야한다는 압력이 훨씬 더 커지고 있다고 확신합니다.

추측을 중지하고 실제 측정 항목을 얻으려면 ProcessExplorer을 실행하고 GC Gen 0,1,2 컬렉션 열을 표시하는 열을 추가하십시오. 그런 다음 응용 프로그램을 실행하고 콜렉션 수를 관찰하십시오. 이 열에 큰 숫자가 표시되면 GC가 어려워지고 메모리 할당을 줄이기 위해 다시 디자인해야합니다.

또는 Microsoft의 무료 CLR Profiler 2.0은 응용 프로그램 내에서 .NET 메모리 할당을 시각적으로 잘 보여줍니다.

+0

"실제로 로그 파일의 모든 줄을 fb로 읽어 들이고 나서 다음으로 변환합니다. 문자열. " 예, parseMessages()가 HtmlAgilityPack을 사용하여 파일을 스크랩했기 때문입니다. –

+0

@Daniel, HtmlAgilityPack은 StreamReader 등의 스트림에서 읽을 수도 있습니다 (Load() 메서드로 전달). 스트림을 사용하면 전체 문자열/파일을 메모리에로드하지 않아도됩니다. – Ash

0

명백한 메모리 누수가 표시되지 않습니다. 내 첫 번째 추측은 그것이 도서관에있는 것이라고 생각합니다.

SciTech의 .NET Memory Profiler는 이런 종류의 문제를 해결하는 데 유용한 도구입니다. 그들은 무료 2 주간 재판을 받았습니다.

부족한 점은 일부 라이브러리 함수를 주석 처리하고 파일을 읽었을 때 문제가 없어지는지 확인할 수 있고 데이터를 사용하지 않으면됩니다.

또한 메모리 사용 통계는 어디에서 찾으십니까? 작업 관리자가보고 한 통계는 실제 메모리 사용에 항상 유용하거나 반영되지는 않습니다.

4

메모리 누수가 없습니다. Windows 작업 관리자를 사용하여 .NET 응용 프로그램에서 사용하는 메모리를 측정하는 경우 GC는 작업 관리자가 반영하지 않는 복잡한 방식으로 메모리를 관리하기 때문에 현재 진행중인 작업을 명확하게 파악할 수 없습니다.

MS 엔지니어는 메모리가 누출되는 것 같은 .NET 응용 프로그램이 왜 그리 좋지 않은지에 대해 큰 article을 작성했으며 GC가 실제로 어떻게 작동하는지에 대한 깊이있는 설명에 대한 링크가 있습니다. 모든 .NET 프로그래머는이를 읽어야합니다.

+0

나는 이것을 인정한 것으로 표시 할 것이지만 나는 2 개의 답을 선택할 수 없다. 고맙습니다! –

0

HtmlDocument 클래스 (가능한 한 내가 결정할 수 있음)는 관리되는 코드에서 사용될 때 심각한 메모리 누수가 있습니다. 대신 XMLDOM 파서를 사용하는 것이 좋습니다 (잘 구성된 문서가 필요하지만 또 다른 +는 필요합니다).

+0

HtmlDocument를 사용하여 심각한 메모리 누출 문제에 대해 들어 본 적이 없습니다. 참고 문헌을 인용하거나 예제를 제공해 줄 수 있습니까? –

관련 문제