Skip to content

Architecture

Sangyong Bae edited this page Aug 7, 2026 · 1 revision

구조

설계의 출발점은 하나다 — "메모리를 아끼는 도구가 자기 메모리를 많이 쓰면 목적이 뒤집힌다." 그래서 판단 기준을 성능·동작 안정성 > 개발·배포 편의성으로 못 박고, 그 아래에서 선택을 정리했다.

스택

Rust + Win32 직접 호출. 관리 런타임(GC)도, UI 프레임워크도, 외부 crate도 쓰지 않는다 (Win32 바인딩 제외). 화면은 GDI로 직접 그린다.

후보 왜 안 골랐나
C# / .NET 런타임 상주만으로 예산(5MB)을 넘긴다. GC가 유휴 CPU를 흔든다
Electron 등 논외 — 수백 MB
C++ 성능은 동등. 메모리 안전성과 도구 체인 때문에 Rust를 택했다. 시스템 콜 중심이라 Rust의 오버헤드는 사실상 0

실측으로 확인한다: 프로세스 500개 열거 + Top 5 선정에 8~10ms, 상주 유휴 CPU ~0%.


하이브리드 — 상주 셸 + 온디맨드 워커

NexaMemKeeper.exe            (상주 셸 — 트레이·폴링·Top 5 팝업)
  └─ NexaMemKeeper.exe --worker <작업>   (필요할 때만 뜨는 별도 프로세스)
       clean / trim / kill / killtree / killthread / list / settings

**핵심은 "무거운 코드는 상주하지 않는다"**는 것이다.

  • 프로세스 목록 창, 정리 엔진, 설정 창은 전부 별도 프로세스에서 돈다.
  • 창을 닫으면 그 프로세스가 끝나므로, 사용한 메모리를 OS가 100% 회수한다. 같은 프로세스 안에서 창만 닫는 방식은 힙 조각이 남아 상주 크기가 계속 커진다.
  • 워커가 크래시해도 트레이 셸은 살아 있다 — 미문서 NT API를 다루는 코드가 상주부와 격리된다.
  • 상태 변경(정리·종료)은 워커만 한다. 셸은 결과 코드를 받아 알림만 띄운다(IPC를 최소로).

목록 창처럼 오래 걸릴 수 있는 작업(트리 종료 등)은 워커 안에서 다시 백그라운드 스레드로 내린다. UI 스레드가 멈추면 "응답 없음"으로 보이기 때문이다.


예산 게이트

숫자가 문서에만 있으면 지켜지지 않는다. 그래서 빌드 파이프라인이 검사한다.

항목 기준 0.6.0 실측
상주 유휴 메모리 ≤ 5 MB 1.30 MB
실행 파일 ≤ 3 MB 0.64 MB
외부 DLL 의존 0 0 (OS 기본 + API 세트만)
유휴 CPU ~0% ~0%

포장 스크립트가 exe 크기와 임포트 DLL 목록을 검사하고, 기준을 넘으면 포장하지 않고 중단한다. GitHub Actions도 같은 스크립트를 돌린다 — 로컬과 CI의 기준이 갈라지지 않게.


관찰과 회수

  • 열거: NtQuerySystemInformation(SystemProcessInformation) 한 번의 호출로 전체 프로세스와 쓰레드를 받는다. 프로세스마다 핸들을 여는 방식보다 훨씬 싸다.
  • 사용률: GlobalMemoryStatusEx.
  • 회수: K32EmptyWorkingSet(①) + NtSetSystemInformation(②~⑥). 자세히는 정리 영역.
  • 표시 이름: 실행 파일 버전 리소스의 FileDescription — 프로세스당 한 번만 읽고 캐시한다.

권한

기본은 asInvoker(일반 권한). 필요할 때만 트레이 메뉴로 승격 재시작한다. 승격 시 SeDebugPrivilege·SeProfileSingleProcessPrivilege·SeIncreaseQuotaPrivilege를 활성화한다.

권한이 없어 건너뛴 영역은 알림에 명시한다. 조용한 성공은 사용자를 속이는 것과 같다.


설정 저장

포터블 우선 — exe 옆 data\settings.ini. 그 위치에 쓸 수 없으면 %LOCALAPPDATA%로 폴백한다. 레지스트리를 쓰는 곳은 자동 실행 값 하나뿐이며(HKCU\...\Run), 사용자가 메뉴로 켤 때만 생기고 끄면 지워진다.


더 깊은 배경(결정 기록·ADR·측정 방법)은 저장소의 docs/에 정리되어 있다.

Clone this wiki locally