간만에 블로깅이다.
오늘은 .NET Framework의 가비지콜랙터에 대해 끄적여볼껀데,
가비지콜랙터, 메모리누수에 대해 눈꼽만큼이라도 고민해 본적이 있는 어린이들에게 도움이 됐으면 한다.

※여기서 [객체], [인스턴스변수], [포인터]는 모두 같은 의미이다.※


C나 C++에서는 힙 메모리를 할당한 후 그것을 관리하던 포인터를 잃어리면...
해당 쓰레드가 종료되기 전까지 세어나간 메모리를 돌려놓을 방법이 없다.
이것이 바로 일반적으로 메모리누수라고 부르는 것이다.

엄밀히 말하자면...
Java, C#으로 넘어오면서 프로그래머들은 메모리 누수로 부터 해방됐다.
바로 가비지콜랙터라는 놈이 힙 메모리를 자동으로 반환해주기 때문이다.

그럼에도 불구하고 아직도 우리는 메모리누수라는 말을 가끔 쓰고 있다.
그럼 Java나 C#을 사용하면서 얘기하는 메모리누수라는 것은 무엇이냐?
.....
여기서 메모리누수라는 것은.... 반환되어야할 시기에 반환되지 않는 메모리를 얘기하는 것이다.

가비지콜랙터는 객체(포인터, 인스턴스변수)가 NULL로 설정되면 사용하던 힙메모리를 자동으로 반환해준다.
물론 NULL을 명시적으로 주지 않아도,
함수내에서 생성했던 객체들은 함수의 활동이 끝나서 스택에서 퇴출될때 함께 반환된다.
(알아둬야 할 것은 NULL로 설정 되었다고해서 가비지콜랙터가 즉시 반환해주지는 않는다. 언젠가는 반환해주지만, 가비지콜랙터가 자주 출동하면 시스템이 느려진다. 이건 나중에 따로 포스팅을 해볼 생각이다.)

어쨋든 포인터를 NULL로 설정하지 않으면 해당 프로세스, 쓰레드, 함수가 종료되기 전까지는
가비지콜랙터가 메모리를 반환할 방법이 없다.
이런 것들이 쌓이는 것이 여기서 얘기하는 메모리누수인데....
가장 흔한 경우가 바로 활동시간이 아주 기~인 객체가 여러 객체들과 짝지어진 경우이다.
한두번 쓰이고 반환되어야할 많은 객체들이 활동시간이 긴 객체와 함께 오래오래 살아남아 문제가 된다.
자신을 참조하는 객체가 계속 살아있으니, 참조당하는 객체들이 NULL로 될수 없고,
NULL이 아닌 객체들을 가비지콜랙터가 반환할 수는 없으니 말이다.

따라서 메모리누수를 막기위해서는
첫째, 활동기간이 긴 객체와 짧은 객체는 되도록 참조하는 것을 피하고,
둘째, 사용이 끝난 포인터는 즉시 NULL로 할당해주는 코딩습관이 필요하다.
  괜히 순환문이 있는 길고 긴 함수가 종료되거나, 호출객체의 활동이 끝때까지 그대로 놔두면...
  언젠가 큰 화를 불러올지도 모른다.
셋째, Full 가비지콜랙터가 출동하는 것을 막는 것이 좋다.
  흠, 이건 메모리누수보단 Full 가비지콜랙터의 작동때문에 시스템이 느려지는 것을 막기위함인데...
  활동기간이 긴 객체는 최소한의 메모리만 사용하도록 하는 것이 좋다.

Google AdSense


.NET에서 사운드 볼륨을 조절하는 방법에 대해 적어본다.
볼륨 조절은 하드웨어(사운드카드) 제어에 속하는데,
닷넷에서는 하드웨어 제어 수단을 제공하지 않는다.

따라서 C#에서는 윈도우즈에서 제공하는 Win32 API를 사용해야한다.

거두절미하고,
사운드 제어를 위해 작성한 SoundUtils클래스 파일을 첨부한다.

