35
35
-export ([get_total_memory /0 , get_vm_limit /0 ,
36
36
get_check_interval /0 , set_check_interval /1 ,
37
37
get_vm_memory_high_watermark /0 , set_vm_memory_high_watermark /1 ,
38
- get_memory_limit /0 , get_memory_use /1 ]).
38
+ get_memory_limit /0 , get_memory_use /1 ,
39
+ get_process_memory /0 ]).
39
40
40
41
% % for tests
41
42
-export ([parse_line_linux /1 ]).
@@ -117,17 +118,109 @@ get_memory_limit() ->
117
118
118
119
get_memory_use (bytes ) ->
119
120
MemoryLimit = get_memory_limit (),
120
- {rabbit_vm : total_memory (), case MemoryLimit > 0.0 of
121
- true -> MemoryLimit ;
122
- false -> infinity
123
- end };
121
+ {get_process_memory (), case MemoryLimit > 0.0 of
122
+ true -> MemoryLimit ;
123
+ false -> infinity
124
+ end };
124
125
get_memory_use (ratio ) ->
125
126
MemoryLimit = get_memory_limit (),
126
127
case MemoryLimit > 0.0 of
127
- true -> rabbit_vm : total_memory () / MemoryLimit ;
128
+ true -> get_process_memory () / MemoryLimit ;
128
129
false -> infinity
129
130
end .
130
131
132
+ % % Memory reported by erlang:memory(total) is not supposed to
133
+ % % be equal to the total size of all pages mapped to the emulator,
134
+ % % according to http://erlang.org/doc/man/erlang.html#memory-0
135
+ % % erlang:memory(total) under-reports memory usage by around 20%
136
+ -spec get_process_memory () -> Bytes :: integer ().
137
+ get_process_memory () ->
138
+ case get_memory_calculation_strategy () of
139
+ rss ->
140
+ case get_system_process_resident_memory () of
141
+ {ok , MemInBytes } ->
142
+ MemInBytes ;
143
+ {error , Reason } ->
144
+ rabbit_log :debug (" Unable to get system memory used. Reason: ~p ."
145
+ " Falling back to erlang memory reporting" ,
146
+ [Reason ]),
147
+ erlang :memory (total )
148
+ end ;
149
+ erlang ->
150
+ erlang :memory (total )
151
+ end .
152
+
153
+ -spec get_memory_calculation_strategy () -> rss | erlang .
154
+ get_memory_calculation_strategy () ->
155
+ case application :get_env (rabbit , vm_memory_calculation_strategy , rss ) of
156
+ erlang ->
157
+ erlang ;
158
+ rss ->
159
+ rss ;
160
+ UnsupportedValue ->
161
+ rabbit_log :warning (
162
+ " Unsupported value '~p ' for vm_memory_calculation_strategy. "
163
+ " Supported values: (rss|erlang). "
164
+ " Defaulting to 'rss'" ,
165
+ [UnsupportedValue ]
166
+ ),
167
+ rss
168
+ end .
169
+
170
+ -spec get_system_process_resident_memory () -> {ok , Bytes :: integer ()} | {error , term ()}.
171
+ get_system_process_resident_memory () ->
172
+ try
173
+ get_system_process_resident_memory (os :type ())
174
+ catch _ :Error ->
175
+ {error , {" Failed to get process resident memory" , Error }}
176
+ end .
177
+
178
+ get_system_process_resident_memory ({unix ,darwin }) ->
179
+ get_ps_memory ();
180
+
181
+ get_system_process_resident_memory ({unix , linux }) ->
182
+ get_ps_memory ();
183
+
184
+ get_system_process_resident_memory ({unix ,freebsd }) ->
185
+ get_ps_memory ();
186
+
187
+ get_system_process_resident_memory ({unix ,openbsd }) ->
188
+ get_ps_memory ();
189
+
190
+ get_system_process_resident_memory ({win32 ,_OSname }) ->
191
+ OsPid = os :getpid (),
192
+ Cmd = " tasklist /fi \" pid eq " ++ OsPid ++ " \" /fo LIST 2>&1 " ,
193
+ CmdOutput = os :cmd (Cmd ),
194
+ % % Memory usage is displayed in kilobytes
195
+ % % with comma-separated thousands
196
+ case re :run (CmdOutput , " Mem Usage:\\ s+([0-9,]+)\\ s+K" , [{capture , all_but_first , list }]) of
197
+ {match , [Match ]} ->
198
+ NoCommas = [ N || N <- Match , N =/= $, ],
199
+ {ok , list_to_integer (NoCommas ) * 1024 };
200
+ _ ->
201
+ {error , {unexpected_output_from_command , Cmd , CmdOutput }}
202
+ end ;
203
+
204
+ get_system_process_resident_memory ({unix , sunos }) ->
205
+ get_ps_memory ();
206
+
207
+ get_system_process_resident_memory ({unix , aix }) ->
208
+ get_ps_memory ();
209
+
210
+ get_system_process_resident_memory (_OsType ) ->
211
+ {error , not_implemented_for_os }.
212
+
213
+ get_ps_memory () ->
214
+ OsPid = os :getpid (),
215
+ Cmd = " ps -p " ++ OsPid ++ " -o rss=" ,
216
+ CmdOutput = os :cmd (Cmd ),
217
+ case re :run (CmdOutput , " [0-9]+" , [{capture , first , list }]) of
218
+ {match , [Match ]} ->
219
+ {ok , list_to_integer (Match ) * 1024 };
220
+ _ ->
221
+ {error , {unexpected_output_from_command , Cmd , CmdOutput }}
222
+ end .
223
+
131
224
% %----------------------------------------------------------------------------
132
225
% % gen_server callbacks
133
226
% %----------------------------------------------------------------------------
@@ -149,7 +242,7 @@ init([MemFraction, AlarmFuns]) ->
149
242
{ok , set_mem_limits (State , MemFraction )}.
150
243
151
244
handle_call (get_vm_memory_high_watermark , _From ,
152
- # state {memory_config_limit = MemLimit } = State ) ->
245
+ # state {memory_config_limit = MemLimit } = State ) ->
153
246
{reply , MemLimit , State };
154
247
155
248
handle_call ({set_vm_memory_high_watermark , MemLimit }, _From , State ) ->
@@ -268,7 +361,7 @@ parse_mem_limit(_) ->
268
361
internal_update (State = # state { memory_limit = MemLimit ,
269
362
alarmed = Alarmed ,
270
363
alarm_funs = {AlarmSet , AlarmClear } }) ->
271
- MemUsed = rabbit_vm : total_memory (),
364
+ MemUsed = get_process_memory (),
272
365
NewAlarmed = MemUsed > MemLimit ,
273
366
case {Alarmed , NewAlarmed } of
274
367
{false , true } -> emit_update_info (set , MemUsed , MemLimit ),
@@ -365,7 +458,6 @@ get_total_memory({unix, aix}) ->
365
458
get_total_memory (_OsType ) ->
366
459
unknown .
367
460
368
-
369
461
% % A line looks like "Foo bar: 123456."
370
462
parse_line_mach (Line ) ->
371
463
[Name , RHS | _Rest ] = string :tokens (Line , " :" ),
0 commit comments