C #의 메모리 누수
관리되는 시스템에서 모든 핸들, 구현 된 항목 IDispose
이 삭제 되었는지 확인할 때 메모리 누수가 가능 합니까?
일부 변수가 누락 된 경우가 있습니까?
이벤트 핸들러는 명백하지 않은 메모리 누수의 매우 일반적인 원인입니다. object2에서 object1에 대한 이벤트를 구독 한 다음 object2.Dispose ()를 수행하고 존재하지 않는 척 (그리고 코드에서 모든 참조를 삭제)하면 object2가 발생하지 않도록하는 암시 적 참조가 object1의 이벤트에 있습니다. 쓰레기 수거.
MyType object2 = new MyType();
// ...
object1.SomeEvent += object2.myEventHandler;
// ...
// Should call this
// object1.SomeEvent -= object2.myEventHandler;
object2.Dispose();
이것은 유출의 일반적인 경우입니다. 이벤트에서 쉽게 구독 취소하는 것을 잊었습니다. 물론 object1이 수집되면 object2도 수집되지만 그때까지는 수집되지 않습니다.
나는 C ++ 스타일의 메모리 누수가 가능하다고 생각하지 않습니다. 가비지 수집기는이를 고려해야합니다. 개체를 다시 사용하지 않더라도 개체 참조를 집계하는 정적 개체를 만들 수 있습니다. 이 같은:
public static class SomethingFactory
{
private static List<Something> listOfSomethings = new List<Something>();
public static Something CreateSomething()
{
var something = new Something();
listOfSomethings.Add(something);
return something;
}
}
이는 명백히 어리석은 예이지만 관리되는 런타임 메모리 누수와 동일합니다.
다른 사람들이 지적했듯이 메모리 관리자에 실제 버그가없는 한 관리되지 않는 리소스를 사용하지 않는 클래스는 메모리를 누수하지 않습니다.
.NET에서 볼 수있는 것은 메모리 누수가 아니라 폐기되지 않는 객체입니다. 가비지 수집기가 개체 그래프에서 찾을 수있는 한 개체는 삭제되지 않습니다. 따라서 모든 살아있는 객체가 객체에 대한 참조를 가지고 있으면 폐기되지 않습니다.
이벤트 등록은이를 가능하게하는 좋은 방법입니다. 객체가 이벤트에 등록하면 등록 된 모든 객체에 대한 참조가 있고 객체에 대한 다른 모든 참조를 제거하더라도 등록을 취소 할 때까지 (또는 등록한 객체에 연결할 수 없게 될 때까지) 살아남습니다.
따라서 사용자 모르게 정적 이벤트에 등록하는 객체를주의해야합니다. ToolStrip
예를 들어 의 멋진 기능은 디스플레이 테마를 변경하면 새 테마에 자동으로 다시 표시된다는 것입니다. 정적 SystemEvents.UserPreferenceChanged
이벤트 에 등록하여이 멋진 기능을 수행합니다 . Windows 테마를 변경하면 이벤트가 발생 ToolStrip
하고 이벤트를 수신하는 모든 개체에 새 테마가 있음을 알립니다.
자, ToolStrip
양식에 a 를 버리기로 결정했다고 가정 하십시오.
private void DiscardMyToolstrip()
{
Controls.Remove("MyToolStrip");
}
이제 당신 ToolStrip
은 결코 죽지 않을 것입니다. 더 이상 양식에 없더라도 사용자가 테마를 변경할 때마다 Windows는 존재하지 않는 사람들에게 성실하게 알려줍니다 ToolStrip
. 가비지 수집기가 실행될 때마다 "그 개체를 버릴 수 없습니다 UserPreferenceChanged
. 이벤트에서 사용하고 있습니다."라고 생각합니다.
그것은 메모리 누수가 아닙니다. 하지만 그럴 수도 있습니다.
이와 같은 것들이 메모리 프로파일 러를 귀중하게 만듭니다. 메모리 프로파일 러를 실행하면 " ToolStrip
내 양식에 개체가 하나만 있어도 힙에 10,000 개의 개체 가있는 것 같습니다 . 어떻게 된 일입니까?" 라고 말할 수 있습니다 .
오, 그리고 어떤 사람들이 속성 설정자가 왜 악하다고 생각하는지 궁금한 경우 : 이벤트 ToolStrip
에서 등록을 취소하려면 속성을 .UserPreferenceChanged
Visible
false
대리인은 직관적이지 않은 메모리 누수를 초래할 수 있습니다.
인스턴스 메서드에서 대리자를 만들 때마다 해당 인스턴스에 대한 참조가 해당 대리자 "내"에 저장됩니다.
또한 여러 대리자를 멀티 캐스트 대리자로 결합하는 경우 해당 멀티 캐스트 대리자가 어딘가에서 사용되는 한 가비지 수집되지 않도록 유지되는 수많은 개체에 대한 하나의 큰 참조가 있습니다.
WinForms 응용 프로그램을 개발하는 경우 미묘한 "누수"가 Control.AllowDrop
속성입니다 (드래그 앤 드롭을 활성화하는 데 사용됨). AllowDrop
가 "true"로 설정된 경우 CLR은 System.Windows.Forms.DropTarget
. 이 문제를 해결하려면 Control
의 AllowDrop
속성이 false
더 이상 필요하지 않을 때로 설정되어 있는지 확인 하고 나머지는 CLR에서 처리합니다.
이미 언급했듯이 참조를 유지하면 시간이 지남에 따라 메모리 사용량이 증가합니다. 이 상황에 들어가는 쉬운 방법은 이벤트를 사용하는 것입니다. 다른 개체가 수신하는 일부 이벤트가있는 수명이 긴 개체가있는 경우 리스너가 제거되지 않으면 수명이 긴 개체의 이벤트는 더 이상 필요하지 않은 후에도 다른 인스턴스를 유지합니다.
.NET 응용 프로그램에서 메모리 누수가 발생하는 유일한 이유는 수명이 종료되었지만 개체가 계속 참조되기 때문입니다. 따라서 가비지 수집기는 수집 할 수 없습니다. 그리고 그들은 오래 사는 물건이됩니다.
객체의 수명이 다했을 때 구독을 해지하지 않고 이벤트를 구독함으로써 누출을 일으키는 것이 매우 쉽다는 것을 알았습니다.
내 새 기사가 유용 할 수 있습니다. .NET 응용 프로그램에서 메모리 및 리소스 누수를 감지하고 방지하는 방법
리플렉션 방출 은 내장 된 객체 디시리얼라이저 및 멋진 SOAP / XML 클라이언트와 같은 또 다른 잠재적 누수 원인입니다. 적어도 이전 버전의 프레임 워크에서는 종속 AppDomain에서 생성 된 코드가 언로드되지 않았습니다.
관리 코드에서 메모리를 누수 할 수 없다는 것은 신화입니다. 물론 관리되지 않는 C ++보다 훨씬 어렵지만이를 수행하는 방법은 백만 가지가 있습니다. 참조, 불필요한 참조, 캐싱 등을 보유하는 정적 객체. "올바른"방식으로 작업을 수행하면 많은 객체가 필요 이상으로 늦게까지 가비지 수집되지 않습니다. 이는 제 생각에는 메모리 누수와도 같습니다. 이론적이지 않은 실용적인 방식으로.
다행히도 도움이 될 수있는 도구가 있습니다. 저는 Microsoft의 CLR Profiler 를 많이 사용합니다. 지금까지 작성된 도구 중 가장 사용자 친화적 인 도구는 아니지만 확실히 매우 유용하며 무료입니다.
객체에 대한 모든 참조가 사라지면 가비지 수집기는 다음 패스에서 해당 객체를 해제합니다. 메모리 누수가 불가능하다고 말하지는 않겠지 만, 메모리 누수는 상당히 어렵습니다. 누수하려면 인식하지 못한 채 주위에 앉아있는 객체에 대한 참조를 가져야합니다.
예를 들어 개체를 목록으로 인스턴스화 한 다음 작업이 끝났을 때 목록에서 제거하는 것을 잊고 삭제하는 것을 잊은 경우입니다.
관리되지 않는 리소스가 제대로 정리되지 않으면 누수가 발생할 수 있습니다. IDisposable 을 구현하는 클래스가 누출 될 수 있습니다.
그러나 일반 개체 참조는 하위 수준 언어처럼 명시적인 메모리 관리가 필요하지 않습니다.
실제로 메모리 누수는 아니지만 큰 개체를 사용할 때 메모리가 부족해지기 쉽습니다 (올바르게 기억한다면 64K 이상). LOH에 저장되며 조각 모음되지 않습니다. 따라서 이러한 큰 개체를 사용하고이를 해제하면 LOH의 메모리가 해제되지만 해당 사용 가능한 메모리는이 프로세스를 위해 .NET 런타임에서 더 이상 사용되지 않습니다. 따라서 LOH에서 몇 개의 큰 물체를 사용하여 LOH의 공간을 쉽게 부족할 수 있습니다. 이 문제는 Microsoft에 알려져 있지만 지금은이 문제에 대한 해결책을 계획 중입니다.
The only leaks (other than bugs in the runtime which may be present, though not terribly likely due to garbage collection) are going to be for native resources. If you P/Invoke into a native library which opens file handles, or socket connections, or whatever on your managed application's behalf, and you never explicitly close them (and don't handle them in a disposer or destructor/finalizer), you can have memory or resource leaks because the runtime cannot manage all of those automatically for you.
If you stick with purely managed resources, though, you should be just fine. If you experience any form of memory leak without calling into native code, then that's a bug.
While it is possible that something in the framework has a leak, more then likely you have something that isn't being being disposed of properly or something is blocking the GC from disposing of it, IIS would be a prime candidate for this.
Just remember that not everything in .NET is fully managed code, COM interop, file io like file streams, DB requests, images, etc.
A problem we had a while ago (.net 2.0 on IIS 6) was that we would create an image and then dispose of it but IIS wouldn't release the memory for a while.
At my last job, we were using a 3rd party .NET SQLite library which leaked like a sieve.
We were doing a lot of rapid data inserts in a weird situation where the database connection had to be opened and closed each time. The 3rd party lib did some of the same connection opening that we were supposed to do manually and didn't document it. It also held the references somewhere we never did find. The result was 2x as many connections being opened as were supposed to be and only 1/2 getting closed. And since the references were held, we had a memory leak.
This is obviously not the same as a classic C/C++ memory leak but for all intents and purposes it was one to us.
If it's considered memory leak, it could be achieved with this kind of code, too:
public class A
{
B b;
public A(B b) { this.b = b; }
~A()
{
b = new B();
}
}
public class B
{
A a;
public B() { this.a = new A(this); }
~B()
{
a = new A(this);
}
}
class Program
{
static void Main(string[] args)
{
{
B[] toBeLost = new B[100000000];
foreach (var c in toBeLost)
{
toBeLost.ToString(); //to make JIT compiler run the instantiation above
}
}
Console.ReadLine();
}
}
Small functions help in avoiding "memory leaks". Because garbage collector frees local variables at the end of functions. If function is big and takes a lot of memory you yourself have to free local variables that take a lot of memory and are no longer needed. Similary global variables (arrays, lists) are also bad.
I experienced memory leaks in C# when creating images and not disposing them. Which is a little strange. People say you have to call .Dispose() on every object that has it. But documentation for graphical C# functions doesn't always mention this, for example for function GetThumbnailImage(). C# compiler should warn you about this I think.
A self reminder How to find Memory Leak:
- Remove and gc.collect calls.
- Wait until we have sure the the memory is leaking.
- Create dump file from the task manager.
- Open the dump files using DebugDiag.
- Analyse the result first. The result from there should help us, which isssue usually takes most of the memory.
- Fix the code until no memory leak can be found in there.
- Use 3rd party application such as .net profiler. (We can use trial, but need to resolve the issue ASAP. The first dump should help us mostly about how the leaking)
- If the issue is in virtual memory, need to watch the unmanaged memory. (Usually there is another configuration in there that need to be enabled)
- Run 3rd party application based on how it's used.
Common memory leak issue:
- Events/delegates is never removed. (When disposing, make sure the event is unregistered) - see ReepChopsey answer
- List/Dictionary were never cleared.
- Object referenced to another object that is saved in memory will never disposed. (Clone it for easier management)
In a Console or Win app create a Panel
object(panel1) and then add 1000 PictureBox
having its Image
property set then call panel1.Controls.Clear
. All PictureBox controls are still in the memory and no way GC
can collect them:
var panel1 = new Panel();
var image = Image.FromFile("image/heavy.png");
for(var i = 0; i < 1000;++i){
panel1.Controls.Add(new PictureBox(){Image = image});
}
panel1.Controls.Clear(); // => Memory Leak!
The correct way of doing it would be
for (int i = panel1.Controls.Count-1; i >= 0; --i)
panel1.Controls[i].Dispose();
Memory leaks in calling Controls.Clear()
Calling the Clear method does not remove control handles from memory. You must explicitly call the Dispose method to avoid memory leaks
You can have a memory leak when using the .NET XmlSerializer, because it uses unmanaged code underneath which will not be disposed.
See docs and search for 'memory leak' on this page:
ReferenceURL : https://stackoverflow.com/questions/620733/memory-leak-in-c-sharp
'IT박스' 카테고리의 다른 글
Windows 7 Git Bash에서 현재 위치에서 디렉터리를 탐색하는 방법이 있습니까? (0) | 2021.01.09 |
---|---|
화면 키보드를 닫으려면 어떻게합니까? (0) | 2021.01.09 |
코드에서 동적 리소스 스타일을 할당하는 방법은 무엇입니까? (0) | 2021.01.09 |
최대 너비를 사용하여 CSS에서 비례 적으로 이미지 크기 조정 (0) | 2021.01.09 |
NSString이 null인지 감지하는 방법은 무엇입니까? (0) | 2021.01.09 |