파일 ==> <== 여기

첨부된 클래스를 간단하게 설명하자면...
public static으로 제공되는 함수는 아래의 4가지 인데...
GetVolume, SetVolumne, SetVolumePercent, PlaySound

볼륨을 50%로 만들고 싶으면
첨부파일을 프로젝트에 추가시킨후
자신의 소스에서 아래와 같이 하면된다.

Nagarry.SoundUtils.SetVolumePercent(50);


도움이 됐다면 리플 좀 남겨주시오!

Ps. 첨부된 소스코드는 시스템볼륨을 조절 하고있습니다.
확인해보니 비스타와 윈7에선 시스템볼륨조절이 막혔고, 미디어볼륨만 조절가능하다네요 ^_~@
Google AdSense



예전에 작성했던 XNA흐름(링크)은 대충 두리뭉실 했는데, 이번에는 그보다 좀더 들어가보자

미리 XNA 흐름의 포인트를 얘기하자면...
모든 컨텐츠를 로딩할 수 있는 곳은 LoadContent 메서드이고,
이것을 호출하는 곳은 Initialize 메서드 뿐이라는 것이다.

즉, 처음 딱한번 Initialize가 호출된 뒤에는 게임컴포넌트를 추가하더라도 그 컴포넌트의 컨텐츠는 로드되지 않는다는 것이다.

XNA의 논리적 흐름은 아래의 17단계로 나눌 수 있다.

1. Main에서 게임 생성자 호출
2. 게임 생성자는 게임컴포넌트 생성자 호출
3. XNA Framework에서 게임의 Initialize 메서드 호출
4. XNA Framework에서 각각의 게임컴포넌트에 해당하는 Initialize 메서드 호출
5. XNA Framework에서 각각의 Drawable 게임 커모넌트에 해당하는 LoadContent 메서드를 호출.
6. XNA Framework에서 게임의 LoadContect메서드를 호출
* 아래의 7~10단계 무한반복
7. XNA Framework에서 게임의 Update메서드를 호출
8. XNA Framework에서 각각의 게임컴포넌트에 해당하는 Update메서드를 호출
9. XNA Framework에서 게임의 Draw 메서드를 호출
10. XNA Framework에서 각각의 Drawable 게임컴포넌트에 해당하는 Draw 메서드를 호출

11. 장치를 유실할 경우(창의 최소/최대화, 해상도 변경 등) 장치 리셋 처리
     - UnloadContent 메서드 호출후 6단계에서 다시 시작
12. 사용자가 프로그램을 종료
13. XNA Framework에서 게임의 Dispose 메서드 호출
14. 게임의 Dispose 메서드에서 기본 객체의 Dispose 메서드를 호출한다.
15. XNA Framework에서 각각의 게임컴포넌트에 해당하는 Dispose 메서드 호출
16. XNA Framework에서 게임의 UnloadContent 메서드 호출
17. 프로그램의 Dispose 메서드로 focus가 반환되고 결국 프로그램이 종료됨

Google AdSense




FPS를 체크해 보자.
FPS(Frame Per Second)는 1초동안 표시되는 프레임수를 나타내는 것으로 게임이나 동영상의 성능을 측정하는 아주 중요한 수단이다.

측정방법은 이렇다.
일정기간동안 프레임표시횟수와 걸린시간을 체크했다가 이를 토대로 FPS를 계산해서 윈도창 제목에다 FPS를 표시하는 것이다.
간단하지 아니한가?




* 사전준비
FPS측정 함수를 만들기전에 Game1(특별히 바꾸지않았다면 Draw가 포함된 클래스)에서 준비해야할 것이있다.
첫째, 일단 FPS표시는 프로그램이 돌아가는 동안 지속적으로 시간을 체크해야하므로 전역변수나 그에 준하는 멤버변수가 필요하다.
둘째, 기본적으로 프레임이 모니터 수직동기화와 FPS60으로 맞춰져 있으므로 이를 풀어줘야 한다.

