2017年3月21日火曜日

メモリ解放のタイミングと解放量のデータ

プログラムで使用するメモリはその他のプログラムなどと共有していて、そのメモリを管理するシステムが状況に応じて色々と処理するので、自身のメモリ使用量を確認するプログラムを作成しても結果がいつも同じということはなく、バラつきがあります。

複数の条件の試験結果を比較したい場合はそのバラツキも考慮しなければいけませんので、どの程度ばらつくのか確認しました。
その過程でHTAでのメモリ解放のタイミングと解放される量が少し分かりましたので試験データと併せて記録・公開します。

試験用ソース
<html>
<title>試験</title>
<body>
<table id=tbl border=1><tr><td></td><td></td></tr></table>
<input id=inp0>
<input id=inp1>
<input id=inp2>
<input id=inp3>
<div id=div></div>
</body>
<script>
resizeTo(400,300)
onload=function(){
var 試験回数=10000, バラツキ確認回数=100, arrS=[], arrE=[], arr解放後=[]
試験(バラツキ確認回数,function(){
arrS.push(getメモリ使用量())
for(var i=0; i<試験回数 ;i++){div.insertBefore(document.createElement('div'))}
arrE.push(getメモリ使用量())
div.innerText = ''
arr解放後.push(getメモリ使用量())
},
function(){ },
function(){ },
function(){
var arr差ES=[], arr差E解=[], sumES=0, sumE解=0
for(var i=0,L=arrS.length; i<L ;i++){
sumES += arr差ES [i] = arrE[i] - arrS[i]
sumE解 += arr差E解[i] = arrE[i] - arr解放後[i]
}
var obj={
試験回数:試験回数,
バラツキ確認回数:バラツキ確認回数,
'arrE[n]-arrS[n].max':Math.max.apply(null,arr差ES),
'arrE[n]-arrS[n].min':Math.min.apply(null,arr差ES),
'arrE[n]-arrS[n].average':(sumES/L),
'arrE[n]-arr解[n].max':Math.max.apply(null,arr差E解),
'arrE[n]-arr解[n].min':Math.min.apply(null,arr差E解),
'arrE[n]-arr解[n].average':(sumE解/L)
}
var tbo=tbl.firstChild, tr=tbo.firstChild.removeNode(true), tds, newTR
for(name in obj){
tbo.insertBefore(newTR=tr.cloneNode(true))
tds = newTR.childNodes
tds[0].innerText = name
tds[1].innerText = obj[name]
}
inp0.value = arrS
inp1.value = arrE
inp2.value = arr解放後
inp3.value = ''
setInterval(function(){
inp3.value += getメモリ使用量()+','
},10)
})
}
// document.body.onclick=function(){ document.title = getメモリ使用量() }
試験=function(バラツキ確認回数, fun試験, fun試験前, fun試験後, fun全行程終了後){
var 実施回数=0
setTimeout(function(){
setTimeout(function(){ fun試験前() },0)
setTimeout(function(){ fun試験 () },0)
setTimeout(function(){ fun試験後() },0)
実施回数++
if(実施回数<バラツキ確認回数){setTimeout(arguments.callee, 0)}else{ fun全行程終了後() }
},0)
}
getメモリ使用量=function(){
var res = new Enumerator(GetObject("winmgmts:root\\CIMV2").ExecQuery("SELECT * FROM Win32_Process where ProcessID="+getPID()))
return res.atEnd() ? (void 0) : res.item().PageFileUsage
}
getPID=function(){
var f=function(){
// 子プロセスをcscriptにすると黒い画面が一瞬表示されてしまう。
// 「//B」オプションを付けないとwscriptの設定画面が表示されてしまう。
var childPID = (new ActiveXObject('WScript.Shell')).Exec('wscript //B').ProcessID
// 子の親(自身)のPIDを特定する
var res = new Enumerator(GetObject("winmgmts:root\\CIMV2").ExecQuery("SELECT * FROM Win32_Process where ProcessID="+childPID))
return res.atEnd() ? (void 0) : res.item().ParentProcessID
}
// 1度目で取得に成功すればその値を返し、ダメなら2回目の処理を実行する。
return f() || f()
}
gt=function(){return (new Date()).getTime()}
loop試験 = function(回数, fun){
for(var s=gt(),i=0;i<回数;i++){ fun() }
return gt() - s
}
</script>
</html>



試験後の画面


以下は試験=function(){}にして実行した結果。





グラフ




以下は試験=function(){}にして実行した結果。




試験用ソースを見てお分かりになるかもしれませんが、メモリ使用量を取得するタイミングやinnerText = ''を実行するタイミングを変えて何度か試してみましたが、結果は同様でした。
setTimeoutによってfunctionを細切れに実行しても、予約済みfunctionがある場合はメモリ解放は先送りされるようです。一方で、実行中のfunctionがあっても周期的に少量の解放は行われるようです。
大規模な解放はビジー状態が解除されてから1秒以内ぐらいに実行されるようです。

0 件のコメント:

コメントを投稿