新增了一個新的函式 erlang:set_process_info_limit/2
,允許一個程序設定自身記憶體使用的限制。
erlang:set_process_info_limit(Item, Limit :: integer()) ->
Old_Limit :: integer()
Old_Limit
為 0 表示沒有設定限制。Limit
為 0 表示不設定限制。
可以設定的項目有:
memory
,可用於堆疊、堆積和內部結構的位元組數。
memory_words
,與 memory
相同的值,但以字組表示,以便與 erlang:process_info/[1,2]
中的 heap_size
、total_heap_size
和 stack_size
一致。這些函式也應修改為接受此項目。
message_queue_len
,未處理訊息的數量。(旁註。)文件參考的是 message_queue_len
,但系統產生和識別的是 message_queue_len
。(旁註結束。)
實際設定的值可能小於 Limit
,只要節點中任何實際可實現的系統在設定時都不能超過所使用的值。
每次垃圾回收或其他記憶體重組後都會檢查非零記憶體限制;將記憶體限制設定為小於目前 process_info(self(), 'memory')
的非零值會強制立即執行垃圾回收。
如果程序所需的記憶體超過其限制,則該程序會以原因 memory
退出。
每當訊息佇列長度即將遞增時,或設定此類限制時,都會檢查非零訊息佇列長度限制。
如果訊息佇列長度超過限制,則程序會以原因 message_queue_len
退出。
目前,Erlang 程序有可能無限制地獲取記憶體,並最終導致整個節點崩潰。這個問題經常在 Erlang 郵件列表中被回報。
Erlang 程序的訊息佇列也可能無限制地增長。這個問題也經常被回報,足以引起注意。
這是一個 EEP 而不是函式庫變更請求,因為它需要底層執行時間系統變更才能支援它。例如,限制必須儲存在某處,這表示儲存程序資訊的資料結構需要變更。限制必須被檢查,這表示需要變更垃圾回收器和訊息傳送核心。限制必須被強制執行,這表示必須支援兩個新的退出原因。而且新的退出原因和新的函式必須記錄,這需要變更多個文件。
這個需求由來已久,而且至少此 EEP 的出現可能會引發討論,最終導致某種解決方案。
設定限制的函式應該根據報告目前使用情況的函式名稱來命名。該函式是 process_info
,因此我在此處選擇的名稱是 set_process_info_limit
。
erlang:process_info/[1,2]
函式可以報告任何 Erlang 程序。但是,讓一個程序設定另一個程序的限制顯然是很危險的。我們實際需要的是讓一個程序能夠在啟動階段設定自己的限制。這可以透過將 {'memory',Size}
和 {'message_queue_len',Count}
選項新增至 spawn_opt/[2,3]
來完成。但是,
spawn_opt(Fun, [{memory,128*1024}])
可以透過以下方式模擬:
spawn(fun () ->
erlang:set_process_info_limit(memory, 128*1024),
Fun()
end)
而且 set_process_info_limit/2
允許一個程序在不同的時間設定不同的限制。如果您試圖保護系統免受惡意侵害,那麼在 spawn_opt/[2,3]
中設定限制是可行的。如果您試圖保護系統免受意外侵害,那麼讓程序設定自己的限制是可行的。
要設定的項目的名稱顯然必須與其他地方用於相同事物的名稱相同。
引入 memory_words
項目是因為在我的某些程式運行 32 位元,而某些程式運行 64 位元的環境中,計算位元組太容易混淆了,尤其是堆疊大小等都是以字組而不是位元組來衡量的。
我曾考慮允許 total_heap_size
和 stack_size
等設定自己的限制,但在發現 total_heap_size
「目前包含該程序的堆疊」後,我認為「目前」這不是一個好主意。
我本來想允許設定縮減次數的限制,以便不打算永遠存活的程序可以確保自己的最終死亡。但是,文件警告說 erlang:bump_reductions/1
「可能會被移除」,因此推測本身的縮減計數很可能會消失。
允許限制的實際設定值較小的目的是允許實作僅使用適合 size_t
的值,以便底層程式碼不需要處理大數。在符合 POSIX 標準的作業系統上,如果記憶體限制較大,則 Erlang 實作可以使用 UNIX 程序的 RLIMIT_DATA
值,如果訊息佇列長度較大,則可以使用 RLIMIT_DATA
除以訊息的最小大小。或者它可以使用其他適當的系統限制。
最大的問題是「如果超過限制會發生什麼?」
對於記憶體,我們可以以 system_limit
為原因退出,但這不會明確指出哪個限制已被超過。引入一個新的原因似乎是明智的,而且使原因與限制的名稱相同似乎是最不容易混淆的方法。
對於訊息佇列長度,我會希望傳送訊息的程序出現執行階段錯誤。但是,Erlang 文件保證傳送訊息到 pid 始終成功,無論該程序是存活還是死亡,而且我不想變更太多。訊息佇列可能會累積,因為某些程序正在傳送垃圾;我們寧願退出垃圾傳送者,也不願退出垃圾接收者。但是,如果垃圾接收者沒有清理垃圾,那麼這些垃圾將永遠不會消失,因此除了儲存記憶體之外,跳過這些垃圾也總是會耗費時間。這個論點也適用於我考慮的另一個選項:只是丟棄會使佇列太長的訊息。Erlang 的方式是讓陷入麻煩的子系統崩潰。就這樣吧。
新函式預設不會匯出,因此不會意外被呼叫。
在本草案中沒有。
本文件已置於公有領域。