動機
こんなポンコツ PC でプログラミングしています。VSCode とブラウザ開いただけでこの有様だよ!
VSCode にはメモリを一杯使ってサクサク動いて欲しいので、「Chrome オメーに喰わせるメモリはねぇ!」ということを実践してみたいと思います。Rust で。(いつもは Firefox 使ってますが、Chrome の方が受けがいいかなと思って Chrome にしてます)
結果
先に結果です。
↓減らす前
↓減らした後
わーい、Chrome のメモリ使用量減ったー。
どうやるか
EmptyWorkingSet 関数を呼んでるだけです。おわり。
解説
ワーキングセットとは?
ワーキングセットとは、物理メモリー領域の中でも、アプリケーションが現在使用している状態の領域のことである。言い換えれば、アプリケーションが使用しているメモリーでもあまり使用されていないデータをOSがハードディスクに退避(スワップ)させる際に、退避の対象とならずに物理メモリー内に留められる部分のこと。
なのでワーキングセットを空にすることはアプリケーションのパフォーマンスに重大な影響を及ぼします。HDD のころはEmptyWorkingSet
関数を呼ぶと、カリカリ言って実用に耐えないレベルでした。最近の比較的高速なストレージならあまり気にならないかもしれません。
コード
ここでの肝は main.rs の中のimpl Drop for Handle
です。Drop
を実装した Rust の構造体にHANDLE
を束縛することで、スコープを抜けたら自動的にリソースを開放するようにしています。
[package]
name = "chromempty"
version = "0.1.0"
authors = ["benki"]
edition = "2018"
[dependencies.winapi]
version = "0.3"
features = ["tlhelp32", "handleapi", "processthreadsapi", "psapi"]
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_char;
use std::thread;
use std::time::Duration;
use winapi::{
shared::minwindef::{FALSE, TRUE},
um::{
handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
processthreadsapi::OpenProcess,
psapi::EmptyWorkingSet,
tlhelp32::{
CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32,
TH32CS_SNAPPROCESS,
},
winnt::{HANDLE, PROCESS_ALL_ACCESS},
},
};
struct Handle(HANDLE);
impl Drop for Handle {
fn drop(&mut self) {
unsafe { CloseHandle(self.0); }
}
}
fn main() {
loop {
chromempty();
thread::sleep(Duration::from_secs(5));
}
}
fn chromempty() {
unsafe {
let mut entry = mem::MaybeUninit::<PROCESSENTRY32>::uninit().assume_init();
entry.dwSize = mem::size_of::<PROCESSENTRY32>() as u32;
let snap_shot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snap_shot == INVALID_HANDLE_VALUE {
return;
}
// SNAPSHOT HANDLE を Rust の構造体に束縛
let snap_shot = Handle(snap_shot);
if Process32First(snap_shot.0, &mut entry) == TRUE {
while Process32Next(snap_shot.0, &mut entry) == TRUE {
if let Ok(exe) = CStr::from_ptr(&entry.szExeFile as *const c_char).to_str() {
if exe.eq("chrome.exe") {
let handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
if handle.is_null() {
continue;
}
// PROCESS HANDLE を束縛
let handle = Handle(handle);
EmptyWorkingSet(handle.0);
// PROCESS HANDLE はここで開放される
}
}
}
}
// SNAPSHOT HANDLE はここで開放される
}
}
まとめ
-
EmptyWorkingSet
関数を知ったよ - ワーキングセットとは何かを知ったよ
-
Drop
による自動的なリソース開放を知ったよ - ポンコツ PC つらい