記事を書いた理由
Oracle PGXでOutOfMemoryErrorが発生した際、対処に土日を費やしてしまったため、対処法を備忘録として残しておきます。
発生したエラー
DeepWalk、Pg2vecというアルゴリズムを動かそうとしている時、以下のエラーが発生しました。
pgx> graph=session.readGraphWithProperties("NCI109.json").undirect() #Load Data
==> PgxGraph[name=sub-graph_1,N=122494,E=265202,created=1567303434267]
pgx> model=analyst.deepWalkModelBuilder()\ #Create Model
..1> .setWindowSize(3)\
..2> .setWalksPerVertex(6)\
..3> .setWalkLength(4)\
..4> .build()
==> oracle.pgx.api.beta.mllib.DeepWalkModel@2abbd0d9
pgx> model.fit(graph) #Training
ERROR: java.lang.OutOfMemoryError: Cannot allocate new FloatPointer(24403600): totalBytes = 338M, physicalBytes = 955M
pgx> graph=session.readGraphWithProperties("NCI109.json").undirect() #Load Data
==> PgxGraph[name=sub-graph_1,N=122494,E=265202,created=1567303434267]
pgx> model = analyst.pg2vecModelBuilder()\ #Training
..1> .setGraphLetIdPropertyName("graph_id")\
..2> .setVertexPropertyNames(Arrays.asList("node_label"))\
..3> .setWindowSize(4)\
..4> .setWalksPerVertex(5)\
..5> .setWalkLength(8)\
..6> .build()
==> oracle.pgx.api.beta.mllib.Pg2vecModel@5a936e64
pgx> model.fit(graph) #Training
ERROR: java.lang.OutOfMemoryError: Java heap space
PGXのメモリ構造
PGXのメモリには、大きく分けて2種類のメモリが存在します。グラフのトポロジーを読み込むメモリとノードとエッジのプロパティーを読み込むメモリの2つです。
グラフのトポロジーを読み込むメモリ
ここには、グラフのトポロジー(プロパティーを考慮せず、各ノードと各エッジがどのように繋がっているかを表す情報)が読み込まれます。
PGXでは、グラフのトポロジーを格納するために、メモリの消費量を最小限に抑え、読み込み速度を最速化できる**圧縮行格納方式(CSR : Compressed Sparse Row)**を採用しています。
圧縮行格納方式に関しては、Wikipediaの解説がわかりやすいので、そちらをご参照ください。
プロパティーを読み込むメモリ
ここには、各ノードと各エッジのプロパティーが読み込まれます。
プロパティのメモリ消費量
Property Type | Size in Bytes |
---|---|
int | 4 |
float | 4 |
long | 8 |
double | 8 |
boolean | 1 |
date | 8 |
local date | 4 |
time | 4 |
timestamp | 8 |
time with timezone | 8 |
timestamp with timezone | 12 |
point 2d | 16 |
string | variable |
Documentより引用 |
考えられる原因
OutOfMemoryErrorの原因として考えられるものは、以下の3つです。
- On-heap領域が足りなくなったにも関わらず、PGXがメモリを獲得しようとしている
- Off-heap領域が足りなくなったにも関わらず、PGXがメモリを獲得しようとしている
- PGXで使用できるメモリ領域が少ない(OS上で設定されている)
これらは、ログを見ることで解析することができます。
PGXで詳細なログを見る場合は、以下のコマンドを実行します。
pgx> :ll root debug
==> log level of root logger set to DEBUG
その他にも、様々なログレベルを設定することができます。詳細は、Documentを参照してください。
対処法:On-heap領域が足りなくなった場合
On-heap領域が足りなくなった場合は、単純にPGXに割り当てるヒープ領域を増やします。
ローカルでJavaアプリケーションとしてPGXを実行している場合
アプリケーション起動時に、以下のオプションを設定します。
例)ヒープ領域の初期サイズを1024MB、最大サイズを2048MBに設定する
java -Xms1024m -Xmx2048m <メインクラス名>
PGXシェルでPGXを実行している場合
環境変数JAVA_OPTS
を設定してから、再度PGXを起動します。
例)ヒープ領域の初期サイズを1024MB、最大サイズを2048MBに設定する
export JAVA_OPTS="-Xms1024m -Xmx2048m"
echo $JAVA_OPTS
-Xms1024m -Xmx2048m
sh $PGX_HOME/bin/pgx
対処法:Off-heap領域が足りなくなった場合
pgx.conf
内のmax_off_heap_size
を確認します。
(max_off_heap_size
は、新たなメモリ領域の獲得をしなくなるための、単なる閾値です。もともと設定されていても、設定されていた値以上にメモリが獲得されることもあります。)
vi $PGX_HOME/conf/pgx.conf
{
"allow_idle_timeout_overwrite": true,
"allow_local_filesystem": false,
"allow_task_timeout_overwrite": true,
"enable_gm_compiler": true,
"max_off_heap_size":1024, #これを無くしてみる
"enterprise_scheduler_config": {
"analysis_task_config": {
"priority": "MEDIUM",
"weight": "<no-of-CPUs>",
"max_threads": "<no-of-CPUs>"
},
"fast_analysis_task_config": {
"priority": "HIGH",
"weight": 1,
"max_threads": "<no-of-CPUs>"
},
"num_io_threads_per_task": "<no-of-CPUs>"
},
"graphs": [],
"max_active_sessions": 1024,
"max_queue_size_per_session": -1,
"max_snapshot_count": 0,
"memory_cleanup_interval": 600,
"path_to_gm_compiler": null,
"release_memory_threshold": 0.85,
"session_idle_timeout_secs": 0,
"session_task_timeout_secs": 0,
"strict_mode": true,
"tmp_dir": "tmp_data",
"in_place_update_consistency_model" : "ALLOW_INCONSISTENCIES"
}
対処法:PGXで使用できるメモリ領域が少ない場合
上記2つの方法を試してもうまくいかない場合は、PGXで使用できるメモリ領域が少ないことを疑いましょう。
(今回は、この方法を試してうまくいくようになりました。)
その場合は、OS上でメモリ割り当てを増やしてあげます。
私はDockerを使用してPGXを動かしているのですが、どうも割り当てられているメモリ容量が小さいように感じました。
今までは見逃していたのですが、PGX起動時に、割り当てられたメモリの総量を確認すると、1999MB
しか割り当てられていないことがわかりました。
Kohei:NCI109 kohei$ pgx
tput: unknown terminfo capability 'setafsd'
[WARNING] PGX shell is using Groovy installed in /opt/groovy-2.5.8. It is the responsibility of the user to use the latest version of Groovy and to protect against known Groovy vulnerabilities.
[WARNING] The Groovy-based PGX shell will be removed and replaced by a jshell-based PGX shell.
Sep 01, 2019 1:56:34 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
01:56:36,187 INFO Ctrl$1 - >>> start engine
01:56:37,070 INFO StartupLogging - >>> PGX sm engine 19.2.1/ API 3.4.1 running.
01:56:37,071 INFO StartupLogging - >>> PGQL version 1.2
01:56:37,072 INFO StartupLogging - >>> Built from commit fdc975ef0f19828115fd76ed19ca79c45af52d2e
01:56:37,072 INFO StartupLogging - >>> Built at 2019-06-27T09:38:59.982-07:00
01:56:37,073 INFO StartupLogging - >>> JDK version: 1.8.0_212
01:56:37,073 INFO StartupLogging - >>> Operating system: Linux
01:56:37,074 INFO StartupLogging - >>> Number of processors: 2
01:56:37,076 INFO StartupLogging - >>> Total system memory: 1999 MB #これを増やしたい!
PGX Shell 19.2.1
PGX server version: 19.2.1 type: SM running in embedded mode.
PGX server API version: 3.4.1
PGQL version: 1.2
type :help for available commands
variables instance, session and analyst ready to use
pgx>
Dockerのメモリを増やす
ということで、Dockerが使用できるメモリを増やします。
- クジラのマークをクリックします(充電しろよ、というツッコミは無視します)。

