~のためベンチマーク、次のコマンドを実行します。
for i in {1..100000000}; do
echo "$i" line >> file
done
吹く中かっこ拡張そしてリストを1 2 3 4 5 6 ... 100000000
メモリに保存します。
私の考えでは、これはいつか何とか公開されると思います。最終的には一時変数です。数日が経過しましたが、bash
プロセスはまだ17.9GBのメモリを占めていました。
bashでこれらの一時変数を強制的に消去できますか?unset
変数名がわからないため使用できません。 (明らかにunset i
役に立たない)
もちろん、1つの解決策は、シェルを閉じて新しいシェルを開くことです。
私はbashメーリングリストにこの質問を投稿するのに役立つ答えを受けました。チェットラムジー:
これはメモリリークではありません。 Mallocの実装では、メモリをカーネルに再リリースする必要はありません。 bash malloc(およびその他)は限られた状況でのみそうします。 mmap または sbrk を使用してカーネルから取得したメモリと malloc を介してキャッシュに保存されたメモリは、リークを構成しません。リークは、アプリケーションまたは malloc 自体がこれ以上処理できないメモリです。
malloc() はデフォルトでアプリケーションとカーネル間のキャッシュです。カーネルにメモリを返すタイミングと方法を決定します。
答え1
だから私はテストでこれを行いましたが、メモリを大量に消費しました。また、意図的に小さい数字を使用しました。bash
これらのリソースを数日間まとめておくと、やや面倒なことがあると思います。
ps -Fp "$$"; : {1..10000000}; ps -Fp "$$"
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
mikeserv 32601 4241 0 3957 3756 4 08:28 pts/1 00:00:00 bash -l
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
mikeserv 32601 4241 59 472722 1878712 4 08:28 pts/1 00:00:28 bash -l
ご覧のとおり、これはプロセスが消費するリソースに大きな影響を与えます。まあ、私はこれを明確にしようとします。しかし、私が知っている限り、シェルプロセスを別のシェルプロセスに置き換える必要があります。
まず、表示するためにマーカー変数を設定します。注:これはexport
編集内容ではありません。
var='just
testing
'\''
this stuff
'\'''
これは、$0
長期実行デーモンが状態を更新するために時々実行する必要がある作業と同じです。これは意味があります。
最初の方法:ここに文書化
現在、シェルを使用して、新しく編集されたシェルプロセスのHeredoc入力ファイル記述子を作成します。exec
これには、現在シェルで宣言されているすべての変数が含まれます。多分違うかもしれませんがわかりませんbash
。
新しいシェルは、-l
oginスイッチ(ファイルが通常どおりインポートされることを保証profile/rc
)と、現在の特別なシェルパラメータに設定および保存されている他のシェルオプションを使用して呼び出されます$-
。-l
oginが正しいアプローチではないと思われる場合は、-i
スイッチを使用して少なくともrc
ファイルを実行する必要があります。
exec "${0#-}" "-l$-" 3<<ENV
$(set)
ENV
いいです、たった1秒しかかかりませんでした。効果はどうですか?
. /dev/fd/3 2>/dev/null
echo "$var"; ps -Fp "$$"
just
testing
'
this stuff
'
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
mikeserv 32601 4241 12 4054 3800 5 08:28 pts/1 00:00:29 bash -lhimBH
見えるように大丈夫です。<&3
読み込まれるまで、新しいシェルプロセスの入力を待ちます。だからそうして.
インポートします。おそらく、ファイルなどを介して新しいシェルに設定されたいくつかのデフォルトの読み取り専用変数が含まれているため、rc
いくつかのエラーがあります2>/dev/null
。ただし、これを行った後は、ご覧のとおり、すべての変数の変数があります。古いシェルプロセス - 私のフラグが含まれています$var
。
2番目の方法:環境変数
この問題についてGoogleを1〜2回検索した後、これが考慮する価値のある別のアプローチになる可能性があると思いました。最初はこう思ったけど(明らかに間違っていた)単一の環境変数の値にカーネル強制ランダムな長さ制限があるという信念に基づいて、このオプションは考慮されません。アグレンまたはライアンマックス (これが影響を与える可能性があります)ただし、単一値の場合は小さいです。しかし、私の正しい声明は次のとおりです。execve
環境全体が大きすぎると、呼び出しは機能しません。したがって、このアプローチは、現在の環境が呼び出しを許可するのに十分小さいことを保証できる場合にのみ優先されるべきであると思いますexec
。
実際、やり直すのに十分違いがあります。
ps -pF "$$"; : {1..10000000}; ps -pF "$$"
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
mikeserv 26296 4241 0 3957 3788 3 14:28 pts/1 00:00:00 bash -l
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
mikeserv 26296 4241 38 472722 1878740 3 14:28 pts/1 00:00:11 bash -l
最初のラウンドで私が失敗したことの1つは、シェル機能を移行することでした。直接追跡することは含まれません。(おそらくこれが最善の方法でしょう)、私が知っている限り、これを行うことができるシェル移植可能な方法はありません。bash
ただし、この機能は移植可能なシェル変数とほぼ同じ方法で機能するため、実際には許可されますdeclare -f
。最初の方法を使用してこれを行うには、set
ここに; declare -f
追加します。set
私のマーカー変数は同じままですが、私のマーカー機能は次のとおりです。
chk () {
printf '###%s:###\n%s\n' \
\$VAR "${var-NOT SET}" \
PSINFO "$(ps -Fp $$)" \
ENV\ LEN "$(env | wc -c)"
}
したがって、新しいシェルにファイル記述子を提供するのではなく、2つの環境変数に渡します。
varstate=$(set) fnstate=$(declare -f) exec "${0#-}" "-l$-"
わかりました、実行中のシェルを変更しました。今どうなりますか?
chk
bash: chk: command not found
確かに。しかし...
{ echo '###EVAL/UNSET $FNSTATE###'
eval "$fnstate"; unset fnstate
chk
echo '###EVAL/UNSET $VARSTATE###'
eval "$varstate"; unset varstate
chk
}
出力
###EVAL/UNSET $FNSTATE###
###$VAR:###
NOT SET
###PSINFO:###
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
mikeserv 26296 4241 10 3991 3736 1 14:28 pts/1 00:00:12 bash -lhimBH
###ENV LEN:###
6813
###EVAL/UNSET $VARSTATE###
bash: BASHOPTS: readonly variable
bash: BASH_VERSINFO: readonly variable
bash: EUID: readonly variable
bash: PPID: readonly variable
bash: SHELLOPTS: readonly variable
bash: UID: readonly variable
###$VAR:###
just
testing
'
this stuff
'
###PSINFO:###
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
mikeserv 26296 4241 10 4056 3772 1 14:28 pts/1 00:00:12 bash -lhimBH
###ENV LEN:###
2839
答え2
習慣。 Bashは、何らかの目的で割り当てられたメモリをオペレーティングシステムに返しません。 (しかし、私が間違っている場合は訂正してください。)
ただし、bashは必要に応じて他の目的でメモリを再利用し、そうでない場合はカーネルはメモリを交換して実際にRAMに入らないようにします。