Game1클래스에 아래의 멤버변수를 추가하자.

private const float UPDATEINTERVAL = 1.0f; //측정의 기준기간, 더 늘리거나 줄여도 된다
private float m_fTimeSinceLastUpdate = 0.0f; //소요된 시간
private float m_fFramecount = 0; //프레임표시횟수


이젠 Game1의 생성자에 아래의 코드를 추가하여 모니터 수직동기화와 FPS60을 풀어주자

graphics.SynchronizeWithVerticalRetrace = false;    //Draw를 모니터 수직동기화하지 않음
IsFixedTimeStep = false;    //Update를 기본값(1/60초)로 하지 않음

자 이젠 준비가 완료되었다.

* 본격작성
그럼 BenchMark함수를 아래와 같이 작성하자

private void BenchMark(GameTime gameTime)
{
    float elapsed = (float)gameTime.ElapsedRealTime.TotalSeconds;
    m_fFramecount++;    //프레임카운트
    m_fTimeSinceLastUpdate += elapsed;

    //충분한 시간(UPDATEINTERVAL)을 FPS계산 구간으로 삼음
    if (m_fTimeSinceLastUpdate > UPDATEINTERVAL)
    {
        float fFps = m_fFramecount / m_fTimeSinceLastUpdate;    //FPS계산 - 프레임카운트 / 경과시간
        Window.Title = "FPS:" + fFps.ToString()
            + " - RT:" + gameTime.ElapsedRealTime.TotalSeconds.ToString()   //Draw호출당시간
            + " - GT:" + gameTime.ElapsedGameTime.TotalSeconds.ToString();  //Update호출당시간
        m_fFramecount = 0;
        m_fTimeSinceLastUpdate = 0.0f;
    }
}

* 수행
이제 BenchMark를 Draw함수에서 호출 해주면 된다.
base.Draw(gameTime) 호출 직전에 불러주면 될 것이다.

생성자에 추가했던 IsFixedTimeStep설정을 true로 하고 실행해보면 FPS 60에 거의 맞아떨어진다는 것도 확인 할 수 있을 것이다.(별다른 무거운 작업을 하지 않았다면 말이다)
Google AdSense
Win32 API 쓸때는 Window에서 생성되는 메세지 순서를 줄줄 꿰고 살았었는데...
.NET쪽으로 넘어와서는 순서에 어느정도 무신경해진거 같다.
그래도 항상 신경쓰는건 Load, Shown, Paint와 기타 키이벤트 정도?
그래서 정리해본다. 아래 순서는 banghazi님 블로그에서 그대로 따온 것이다.
(Naver는 텍스트복사가 안되서 그냥 타이핑했다. 쓸데없는 구속이 많은 네이버 ㅡㅡ)

.NET을 이용해 생성하는 윈도우폼의 이벤트 순서는 다음과 같다.

[폼실행]
Move
LocationChanged
StyleChanged
BindingContextChanged
Load
Layout
VisibleChanged
Activated
Shown
Paint
KeyUp


[다른창으로 가림]
Deactivate
Paint


[다른창으로 가림해제]
Actiavted
Paint


[클릭해서 다른창으로 가림해제]
Activated
Paint
MouseCaptureChanged


[마우스 움직임]
MouseEnter
MouseMove
MouseHover
MouseLeave


[키보드 누름]
PreviewKeyDown
KeyDown
KeyPress
KeyUp


[창종료]
FormClosing
FormClosed
Google AdSense

'끄적끄적 > Programming' 카테고리의 다른 글

[XNA] XNA 논리적 흐름  (0) 2011.03.21
[XNA] FPS측정을 통한 BenchMark  (2) 2011.03.18
[Oracle] White SPACE(특수문자) 입력하기  (0) 2010.06.18
[XNA] XNA 흐름  (0) 2010.05.17
[Oracle] 순위함수 Rank()  (2) 2009.12.21

+ Recent posts