2012년 1월 28일 토요일

Windows의 메모리를 관리해보자.

일반적으로 Windows 사용자 입장에서는 메모리 관리를 OS에 맡기거나 (즉 아무것도 하지 않거나), 저처럼 Freeware 를 사용하여 메모리를 관리하겠죠. 저는 IOBit Smart RAM 을 사용합니다만, 워낙 많은 Freeware 가 있으니 뭘 쓰던 그리 큰 문제는 아닙니다.

오늘 얘기하고자 하는 방향은 서버 관리자 입장에서 본 윈도우즈의 메모리 관리를 말하고자 합니다. 개인용 PC와는 달리 서버 시스템 특히 운영 서버의 경우에는 Freeware 같이 검증(?)되지 않은 것을 함부로 깔 수도 없습니다. 근데 이런 문제가 가끔 발생합니다.

  1. 캐시 시스템을 사용하는데, 이 놈이 Max 값으로 올라간 후 내려오지 않아요 (분명 Cache Entry 는 expires가 되어 메모리에서 해제된 것을 확인 했는데도)
  2. RDBMS 등에서 미리 여유분으로 잡아 먹고 있어요.
  3. 자주 사용하지 않는 시스템이 한번 실행 된 후 메모리를 반환하지 않아요

뭐 더 많은 상황이 있겠지만, 제가 간접적으로 겪은 1번 상황에 대해서만 얘기하겠습니다. 2번은 성능을 위해서라도 그냥 냅두시는 게 좋을 것이고, 3번은 1번에 묻어서 같이 해결 보는 걸로 하겠습니다.

자 그럼 우선 윈도우즈의 메모리 관리에 대해 알아봐야겠지요? 윈도우즈 OS 라는 놈이 메모리 관리에 대해서 그리 좋은 시스템이 못 되는 것은 유명합니다만, 그래도 쓸만은 합니다. 즉 특수한 극한 상황이 아니라면, 훌륭한 역할을 수행하고 있다는 얘기지요.

다만 메모리를 많이 쓰는 프로세스가 많은 일을 하고자 할 때, 메모리를 미리 선점한 놈들에게 정리 시키도록 해주면 훨씬 좋은 성능의 시스템이 될 수 있습니다.

위의 1번의 경우처럼 캐시시스템은 자신이 사용할 메모리를 예측하여, 미리 할당해서 사용하고, 메모리 사용을 더 이상 할 필요가 없어도, 향후 또 필요할 것을 대비해서 그냥 잡아둡니다. 그게 캐시 시스템 입장에서는 더 효과적이죠. 메모리를 반환하고, 필요할 때 다시 할당하는 일은 2중의 비용이 들어가니까요. (캐시 시스템 만든 개발자들이 똑똑하게 만들었겠죠) 다만 Memcached 나 SharedCache 처럼 메모리만을 기반으로 하는 캐시 시스템의 경우, 특별한 상황에서는 과도한 메모리를 사용하여, 운영자의 간담을 서늘케 하는 경우가 가끔 있습니다. (우린 이런 사람들을 새가슴 혹은 원리를 모르는 사람 이라고 하지요^^ – 농담입니다.)

다만 1번 경우 때문에 운영서버를 하루에 한번씩 RESET 하는 회사가 있어서요… 참 답답할 노릇이죠…

자 그럼 이런 상황에서 강제로 메모리를 반환 받을 수는 없을까요? OS 라면 가능하겠지요? OS가 대빵인데, 지들이 무슨 힘이 있겠어요?

자 여기서 보면 그럼 OS가 효율적으로 메모리 관리를 하면 1번과 같은 문제가 안 생기겠네요? 네 대부분 안 생깁니다. 다만 급격하게 메모리를 많이 사용해야 하는 프로세스가 늘어나면, Pagefile을 써야하고, 그럼 실행속도가 느려집니다. 이때서야 OS가 아… 너 메모리가 많이 부족하구나… 다른 놈들 메모리 좀 정리해서 방 비워줄께…

즉 뒷북이란 말이죠…

그럼 처음으로 돌아가서 메모리 관리용 Freeware를 사용하면 되잖아요… 운영지침상 서버에는 깔지 못한다니까요^^

그럼 어떻하지요? OS가 방을 비워주기를 기다리지 말고, 메모리 관리 Freeware 처럼 내 프로그램에서 OS에게 실행중인 프로세스들에게 사용하지 않는 메모리 비워라!!! 라고 요청할 수 있으면 되겠지요?

말이 참 길었죠? 실제 메모리를 반환하도록 하는 코드입니다.

