レック・テクノロジー・コンサルティング株式会社TECH BLOG

大容量メモリに潜むメモリ枯渇問題その3 ~Linux HugePageの薦め~

こんにちは。
前回ページテーブルページテーブルエントリ(以下、PTE)の概要を説明しましたが、
今回は『大容量メモリに潜むメモリ枯渇問題』にPTEがどのように関与するのかを説明します。

なぜ、大容量のメモリを搭載するとPTEがメモリを過剰消費するのか?

 
いくら512エントリー分のメモリ領域をカバーできた(詳細は前回を参照)といっても、
今日のような巨大な物理メモリを使用する場合においても適切な設定なのでしょうか。

PTEをもう少し掘り下げて確認してみましょう。

まず、PTEで使用しているメモリサイズの確認です。

$ grep PageTables /proc/meminfo
PageTables: 6468 kB

次にプロセスの使用サイズを確認します。
これはPTEがプロセス毎に割り当てられるためです。

$ grep PTE /proc/<pid>/status
VmPTE: 36 kB

更に、PTEの1エントリーにおけるサイズを確認してみましょう。
これはカーネルのソースコードから確認しますが、「/proc/<pid>/status」を読み込んだときに実行される関数からコードウォークします。

■PTEの算出ロジック

<kernel 2.6.13を使用>
####################################################################################
[proc/pid/status 読み込み時の実行処理]
311 int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
312 struct pid *pid, struct task_struct *task)
313 {
314 struct mm_struct *mm = get_task_mm(task); ←★mm_structはメモリ管理構造体(メモリディスクリプタ)
315
316 task_name(m, task);
317 task_state(m, ns, pid, task);
318
319 if (mm) {
320 task_mem(m, mm); ←★statusファイル内容の出力関数
321 mmput(mm);
322 }
323 task_sig(m, task);
324 task_cap(m, task);
325 cpuset_task_status_allowed(m, task);
326 #if defined(CONFIG_S390)
327 task_show_regs(m, task);
328 #endif
329 task_context_switch_counts(m, task);
330 return 0;
331

[task_mem 関数]
17 void task_mem(struct seq_file *m, struct mm_struct *mm)
18 {
~~~~
56 mm->stack_vm << (PAGE_SHIFT-10), text, lib,
57 (PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10); ←★PTEの算出部分。
※PTRS_PER_PTEマクロ×PTEページエントリー構造体のサイズ × ページ数 となります。

[PTRS_PER_PTEマクロ]
#define PTRS_PER_PTE 512 ←★9bitアドレス(表現領域512バイト)

[pte_t]
85 typedef struct { pteval_t pte; } pte_t ←★pte_tはpteval_t(ページエントリー構造体)へのポインタsizeof=8バイト

[MM構造体]
mm_struct構造体は、task_struct構造体のメンバーからポイントされているプロセスのメモリ状態に関する情報を格納している構造体。

struct mm_struct {
174 struct vm_area_struct * mmap; /* list of VMAs */
175 struct rb_root mm_rb;

~~~~

202
203 unsigned long hiwater_rss; /* High-watermark of RSS usage */
204 unsigned long hiwater_vm; /* High-water virtual memory usage */
205
206 unsigned long total_vm, locked_vm, shared_vm, exec_vm;
207 unsigned long stack_vm, reserved_vm, def_flags, nr_ptes; ←★一番右がページテーブルエントリー数
208 unsigned long start_code, end_code, start_data, end_data;

~~~~

259 };
260
####################################################################################

結果、PTEの算出は以下の式であることがわかりました。

PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10

仮に、1GBのメモリ領域をカバーするために必要なPTEサイズは、

1024M/512=2M

ということになります。
つまり、マップするメモリサイズの1/512が必要となります。

共有メモリの扱い ~SGAとPTEの関係~

 
共有メモリ(IPC Shared Memory)はプロセスの占有領域ではないため、サーバプロセスのメモリサイズを確認する際にプロセスがアタッチしているメモリサイズから引いて算出していることがあります。
 
これは確かにそうなのですが、PTEについては、共有メモリ上のマップ情報であってもプロセス毎に確保されるため注意が必要です。
 
OracleDatabaseでいうとSGAは共有メモリを使用しているため、SGAサイズに応じてサーバプロセスのPTEサイズが変動します。

例えば、
SGA=10GBで、専用サーバセッションがSGA領域の大半をマップするような場合、
PTEだけで1セッションで20MBも消費することになります。
500セッションで10GBです。

今日数百セッションという接続数については特に珍しくないことだと思います。
さらに、接続プーリングによる長時間に亘る接続時間によって広範の共有メモリをアタッチすることも十分に考えられることです。

これではすぐにメモリ領域の枯渇が発生してしまいます。

次回は、共有メモリとPTEに関する検証を行いながらご説明します。

この記事をシェアする

  • Facebook
  • X
  • Pocket
  • Line
  • Hatena
  • Linkedin

資料請求・お問い合わせはこちら

ページトップへ戻る