- Preferencesをクリックします。

- Advancedタブを選択し、割り当てたいメモリ量を調整します。

- Apply & Restartをクリックし、Dockerが再起動するのを待ちます。

- Dockerが再起動したら、再度PGXを起動します。
kohei$ pgx
tput: unknown terminfo capability 'setafsd'
[WARNING] PGX shell is using Groovy installed in /opt/groovy-2.5.8. It is the responsibility of the user to use the latest version of Groovy and to protect against known Groovy vulnerabilities.
[WARNING] The Groovy-based PGX shell will be removed and replaced by a jshell-based PGX shell.
Sep 01, 2019 7:58:52 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
07:58:55,010 INFO Ctrl$1 - >>> start engine
07:58:56,168 INFO StartupLogging - >>> PGX sm engine 19.2.1/ API 3.4.1 running.
07:58:56,173 INFO StartupLogging - >>> PGQL version 1.2
07:58:56,173 INFO StartupLogging - >>> Built from commit fdc975ef0f19828115fd76ed19ca79c45af52d2e
07:58:56,174 INFO StartupLogging - >>> Built at 2019-06-27T09:38:59.982-07:00
07:58:56,178 INFO StartupLogging - >>> JDK version: 1.8.0_212
07:58:56,178 INFO StartupLogging - >>> Operating system: Linux
07:58:56,180 INFO StartupLogging - >>> Number of processors: 2
07:58:56,185 INFO StartupLogging - >>> Total system memory: 5453 MB #1999MBから増加した!
PGX Shell 19.2.1
PGX server version: 19.2.1 type: SM running in embedded mode.
PGX server API version: 3.4.1
PGQL version: 1.2
type :help for available commands
variables instance, session and analyst ready to use
pgx>
メモリ容量が増えました!早速実行します!
DeepWalk
pgx> graph=session.readGraphWithProperties("NCI109.json").undirect()
==> PgxGraph[name=sub-graph_1,N=122494,E=265202,created=1567324836478]
pgx> model=analyst.deepWalkModelBuilder()\
..1> .setWindowSize(3)\
..2> .setWalksPerVertex(6)\
..3>
..3> .setWalkLength(4)\
..4> .build()
==> null
pgx> model.getLoss() #学習後の損失を算出する
==> -2.85346752548799
pgx> sim=model.computeSimilars(111,3) #ID111のノードと似ているノードを3つ見つける
==> oracle.pgx.api.beta.frames.internal.PgxFrameImpl@bf75b5c
pgx> sim.print()
+--------------------------------+
| dstVertex | similarity |
+--------------------------------+
| 111 | 1.0000001192092896 |
| 85 | 0.9343633055686951 |
| 81 | 0.8769448399543762 |
+--------------------------------+
==> oracle.pgx.api.beta.frames.internal.PgxFrameImpl@bf75b5c
Pg2vec
pgx> graph=session.readGraphWithProperties("NCI109.json").undirect()
==> PgxGraph[name=sub-graph_1,N=122494,E=265202,created=1567325544948]
pgx> model = analyst.pg2vecModelBuilder()\
..1> .setGraphLetIdPropertyName("graph_id")\
..2> .setVertexPropertyNames(Arrays.asList("node_label"))\
..3> .setWindowSize(4)\
..4> .setWalksPerVertex(5)\
..5> .setWalkLength(8)\
..6> .build()
==> oracle.pgx.api.beta.mllib.Pg2vecModel@1727e03a
pgx> model.fit(graph)
==> null
pgx> similars = model.computeSimilars(52, 10) #ID52のグラフレットと似ているグラフレットを10個見つける
==> oracle.pgx.api.beta.frames.internal.PgxFrameImpl@78d71df1
pgx> similars.print()
+----------------------------------+
| dstGraphlet | similarity |
+----------------------------------+
| 52 | 1.0 |
| 42 | 0.8582297563552856 |
| 49 | 0.8517727255821228 |
| 74 | 0.8314518332481384 |
| 483 | 0.8259901404380798 |
| 48 | 0.8057224154472351 |
| 102 | 0.7991676330566406 |
| 73 | 0.7878939509391785 |
| 55 | 0.7401301860809326 |
| 381 | 0.7370997071266174 |
+----------------------------------+
==> oracle.pgx.api.beta.frames.internal.PgxFrameImpl@78d71df1
動くようになりました!
まとめ
PGXでOutOfMemoryErrorが発生したら、まずは以下の3つの方法を順に試すべきです。
詳しくはDocumentにも記載されているので、ご参照ください。
- On-heap領域を増やす
- Off-heap領域を増やす
- PGXで使用できるメモリ領域を増やす