public static class ProcessTool
{
#region << logger >>

private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
private static readonly bool IsDebugEnabled = log.IsDebugEnabled;

#endregion

/// <summary>
///
시스템에서 실행중인 모든 프로세스에 대해, 사용하지 않는 WorkingSet 메모리를 OS에게 반환하도록 합니다.
/// </summary>
/// <param name="excludeThisProcess">
현재 프로세스를 제외할 것인가 여부 (기본값은 제외)</param>
/// <param name="excludeProcessNames">
메모리 반환을 하지 않을 프로세스 명의 컬렉션</param>
public static void TrimAllProcessMemory(bool excludeThisProcess = true, string[] excludeProcessNames = null)
{
if(log.IsInfoEnabled)
log.Info("컴퓨터의 모든 프로세스에 대해 사용하지 않는 메모리를 OS에 반환하도록 합니다...");

var currentProcess = Process.GetCurrentProcess();

Parallel.ForEach(Process.GetProcesses(),
process =>
{
if(excludeThisProcess && (process.ProcessName == currentProcess.ProcessName))
return;

if(excludeProcessNames != null &&
excludeProcessNames.
Any(procName => procName == process.ProcessName))
return;

TrimProcessMemory(process);
});
}

/// <summary>
///
지정된 프로세스의 사용하지 않는 WorkingSet 메모리를 OS에게 반환하도록 합니다.
/// </summary>
/// <param name="process">
메모리 해제를 할 프로세스</param>
/// <returns>
메모리 해제 여부</returns>
public static bool TrimProcessMemory(Process process)
{
if(IsDebugEnabled)
log.Debug("프로세스의 Working Memory 중에 사용하지 않는 부분을 OS에 반환하도록 합니다.");

if(process == null)
return false;

bool _result;

try
{
long oldWorkingSet64 = process.WorkingSet64;
_result =
EmptyWorkingSet((long) process.Handle);

var targetProcess = Process.GetProcessById(process.Id);

if(_result)
{
if(log.IsInfoEnabled)
log.Info("프로세스[{0}]의 WorkingSet 메모리를 비웠습니다. 기존=[{1}], 현재=[{2}], 반환값=[{3}]",
process.
ProcessName, oldWorkingSet64, targetProcess.WorkingSet64, oldWorkingSet64 -
}
}
catch(Exception ex)
{
if(log.IsWarnEnabled)
{
log.Warn("Process[{0}]의 메모리를 정리하는데 예외가 발생했습니다...", process.ProcessName);
log.Warn(ex);
}
_result =
false;
}

return _result;
}

[
DllImport("psapi")]
internal static extern bool EmptyWorkingSet(long hProcess);
}




핵심은 psapi.dll  에 있는 EmptyWorkingSet(long hProcess) 입니다. 이 놈이 특정 프로세스의 사용하지 않는 메모리를 반환하게끔 합니다.



혹시 Win32 API 사용에 어려움이 있으시다면, PInvoke.net 사이트를 참조하세요. .NET에서 Win32 API 를 사용할 때, 많은 도움을 줄 수 있는 사이트입니다.



저는 위의 ProcessTool .TrimAllProcessMemory()  를 CPU 활용률이 5% 미만일 경우 1시간마다 실행하게끔 웹 어플리케이션에서 Background Service로 돌립니다. 그럼 운영자는 메모리를 별로 사용하지 않는데도 잘 돌아간다고 좋아합니다^^

2012년 1월 23일 월요일

Resharper 가 너무 느려요.

Resharper 가 상당히 좋은 개발 툴임에도 불구하고, 많은 닷넷 개발자에게는 그림이 떡 또는 가까이 하기엔 먼 당신이 되는 건 왜일까요?
우선 전 Resharper 를 4.x 때부터 사용해 왔으며, Resharper 없이는 개발을 못할 정도로 의존을 많이 하고 있습니다. 그만큼 저는 Resharper를 사용하는데 문제가 없습니다.
자 그럼 일반적으로 Resharper 가 느려서 쓰지 못하겠다고 하는 상황과 제의 개발 상황을 비교해 보시면 답이 어느 정도 나옵니다.
저는 다음과 같은 환경에서 Resharper를 씁니다.
  1. Class Library 개발을 주로 한다.
  2. 한 솔루션에 80여개의 프로젝트가 있다. (물론 Class 수에 따라 많이 다르다) (여기에는 Sample용 웹 Application 도 있다)
  3. Temp 폴더를 물리적으로 다른 폴더에 둔고, Resharper 파일들을 거기에 둔다.
  4. 메모리는 예전에는 4GB, 지금은 8GB를 사용, HDD 를 SSD를 사용한다.
자 이런 환경인데도, Visual Studio .NET 2010은 태생적으로 무거운데, 거기다가 확장 프로그램도 깔고, Resharper 도 구동하게 되면 상당히 부하가 걸립니다.
특히 코드 페이지에서 부하가 많이 걸리는 데, 이는 대부분 Intellisense 때문이다. 여기다가 Visual Studio 확장 툴 중에 Productivity Power Tool 기능을 같이 쓰게 되면, 더욱 느려지게 된다. (우선 이 확장 프로그램의 기능을 최소화 한 다음에 써보기 바란다.)
그 다음으로 아래 그림처럼 Resharper 의 Intellisense 기능 중에 C#, Javascript 만 두고 나머지는 제외 한다.
Resharper_Interllisense
HTML, CSS 등은 이미지 등의 리소스 들을 경로를 읽어드리고, 캐시해야 하기 때문에 속도가 느려지는 것입니다.
웹 개발자의 경우는 이와 같이 HTML, CSS 등의 Intellisense는 포기하고 해보시기 바랍니다. 쩝…
예전 버전에서는 웹 Application의 경우 이미지, CSS, Javascript 등의 리소스들의 경로 및 캐싱을 수행하는데, 프로젝트에서 제외하는 방법을 사용했는데, 위의 그림처럼 Intellisense에서 분야별로 선택해서 사용하면 좀 더 쾌적한 화경에서 Resharper를 사용하실 수 있을 것입니다.

2012년 1월 22일 일요일

Facebook에 NHibernate Group을 만들었습니다.

안녕하세요^^ 블로그로도 NHibernate 정보를 제공하기도 했는데, Facebook에 Group 기능이 있더군요. 이를 이용해서 Facebook에 NHibernate 그룹을 만들었습니다.

http://www.facebook.com/#!/groups/nhibernate.korea/

제가 관리하고 있고요, 신청하시는 분들은 모두 가입되도록 하겠습니다. 관심 있으신 분은 참여해주기 바랍니다.

한가지 더 – Google+ 에도 NHibernate Korea 서클을 만들었습니다.
둘 중 어떤 서비스가 더 좋을 지 몰라 우선 둘 다 만들어서 써보면서, 좋은 쪽으로 단일화해야 할 것 같습니다.