This is an automated email from the ASF dual-hosted git repository. zouxinyi pushed a commit to branch branch-1.1-lts in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-1.1-lts by this push: new d3006ddd12 [branch-1.1-lts](cherry-pick) Some fixes for mem tracker (#12889) d3006ddd12 is described below commit d3006ddd121f5c89dfea8f38f192de7d03fe5dd4 Author: Xinyi Zou <zouxiny...@gmail.com> AuthorDate: Thu Sep 22 21:47:45 2022 +0800 [branch-1.1-lts](cherry-pick) Some fixes for mem tracker (#12889) * [fix][memtracker] remove gc and fix print * [fix](memory) Fix BE OOM when load -238 fail * [fix](memtracker) Process physical mem check does not include tc/jemalloc allocator cache (#12688) tcmalloc/jemalloc allocator cache does not participate in the mem check as part of the process physical memory. because new/malloc will trigger mem hook when using tcmalloc/jemalloc allocator cache, but it may not actually alloc physical memory, which is not expected in mem hook fail. in addition: The value of tcmalloc/jemalloc allocator cache is used as a mem tracker, the parent is the process mem tracker, which is updated every 1s. Modify the process default mem_limit to 90%. expect mem tracker to effectively limit the memory usage of the process. * Fix memory leak by calling in mem hook (#12708) After the consume mem tracker exceeds the mem limit in the mem hook, the boost stacktrace will be printed. A query/load will only be printed once, and the process tracker will only be printed once per second. After the process memory reaches the upper limit, the boost stacktrace will be printed every second. The observed phenomena are as follows: After query/load is canceled, the memory increases instantly; tcmalloc profile total physical memory is less than perf process memory; The process mem tracker is smaller than the perf process memory; * [fix](memtracker) Fix thread mem tracker try consume accuracy #12782 * [Bugfix](mem) Fix memory limit check may overflow (#12776) This bug is because the result of subtracting signed and unsigned numbers may overflow if it is negative. Co-authored-by: Zhengguo Yang <yangz...@gmail.com> --- be/src/common/config.h | 2 +- be/src/common/daemon.cpp | 1 + be/src/http/default_path_handlers.cpp | 5 +- be/src/runtime/exec_env.h | 9 ++ be/src/runtime/exec_env_init.cpp | 1 + be/src/runtime/load_channel.cpp | 9 +- be/src/runtime/load_channel.h | 2 +- be/src/runtime/load_channel_mgr.cpp | 10 +- be/src/runtime/load_channel_mgr.h | 2 +- be/src/runtime/memory/mem_tracker.cpp | 9 +- be/src/runtime/memory/mem_tracker_limiter.cpp | 136 ++++++++--------------- be/src/runtime/memory/mem_tracker_limiter.h | 134 +++++++++++----------- be/src/runtime/memory/thread_mem_tracker_mgr.cpp | 11 +- be/src/runtime/memory/thread_mem_tracker_mgr.h | 18 +-- be/src/runtime/tablets_channel.cpp | 4 + be/src/service/doris_main.cpp | 10 +- be/src/util/mem_info.cpp | 15 ++- be/src/util/mem_info.h | 37 +++++- be/src/util/perf_counters.cpp | 6 + be/src/util/perf_counters.h | 6 +- be/src/util/system_metrics.cpp | 3 +- 21 files changed, 235 insertions(+), 195 deletions(-) diff --git a/be/src/common/config.h b/be/src/common/config.h index 7f1921d496..106609ee05 100644 --- a/be/src/common/config.h +++ b/be/src/common/config.h @@ -68,7 +68,7 @@ CONF_Int64(tc_max_total_thread_cache_bytes, "1073741824"); // defaults to bytes if no unit is given" // must larger than 0. and if larger than physical memory size, // it will be set to physical memory size. -CONF_String(mem_limit, "80%"); +CONF_String(mem_limit, "90%"); // the port heartbeat service used CONF_Int32(heartbeat_service_port, "9050"); diff --git a/be/src/common/daemon.cpp b/be/src/common/daemon.cpp index ea628bb100..bb39bf13ef 100644 --- a/be/src/common/daemon.cpp +++ b/be/src/common/daemon.cpp @@ -68,6 +68,7 @@ namespace doris { bool k_doris_exit = false; void Daemon::tcmalloc_gc_thread() { + // TODO All cache GC wish to be supported while (!_stop_background_threads_latch.wait_for(MonoDelta::FromSeconds(10))) { size_t used_size = 0; size_t free_size = 0; diff --git a/be/src/http/default_path_handlers.cpp b/be/src/http/default_path_handlers.cpp index c7cdcd2ad8..3efed02a09 100644 --- a/be/src/http/default_path_handlers.cpp +++ b/be/src/http/default_path_handlers.cpp @@ -32,6 +32,7 @@ #include "runtime/mem_tracker.h" #include "runtime/memory/mem_tracker_limiter.h" #include "util/debug_util.h" +#include "util/perf_counters.h" #include "util/pretty_printer.h" #include "util/thread.h" @@ -88,8 +89,8 @@ void mem_usage_handler(const std::shared_ptr<MemTracker>& mem_tracker, (*output) << "<pre>" << "Mem Limit: " << PrettyPrinter::print(mem_tracker->limit(), TUnit::BYTES) << std::endl - << "Mem Consumption: " - << PrettyPrinter::print(mem_tracker->consumption(), TUnit::BYTES) << std::endl + << "Physical Mem From Perf: " + << PrettyPrinter::print(PerfCounters::get_vm_rss(), TUnit::BYTES) << std::endl << "</pre>"; } else { (*output) << "<pre>" diff --git a/be/src/runtime/exec_env.h b/be/src/runtime/exec_env.h index e73eaa1c1f..a28e497a6b 100644 --- a/be/src/runtime/exec_env.h +++ b/be/src/runtime/exec_env.h @@ -41,6 +41,7 @@ class EvHttpServer; class ExternalScanContextMgr; class FragmentMgr; class ResultCache; +class NewMemTracker; class LoadPathMgr; class LoadStreamMgr; class MemTracker; @@ -123,6 +124,13 @@ public: std::shared_ptr<MemTrackerLimiter> new_process_mem_tracker() { return _process_mem_tracker; } MemTrackerLimiter* process_mem_tracker_raw() { return _process_mem_tracker_raw; } + void set_process_mem_tracker(const std::shared_ptr<MemTrackerLimiter>& tracker) { + _process_mem_tracker = tracker; + _process_mem_tracker_raw = tracker.get(); + } + std::shared_ptr<NewMemTracker> allocator_cache_mem_tracker() { + return _allocator_cache_mem_tracker; + } std::shared_ptr<MemTrackerLimiter> query_pool_mem_tracker() { return _query_pool_mem_tracker; } std::shared_ptr<MemTrackerLimiter> load_pool_mem_tracker() { return _load_pool_mem_tracker; } MemTrackerTaskPool* task_pool_mem_tracker_registry() { return _task_pool_mem_tracker_registry; } @@ -199,6 +207,7 @@ private: // The ancestor for all trackers. Every tracker is visible from the process down. // Not limit total memory by process tracker, and it's just used to track virtual memory of process. std::shared_ptr<MemTrackerLimiter> _process_mem_tracker; + std::shared_ptr<NewMemTracker> _allocator_cache_mem_tracker; MemTrackerLimiter* _process_mem_tracker_raw; // The ancestor for all querys tracker. std::shared_ptr<MemTrackerLimiter> _query_pool_mem_tracker; diff --git a/be/src/runtime/exec_env_init.cpp b/be/src/runtime/exec_env_init.cpp index 54640a6e70..0fe31a1a6b 100644 --- a/be/src/runtime/exec_env_init.cpp +++ b/be/src/runtime/exec_env_init.cpp @@ -215,6 +215,7 @@ Status ExecEnv::_init_mem_tracker() { } #endif + _allocator_cache_mem_tracker = std::make_shared<NewMemTracker>("Tc/JemallocAllocatorCache"); _query_pool_mem_tracker = std::make_shared<MemTrackerLimiter>(-1, "QueryPool", _process_mem_tracker); _load_pool_mem_tracker = diff --git a/be/src/runtime/load_channel.cpp b/be/src/runtime/load_channel.cpp index 715762bd18..506472ba0a 100644 --- a/be/src/runtime/load_channel.cpp +++ b/be/src/runtime/load_channel.cpp @@ -86,7 +86,7 @@ Status LoadChannel::add_batch(const PTabletWriterAddBatchRequest& request, } // 2. check if mem consumption exceed limit - handle_mem_exceed_limit(false); + RETURN_IF_ERROR(handle_mem_exceed_limit(false)); // 3. add batch to tablets channel if (request.has_row_batch()) { @@ -111,11 +111,11 @@ Status LoadChannel::add_batch(const PTabletWriterAddBatchRequest& request, return st; } -void LoadChannel::handle_mem_exceed_limit(bool force) { +Status LoadChannel::handle_mem_exceed_limit(bool force) { // lock so that only one thread can check mem limit std::lock_guard<std::mutex> l(_lock); if (!(force || _mem_tracker->limit_exceeded())) { - return; + return Status::OK(); } if (!force) { @@ -125,12 +125,13 @@ void LoadChannel::handle_mem_exceed_limit(bool force) { std::shared_ptr<TabletsChannel> channel; if (_find_largest_consumption_channel(&channel)) { - channel->reduce_mem_usage(_mem_tracker->limit()); + return channel->reduce_mem_usage(_mem_tracker->limit()); } else { // should not happen, add log to observe LOG(WARNING) << "fail to find suitable tablets-channel when memory exceed. " << "load_id=" << _load_id; } + return Status::OK(); } // lock should be held when calling this method diff --git a/be/src/runtime/load_channel.h b/be/src/runtime/load_channel.h index 13490f5fa8..c74718c8ab 100644 --- a/be/src/runtime/load_channel.h +++ b/be/src/runtime/load_channel.h @@ -63,7 +63,7 @@ public: // If yes, it will pick a tablets channel to try to reduce memory consumption. // If force is true, even if this load channel does not exceeds limit, it will still // try to reduce memory. - void handle_mem_exceed_limit(bool force); + Status handle_mem_exceed_limit(bool force); int64_t mem_consumption() const { return _mem_tracker->consumption(); } diff --git a/be/src/runtime/load_channel_mgr.cpp b/be/src/runtime/load_channel_mgr.cpp index 98ef8c724d..a76109d700 100644 --- a/be/src/runtime/load_channel_mgr.cpp +++ b/be/src/runtime/load_channel_mgr.cpp @@ -152,7 +152,7 @@ Status LoadChannelMgr::add_batch(const PTabletWriterAddBatchRequest& request, // 2. check if mem consumption exceed limit // If this is a high priority load task, do not handle this. // because this may block for a while, which may lead to rpc timeout. - _handle_mem_exceed_limit(); + RETURN_IF_ERROR(_handle_mem_exceed_limit()); } // 3. add batch to load channel @@ -175,11 +175,11 @@ Status LoadChannelMgr::add_batch(const PTabletWriterAddBatchRequest& request, return Status::OK(); } -void LoadChannelMgr::_handle_mem_exceed_limit() { +Status LoadChannelMgr::_handle_mem_exceed_limit() { // lock so that only one thread can check mem limit std::lock_guard<std::mutex> l(_lock); if (!_mem_tracker->limit_exceeded()) { - return; + return Status::OK(); } int64_t max_consume = 0; @@ -198,14 +198,14 @@ void LoadChannelMgr::_handle_mem_exceed_limit() { if (max_consume == 0) { // should not happen, add log to observe LOG(WARNING) << "failed to find suitable load channel when total load mem limit exceed"; - return; + return Status::OK(); } DCHECK(channel.get() != nullptr); // force reduce mem limit of the selected channel LOG(INFO) << "reducing memory of " << *channel << " because total load mem consumption " << _mem_tracker->consumption() << " has exceeded limit " << _mem_tracker->limit(); - channel->handle_mem_exceed_limit(true); + return channel->handle_mem_exceed_limit(true); } Status LoadChannelMgr::cancel(const PTabletWriterCancelRequest& params) { diff --git a/be/src/runtime/load_channel_mgr.h b/be/src/runtime/load_channel_mgr.h index 1da0ec75a5..a522ab7d39 100644 --- a/be/src/runtime/load_channel_mgr.h +++ b/be/src/runtime/load_channel_mgr.h @@ -59,7 +59,7 @@ public: private: // check if the total load mem consumption exceeds limit. // If yes, it will pick a load channel to try to reduce memory consumption. - void _handle_mem_exceed_limit(); + Status _handle_mem_exceed_limit(); Status _start_bg_worker(); diff --git a/be/src/runtime/memory/mem_tracker.cpp b/be/src/runtime/memory/mem_tracker.cpp index 8c0ae6ebba..07ca883a94 100644 --- a/be/src/runtime/memory/mem_tracker.cpp +++ b/be/src/runtime/memory/mem_tracker.cpp @@ -105,10 +105,11 @@ void NewMemTracker::make_group_snapshot(std::vector<NewMemTracker::Snapshot>* sn } std::string NewMemTracker::log_usage(NewMemTracker::Snapshot snapshot) { - return fmt::format("NewMemTracker Label={}, Parent Label={}, Used={}, Peak={}", snapshot.label, - snapshot.parent, - PrettyPrinter::print(snapshot.cur_consumption, TUnit::BYTES), - PrettyPrinter::print(snapshot.peak_consumption, TUnit::BYTES)); + return fmt::format( + "MemTracker Label={}, Parent Label={}, Used={}({} B), Peak={}({} B)", snapshot.label, + snapshot.parent, PrettyPrinter::print(snapshot.cur_consumption, TUnit::BYTES), + snapshot.cur_consumption, PrettyPrinter::print(snapshot.peak_consumption, TUnit::BYTES), + snapshot.peak_consumption); } } // namespace doris \ No newline at end of file diff --git a/be/src/runtime/memory/mem_tracker_limiter.cpp b/be/src/runtime/memory/mem_tracker_limiter.cpp index fd4c46259c..0494657396 100644 --- a/be/src/runtime/memory/mem_tracker_limiter.cpp +++ b/be/src/runtime/memory/mem_tracker_limiter.cpp @@ -24,7 +24,6 @@ #include "gutil/once.h" #include "runtime/runtime_state.h" #include "runtime/thread_context.h" -#include "service/backend_options.h" #include "util/pretty_printer.h" #include "util/string_util.h" @@ -49,6 +48,8 @@ MemTrackerLimiter::MemTrackerLimiter(int64_t byte_limit, const std::string& labe MemTrackerLimiter* tracker = this; while (tracker != nullptr) { _all_ancestors.push_back(tracker); + // Process tracker does not participate in the process memory limit, process tracker consumption is virtual memory, + // and there is a diff between the real physical memory value of the process. It is replaced by check_sys_mem_info. if (tracker->has_limit() && tracker->label() != "Process") _limited_ancestors.push_back(tracker); tracker = tracker->_parent.get(); @@ -123,42 +124,6 @@ int64_t MemTrackerLimiter::get_lowest_limit() const { return min_limit; } -bool MemTrackerLimiter::gc_memory(int64_t max_consumption) { - if (max_consumption < 0) return true; - std::lock_guard<std::mutex> l(_gc_lock); - int64_t pre_gc_consumption = consumption(); - // Check if someone gc'd before us - if (pre_gc_consumption < max_consumption) return false; - - int64_t curr_consumption = pre_gc_consumption; - // Free some extra memory to avoid frequent GC, 4M is an empirical value, maybe it will be tested later. - const int64_t EXTRA_BYTES_TO_FREE = 4L * 1024L * 1024L * 1024L; - // Try to free up some memory - for (int i = 0; i < _gc_functions.size(); ++i) { - // Try to free up the amount we are over plus some extra so that we don't have to - // immediately GC again. Don't free all the memory since that can be unnecessarily - // expensive. - int64_t bytes_to_free = curr_consumption - max_consumption + EXTRA_BYTES_TO_FREE; - _gc_functions[i](bytes_to_free); - curr_consumption = consumption(); - if (max_consumption - curr_consumption <= EXTRA_BYTES_TO_FREE) break; - } - - return curr_consumption > max_consumption; -} - -Status MemTrackerLimiter::try_gc_memory(int64_t bytes) { - if (UNLIKELY(gc_memory(_limit - bytes))) { - return Status::MemoryLimitExceeded(fmt::format( - "failed_alloc_size={} B, exceeded_tracker={}, limit={} B, peak_used={} B, " - "current_used={} B", - bytes, label(), _limit, _consumption->value(), _consumption->current_value())); - } - VLOG_NOTICE << "GC succeeded, TryConsume bytes=" << bytes - << " consumption=" << _consumption->current_value() << " limit=" << _limit; - return Status::OK(); -} - // Calling this on the query tracker results in output like: // // Query(4a4c81fedaed337d:4acadfda00000000) Limit=10.00 GB Total=508.28 MB Peak=508.45 MB @@ -185,10 +150,10 @@ std::string MemTrackerLimiter::log_usage(int max_recursive_depth, int64_t* logge int64_t peak_consumption = _consumption->value(); if (logged_consumption != nullptr) *logged_consumption = curr_consumption; - std::string detail = "MemTrackerLimiter Label={}, Limit={}, Used={}, Peak={}, Exceeded={}"; - detail = fmt::format(detail, _label, PrettyPrinter::print(_limit, TUnit::BYTES), - PrettyPrinter::print(curr_consumption, TUnit::BYTES), - PrettyPrinter::print(peak_consumption, TUnit::BYTES), + std::string detail = + "MemTrackerLimiter Label={}, Limit={}({} B), Used={}({} B), Peak={}({} B), Exceeded={}"; + detail = fmt::format(detail, _label, print_bytes(_limit), _limit, print_bytes(curr_consumption), + curr_consumption, print_bytes(peak_consumption), peak_consumption, limit_exceeded() ? "true" : "false"); // This call does not need the children, so return early. @@ -222,41 +187,38 @@ std::string MemTrackerLimiter::log_usage(int max_recursive_depth, if (!usage_string.empty()) usage_strings.push_back(usage_string); *logged_consumption += tracker_consumption; } - return join(usage_strings, "\n"); -} - -Status MemTrackerLimiter::mem_limit_exceeded_construct(const std::string& msg) { - std::string detail = fmt::format( - "{}, backend {} process memory used {}, process limit {}. If is query, can " - "change the limit " - "by `set exec_mem_limit=xxx`, details mem usage see be.INFO.", - msg, BackendOptions::get_localhost(), - PrettyPrinter::print(PerfCounters::get_vm_rss(), TUnit::BYTES), - PrettyPrinter::print(MemInfo::mem_limit(), TUnit::BYTES)); - return Status::MemoryLimitExceeded(detail); + return usage_strings.size() == 0 ? "" : "\n " + join(usage_strings, "\n "); } void MemTrackerLimiter::print_log_usage(const std::string& msg) { // only print the tracker log_usage in be log. std::string detail = msg; + detail += "\n " + fmt::format( + "process memory used {}, limit {}, hard limit {}, tc/jemalloc " + "allocator cache {}", + PerfCounters::get_vm_rss_str(), MemInfo::mem_limit_str(), + print_bytes(MemInfo::hard_mem_limit()), + MemInfo::allocator_cache_mem_str()); if (_print_log_usage) { if (_label == "Process") { // Dumping the process MemTracker is expensive. Limiting the recursive depth to two // levels limits the level of detail to a one-line summary for each query MemTracker. - detail += "\n" + log_usage(2); + detail += "\n " + log_usage(2); } else { - detail += "\n" + log_usage(); + detail += "\n " + log_usage(); } - detail += "\n" + boost::stacktrace::to_string(boost::stacktrace::stacktrace()); + // TODO: memory leak by calling `boost::stacktrace` in tcmalloc hook, + // test whether overwriting malloc/free is the same problem in jemalloc/tcmalloc. + // detail += "\n" + boost::stacktrace::to_string(boost::stacktrace::stacktrace()); LOG(WARNING) << detail; _print_log_usage = false; } } -Status MemTrackerLimiter::mem_limit_exceeded(const std::string& msg, - int64_t failed_allocation_size) { +std::string MemTrackerLimiter::mem_limit_exceeded(const std::string& msg, + int64_t failed_allocation_size) { STOP_CHECK_THREAD_MEM_TRACKER_LIMIT(); - std::string detail = fmt::format("Memory limit exceeded:<consuming_tracker={}, ", _label); + std::string detail = fmt::format("Memory limit exceeded:<consuming tracker:<{}>, ", _label); MemTrackerLimiter* exceeded_tracker = nullptr; MemTrackerLimiter* max_consumption_tracker = nullptr; int64_t free_size = INT64_MAX; @@ -275,57 +237,51 @@ Status MemTrackerLimiter::mem_limit_exceeded(const std::string& msg, } } - auto sys_exceed_st = check_sys_mem_info(failed_allocation_size); MemTrackerLimiter* print_log_usage_tracker = nullptr; if (exceeded_tracker != nullptr) { - detail += fmt::format( - "failed_alloc_size={} B, exceeded_tracker={}, limit={} B, peak_used={} B, " - "current_used={} B>, executing_msg:<{}>", - PrettyPrinter::print(failed_allocation_size, TUnit::BYTES), - exceeded_tracker->label(), exceeded_tracker->limit(), - exceeded_tracker->peak_consumption(), exceeded_tracker->consumption(), msg); + detail += limit_exceeded_errmsg_prefix_str(failed_allocation_size, exceeded_tracker); print_log_usage_tracker = exceeded_tracker; - } else if (!sys_exceed_st) { - detail += fmt::format("{}>, executing_msg:<{}>", sys_exceed_st.get_error_msg(), msg); + } else if (sys_mem_exceed_limit_check(failed_allocation_size)) { + detail += fmt::format("{}>, executing msg:<{}>", + limit_exceeded_errmsg_sys_str(failed_allocation_size), msg); } else if (max_consumption_tracker != nullptr) { // must after check_sys_mem_info false detail += fmt::format( - "failed_alloc_size={} B, max_consumption_tracker={}, limit={} B, peak_used={} B, " - "current_used={} B>, executing_msg:<{}>", - PrettyPrinter::print(failed_allocation_size, TUnit::BYTES), - max_consumption_tracker->label(), max_consumption_tracker->limit(), - max_consumption_tracker->peak_consumption(), max_consumption_tracker->consumption(), - msg); + "failed alloc size {}, max consumption tracker:<{}>, limit {}, peak used {}, " + "current used {}>, executing msg:<{}>", + print_bytes(failed_allocation_size), max_consumption_tracker->label(), + print_bytes(max_consumption_tracker->limit()), + print_bytes(max_consumption_tracker->peak_consumption()), + print_bytes(max_consumption_tracker->consumption()), msg); print_log_usage_tracker = max_consumption_tracker; } else { // The limit of the current tracker and parents is less than 0, the consume will not fail, // and the current process memory has no excess limit. - detail += fmt::format("unknown exceed reason, executing_msg:<{}>", msg); + detail += fmt::format("unknown exceed reason, executing msg:<{}>", msg); print_log_usage_tracker = ExecEnv::GetInstance()->process_mem_tracker_raw(); } - auto st = MemTrackerLimiter::mem_limit_exceeded_construct(detail); - if (print_log_usage_tracker != nullptr) - print_log_usage_tracker->print_log_usage(st.get_error_msg()); - return st; + auto failed_msg = MemTrackerLimiter::limit_exceeded_errmsg_suffix_str(detail); + if (print_log_usage_tracker != nullptr) print_log_usage_tracker->print_log_usage(failed_msg); + return failed_msg; } -Status MemTrackerLimiter::mem_limit_exceeded(const std::string& msg, - MemTrackerLimiter* failed_tracker, - Status failed_try_consume_st) { +std::string MemTrackerLimiter::mem_limit_exceeded(const std::string& msg, + MemTrackerLimiter* failed_tracker, + const std::string& limit_exceeded_errmsg_prefix) { STOP_CHECK_THREAD_MEM_TRACKER_LIMIT(); std::string detail = - fmt::format("Memory limit exceeded:<consuming_tracker={}, {}>, executing_msg:<{}>", - _label, failed_try_consume_st.get_error_msg(), msg); - auto st = MemTrackerLimiter::mem_limit_exceeded_construct(detail); - failed_tracker->print_log_usage(st.get_error_msg()); - return st; + fmt::format("Memory limit exceeded:<consuming tracker:<{}>, {}>, executing msg:<{}>", + _label, limit_exceeded_errmsg_prefix, msg); + auto failed_msg = MemTrackerLimiter::limit_exceeded_errmsg_suffix_str(detail); + failed_tracker->print_log_usage(failed_msg); + return failed_msg; } Status MemTrackerLimiter::mem_limit_exceeded(RuntimeState* state, const std::string& msg, int64_t failed_alloc_size) { - Status rt = mem_limit_exceeded(msg, failed_alloc_size); - state->log_error(rt.to_string()); - return rt; + auto failed_msg = mem_limit_exceeded(msg, failed_alloc_size); + state->log_error(failed_msg); + return Status::MemoryLimitExceeded(failed_msg); } } // namespace doris diff --git a/be/src/runtime/memory/mem_tracker_limiter.h b/be/src/runtime/memory/mem_tracker_limiter.h index c2541b6aea..0886279c6e 100644 --- a/be/src/runtime/memory/mem_tracker_limiter.h +++ b/be/src/runtime/memory/mem_tracker_limiter.h @@ -22,8 +22,10 @@ #include "common/config.h" #include "runtime/exec_env.h" #include "runtime/memory/mem_tracker.h" +#include "service/backend_options.h" #include "util/mem_info.h" #include "util/perf_counters.h" +#include "util/pretty_printer.h" namespace doris { @@ -64,19 +66,23 @@ public: size_t upper_level) const; public: - static Status check_sys_mem_info(int64_t bytes) { + static bool sys_mem_exceed_limit_check(int64_t bytes) { // Limit process memory usage using the actual physical memory of the process in `/proc/self/status`. // This is independent of the consumption value of the mem tracker, which counts the virtual memory // of the process malloc. // for fast, expect MemInfo::initialized() to be true. - if (PerfCounters::get_vm_rss() + bytes >= MemInfo::mem_limit()) { - auto st = Status::MemoryLimitExceeded( - fmt::format("process memory used {} B, exceed limit {} B, failed_alloc_size={} B", - PerfCounters::get_vm_rss(), MemInfo::mem_limit(), bytes)); - ExecEnv::GetInstance()->process_mem_tracker_raw()->print_log_usage(st.get_error_msg()); - return st; + // tcmalloc/jemalloc allocator cache does not participate in the mem check as part of the process physical memory. + // because `new/malloc` will trigger mem hook when using tcmalloc/jemalloc allocator cache, + // but it may not actually alloc physical memory, which is not expected in mem hook fail. + // + // TODO: In order to ensure no OOM, currently reserve 200M, and then use the free mem in /proc/meminfo to ensure no OOM. + if (PerfCounters::get_vm_rss() - static_cast<int64_t>(MemInfo::allocator_cache_mem()) + + bytes >= + MemInfo::mem_limit() || + PerfCounters::get_vm_rss() + bytes >= MemInfo::hard_mem_limit()) { + return true; } - return Status::OK(); + return false; } int64_t group_num() const { return _group_num; } @@ -108,21 +114,6 @@ public: // Returns the lowest limit for this tracker limiter and its ancestors. Returns -1 if there is no limit. int64_t get_lowest_limit() const; - typedef std::function<void(int64_t bytes_to_free)> GcFunction; - // Add a function 'f' to be called if the limit is reached, if none of the other - // previously-added GC functions were successful at freeing up enough memory. - // 'f' does not need to be thread-safe as long as it is added to only one tracker limiter. - // Note that 'f' must be valid for the lifetime of this tracker limiter. - void add_gc_function(GcFunction f) { _gc_functions.push_back(f); } - - // TODO Should be managed in a separate process_mem_mgr, not in NewMemTracker - // If consumption is higher than max_consumption, attempts to free memory by calling - // any added GC functions. Returns true if max_consumption is still exceeded. Takes gc_lock. - // Note: If the cache of segment/chunk is released due to insufficient query memory at a certain moment, - // the performance of subsequent queries may be degraded, so the use of gc function should be careful enough. - bool gc_memory(int64_t max_consumption); - Status try_gc_memory(int64_t bytes); - public: // up to (but not including) end_tracker. // This happens when we want to update tracking on a particular mem tracker but the consumption @@ -147,15 +138,16 @@ public: // Limiting the recursive depth reduces the cost of dumping, particularly // for the process tracker limiter. std::string log_usage(int max_recursive_depth = INT_MAX, int64_t* logged_consumption = nullptr); + void print_log_usage(const std::string& msg); // Log the memory usage when memory limit is exceeded and return a status object with // msg of the allocation which caused the limit to be exceeded. // If 'failed_allocation_size' is greater than zero, logs the allocation size. If // 'failed_allocation_size' is zero, nothing about the allocation size is logged. // If 'state' is non-nullptr, logs the error to 'state'. - Status mem_limit_exceeded(const std::string& msg, int64_t failed_allocation_size = 0); - Status mem_limit_exceeded(const std::string& msg, MemTrackerLimiter* failed_tracker, - Status failed_try_consume_st); + std::string mem_limit_exceeded(const std::string& msg, int64_t failed_allocation_size = 0); + std::string mem_limit_exceeded(const std::string& msg, MemTrackerLimiter* failed_tracker, + const std::string& limit_exceeded_errmsg_prefix); Status mem_limit_exceeded(RuntimeState* state, const std::string& msg, int64_t failed_allocation_size = 0); @@ -169,6 +161,10 @@ public: return msg.str(); } + static std::string print_bytes(int64_t bytes) { + return PrettyPrinter::print(bytes, TUnit::BYTES); + } + private: // The following func, for automatic memory tracking and limiting based on system memory allocation. friend class ThreadMemTrackerMgr; @@ -183,7 +179,7 @@ private: // they can all consume 'bytes' without exceeding limit. If limit would be exceed, // no MemTrackerLimiters are updated. Returns true if the consumption was successfully updated. WARN_UNUSED_RESULT - Status try_consume(int64_t bytes); + bool try_consume(int64_t bytes, std::string& failed_msg); // When the accumulated untracked memory value exceeds the upper limit, // the current value is returned and set to 0. @@ -197,8 +193,33 @@ private: const std::list<MemTrackerLimiter*>& trackers, int64_t* logged_consumption); - static Status mem_limit_exceeded_construct(const std::string& msg); - void print_log_usage(const std::string& msg); + static std::string limit_exceeded_errmsg_prefix_str(int64_t bytes, + MemTrackerLimiter* exceed_tracker) { + return fmt::format( + "failed alloc size {}, exceeded tracker:<{}>, limit {}, peak " + "used {}, current used {}", + print_bytes(bytes), exceed_tracker->label(), print_bytes(exceed_tracker->limit()), + print_bytes(exceed_tracker->_consumption->value()), + print_bytes(exceed_tracker->_consumption->current_value())); + } + + static std::string limit_exceeded_errmsg_suffix_str(const std::string& msg) { + return fmt::format( + "{}. backend {} process memory used {}, limit {}. If query tracker exceed, `set " + "exec_mem_limit=8G` to change limit, details mem usage see be.INFO.", + msg, BackendOptions::get_localhost(), PerfCounters::get_vm_rss_str(), + MemInfo::mem_limit_str()); + } + + static std::string limit_exceeded_errmsg_sys_str(int64_t bytes) { + auto err_msg = fmt::format( + "process memory used {}, tc/jemalloc allocator cache {}, exceed limit {}, failed " + "alloc size {}", + PerfCounters::get_vm_rss_str(), MemInfo::allocator_cache_mem_str(), + MemInfo::mem_limit_str(), print_bytes(bytes)); + ExecEnv::GetInstance()->process_mem_tracker_raw()->print_log_usage(err_msg); + return err_msg; + } private: // Limit on memory consumption, in bytes. If limit_ == -1, there is no consumption limit. Used in log_usage。 @@ -230,19 +251,6 @@ private: std::atomic_size_t _had_child_count = 0; bool _print_log_usage = false; - - // Lock to protect gc_memory(). This prevents many GCs from occurring at once. - std::mutex _gc_lock; - // Functions to call after the limit is reached to free memory. - // GcFunctions can be attached to a NewMemTracker in order to free up memory if the limit is - // reached. If limit_exceeded() is called and the limit is exceeded, it will first call - // the GcFunctions to try to free memory and recheck the limit. For example, the process - // tracker has a GcFunction that releases any unused memory still held by tcmalloc, so - // this will be called before the process limit is reported as exceeded. GcFunctions are - // called in the order they are added, so expensive functions should be added last. - // GcFunctions are called with a global lock held, so should be non-blocking and not - // call back into MemTrackers, except to release memory. - std::vector<GcFunction> _gc_functions; }; inline void MemTrackerLimiter::consume(int64_t bytes) { @@ -271,12 +279,16 @@ inline void MemTrackerLimiter::cache_consume_local(int64_t bytes) { } } -inline Status MemTrackerLimiter::try_consume(int64_t bytes) { +inline bool MemTrackerLimiter::try_consume(int64_t bytes, std::string& failed_msg) { if (bytes <= 0) { release(-bytes); - return Status::OK(); + failed_msg = std::string(); + return true; + } + if (sys_mem_exceed_limit_check(bytes)) { + failed_msg = limit_exceeded_errmsg_sys_str(bytes); + return false; } - RETURN_IF_ERROR(check_sys_mem_info(bytes)); int i; // Walk the tracker tree top-down. for (i = _all_ancestors.size() - 1; i >= 0; --i) { @@ -286,39 +298,33 @@ inline Status MemTrackerLimiter::try_consume(int64_t bytes) { if (tracker->limit() < 0 || tracker->label() == "Process") { tracker->_consumption->add(bytes); // No limit at this tracker. } else { - // If TryConsume fails, we can try to GC, but we may need to try several times if - // there are concurrent consumers because we don't take a lock before trying to - // update _consumption. - while (true) { - if (LIKELY(tracker->_consumption->try_add(bytes, tracker->limit()))) break; - Status st = tracker->try_gc_memory(bytes); - if (!st) { - // Failed for this mem tracker. Roll back the ones that succeeded. - for (int j = _all_ancestors.size() - 1; j > i; --j) { - _all_ancestors[j]->_consumption->add(-bytes); - } - return st; + if (!tracker->_consumption->try_add(bytes, tracker->limit())) { + // Failed for this mem tracker. Roll back the ones that succeeded. + for (int j = _all_ancestors.size() - 1; j > i; --j) { + _all_ancestors[j]->_consumption->add(-bytes); } + failed_msg = limit_exceeded_errmsg_prefix_str(bytes, tracker); + return false; } } } // Everyone succeeded, return. DCHECK_EQ(i, -1); - return Status::OK(); + failed_msg = std::string(); + return true; } inline Status MemTrackerLimiter::check_limit(int64_t bytes) { if (bytes <= 0) return Status::OK(); - RETURN_IF_ERROR(check_sys_mem_info(bytes)); + if (sys_mem_exceed_limit_check(bytes)) { + return Status::MemoryLimitExceeded(limit_exceeded_errmsg_sys_str(bytes)); + } int i; // Walk the tracker tree top-down. for (i = _limited_ancestors.size() - 1; i >= 0; --i) { MemTrackerLimiter* tracker = _limited_ancestors[i]; - // Process tracker does not participate in the process memory limit, process tracker consumption is virtual memory, - // and there is a diff between the real physical memory value of the process. It is replaced by check_sys_mem_info. - while (true) { - if (LIKELY(tracker->_consumption->current_value() + bytes < tracker->limit())) break; - RETURN_IF_ERROR(tracker->try_gc_memory(bytes)); + if (tracker->_consumption->current_value() + bytes > tracker->limit()) { + return Status::MemoryLimitExceeded(limit_exceeded_errmsg_prefix_str(bytes, tracker)); } } return Status::OK(); diff --git a/be/src/runtime/memory/thread_mem_tracker_mgr.cpp b/be/src/runtime/memory/thread_mem_tracker_mgr.cpp index 30f7e7f10b..40879b6c4a 100644 --- a/be/src/runtime/memory/thread_mem_tracker_mgr.cpp +++ b/be/src/runtime/memory/thread_mem_tracker_mgr.cpp @@ -51,16 +51,17 @@ void ThreadMemTrackerMgr::exceeded_cancel_task(const std::string& cancel_details } } -void ThreadMemTrackerMgr::exceeded(Status failed_try_consume_st) { +void ThreadMemTrackerMgr::exceeded(const std::string& failed_msg) { if (_cb_func != nullptr) { _cb_func(); } if (is_attach_query()) { - auto st = _limiter_tracker_raw->mem_limit_exceeded(fmt::format("exec node:<{}>", ""), - _limiter_tracker->parent().get(), - failed_try_consume_st); - exceeded_cancel_task(st.get_error_msg()); + auto cancel_msg = _limiter_tracker_raw->mem_limit_exceeded( + fmt::format("exec node:<{}>", ""), + _limiter_tracker_raw->parent().get(), failed_msg); + exceeded_cancel_task(cancel_msg); _check_limit = false; // Make sure it will only be canceled once } } + } // namespace doris diff --git a/be/src/runtime/memory/thread_mem_tracker_mgr.h b/be/src/runtime/memory/thread_mem_tracker_mgr.h index 5135d6222b..3f652ecd5a 100644 --- a/be/src/runtime/memory/thread_mem_tracker_mgr.h +++ b/be/src/runtime/memory/thread_mem_tracker_mgr.h @@ -103,12 +103,14 @@ private: // If tryConsume fails due to task mem tracker exceeding the limit, the task must be canceled void exceeded_cancel_task(const std::string& cancel_details); - void exceeded(Status failed_try_consume_st); + void exceeded(const std::string& failed_msg); private: // Cache untracked mem, only update to _untracked_mems when switching mem tracker. // Frequent calls to unordered_map _untracked_mems[] in consume will degrade performance. int64_t _untracked_mem = 0; + int64_t old_untracked_mem = 0; + std::string failed_msg = std::string(); std::shared_ptr<MemTrackerLimiter> _limiter_tracker; std::vector<NewMemTracker*> _consumer_tracker_stack; @@ -167,6 +169,7 @@ inline void ThreadMemTrackerMgr::flush_untracked_mem() { // Temporary memory may be allocated during the consumption of the mem tracker, which will lead to entering // the TCMalloc Hook again, so suspend consumption to avoid falling into an infinite loop. _stop_consume = true; + old_untracked_mem = _untracked_mem; DCHECK(_limiter_tracker); if (CheckLimit) { #ifndef BE_TEST @@ -179,20 +182,19 @@ inline void ThreadMemTrackerMgr::flush_untracked_mem() { // DCHECK(!_check_attach || btls_key != EMPTY_BTLS_KEY || // _limiter_tracker->label() != "Process"); #endif - Status st = _limiter_tracker_raw->try_consume(_untracked_mem); - if (!st) { + if (!_limiter_tracker_raw->try_consume(old_untracked_mem, failed_msg)) { // The memory has been allocated, so when TryConsume fails, need to continue to complete // the consume to ensure the accuracy of the statistics. - _limiter_tracker_raw->consume(_untracked_mem); - exceeded(st); + _limiter_tracker_raw->consume(old_untracked_mem); + exceeded(failed_msg); } } else { - _limiter_tracker_raw->consume(_untracked_mem); + _limiter_tracker_raw->consume(old_untracked_mem); } for (auto tracker : _consumer_tracker_stack) { - tracker->consume(_untracked_mem); + tracker->consume(old_untracked_mem); } - _untracked_mem = 0; + _untracked_mem -= old_untracked_mem; _stop_consume = false; } diff --git a/be/src/runtime/tablets_channel.cpp b/be/src/runtime/tablets_channel.cpp index 1e1ab2cd7b..3785eedd10 100644 --- a/be/src/runtime/tablets_channel.cpp +++ b/be/src/runtime/tablets_channel.cpp @@ -272,6 +272,10 @@ Status TabletsChannel::reduce_mem_usage(int64_t mem_limit) { } for (int i = 0; i < counter; i++) { + if (_broken_tablets.find(writers[i]->tablet_id()) != _broken_tablets.end()) { + // skip broken tablets + continue; + } OLAPStatus st = writers[i]->wait_flush(); if (st != OLAP_SUCCESS) { return Status::InternalError(fmt::format("failed to reduce mem consumption by flushing memtable. err: {}", st)); diff --git a/be/src/service/doris_main.cpp b/be/src/service/doris_main.cpp index 9150a16343..96ef574261 100644 --- a/be/src/service/doris_main.cpp +++ b/be/src/service/doris_main.cpp @@ -458,12 +458,20 @@ int main(int argc, char** argv) { #endif #if !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && !defined(THREAD_SANITIZER) - doris::MemInfo::refresh_current_mem(); + doris::MemInfo::refresh_allocator_mem(); #endif doris::PerfCounters::refresh_proc_status(); + int64_t allocator_cache_mem_diff = + doris::MemInfo::allocator_cache_mem() - + doris::ExecEnv::GetInstance()->allocator_cache_mem_tracker()->consumption(); + doris::ExecEnv::GetInstance()->allocator_cache_mem_tracker()->consume( + allocator_cache_mem_diff); + CONSUME_THREAD_MEM_TRACKER(allocator_cache_mem_diff); // 1s clear the expired task mem tracker, a query mem tracker is about 57 bytes. doris::ExecEnv::GetInstance()->task_pool_mem_tracker_registry()->logout_task_mem_tracker(); + // The process tracker print log usage interval is 1s to avoid a large number of tasks being + // canceled when the process exceeds the mem limit, resulting in too many duplicate logs. doris::ExecEnv::GetInstance()->process_mem_tracker_raw()->enable_print_log_usage(); sleep(1); } diff --git a/be/src/util/mem_info.cpp b/be/src/util/mem_info.cpp index 3a8c6f494b..fe4e4e8f10 100644 --- a/be/src/util/mem_info.cpp +++ b/be/src/util/mem_info.cpp @@ -37,7 +37,15 @@ namespace doris { bool MemInfo::_s_initialized = false; int64_t MemInfo::_s_physical_mem = -1; int64_t MemInfo::_s_mem_limit = -1; -size_t MemInfo::_s_current_mem = 0; +std::string MemInfo::_s_mem_limit_str = ""; +int64_t MemInfo::_s_hard_mem_limit = -1; +size_t MemInfo::_s_allocator_physical_mem = 0; +size_t MemInfo::_s_tcmalloc_pageheap_free_bytes = 0; +size_t MemInfo::_s_tcmalloc_central_bytes = 0; +size_t MemInfo::_s_tcmalloc_transfer_bytes = 0; +size_t MemInfo::_s_tcmalloc_thread_bytes = 0; +size_t MemInfo::_s_allocator_cache_mem = 0; +std::string MemInfo::_s_allocator_cache_mem_str = ""; void MemInfo::init() { // Read from /proc/meminfo @@ -85,6 +93,8 @@ void MemInfo::init() { bool is_percent = true; _s_mem_limit = ParseUtil::parse_mem_spec(config::mem_limit, -1, _s_physical_mem, &is_percent); + _s_mem_limit_str = PrettyPrinter::print(_s_mem_limit, TUnit::BYTES); + _s_hard_mem_limit = _s_physical_mem - std::min(209715200L, _s_physical_mem / 10); // 200M LOG(INFO) << "Physical Memory: " << PrettyPrinter::print(_s_physical_mem, TUnit::BYTES); _s_initialized = true; @@ -97,7 +107,8 @@ std::string MemInfo::debug_string() { stream << "Physical Memory: " << PrettyPrinter::print(_s_physical_mem, TUnit::BYTES) << std::endl; stream << "Memory Limt: " << PrettyPrinter::print(_s_mem_limit, TUnit::BYTES) << std::endl; - stream << "Current Usage: " << PrettyPrinter::print(_s_current_mem, TUnit::BYTES) << std::endl; + stream << "Current Usage: " << PrettyPrinter::print(_s_allocator_physical_mem, TUnit::BYTES) + << std::endl; stream << "CGroup Info: " << util.debug_string() << std::endl; return stream.str(); } diff --git a/be/src/util/mem_info.h b/be/src/util/mem_info.h index 53972ee925..d07f608f4e 100644 --- a/be/src/util/mem_info.h +++ b/be/src/util/mem_info.h @@ -23,12 +23,13 @@ #include <string> #include "common/logging.h" +#include "util/pretty_printer.h" namespace doris { // Provides the amount of physical memory available. // Populated from /proc/meminfo. -// TODO: Combine mem-info, cpu-info and disk-info into hardware-info? +// TODO: Combine mem-info, cpu-info and disk-info into hardware-info/perf_counters ? class MemInfo { public: // Initialize MemInfo. @@ -40,19 +41,37 @@ public: return _s_physical_mem; } - static inline size_t current_mem() { return _s_current_mem; } + static inline size_t current_mem() { return _s_allocator_physical_mem; } + static inline size_t allocator_cache_mem() { return _s_allocator_cache_mem; } + static inline std::string allocator_cache_mem_str() { return _s_allocator_cache_mem_str; } // Tcmalloc property `generic.total_physical_bytes` records the total length of the virtual memory // obtained by the process malloc, not the physical memory actually used by the process in the OS. - static inline void refresh_current_mem() { + static inline void refresh_allocator_mem() { MallocExtension::instance()->GetNumericProperty("generic.total_physical_bytes", - &_s_current_mem); + &_s_allocator_physical_mem); + MallocExtension::instance()->GetNumericProperty("tcmalloc.pageheap_free_bytes", + &_s_tcmalloc_pageheap_free_bytes); + MallocExtension::instance()->GetNumericProperty("tcmalloc.central_cache_free_bytes", + &_s_tcmalloc_central_bytes); + MallocExtension::instance()->GetNumericProperty("tcmalloc.transfer_cache_free_bytes", + &_s_tcmalloc_transfer_bytes); + MallocExtension::instance()->GetNumericProperty("tcmalloc.thread_cache_free_bytes", + &_s_tcmalloc_thread_bytes); + _s_allocator_cache_mem = _s_tcmalloc_pageheap_free_bytes + _s_tcmalloc_central_bytes + + _s_tcmalloc_transfer_bytes + _s_tcmalloc_thread_bytes; + _s_allocator_cache_mem_str = PrettyPrinter::print(_s_allocator_cache_mem, TUnit::BYTES); } static inline int64_t mem_limit() { DCHECK(_s_initialized); return _s_mem_limit; } + static inline std::string mem_limit_str() { + DCHECK(_s_initialized); + return _s_mem_limit_str; + } + static inline int64_t hard_mem_limit() { return _s_hard_mem_limit; } static std::string debug_string(); @@ -60,7 +79,15 @@ private: static bool _s_initialized; static int64_t _s_physical_mem; static int64_t _s_mem_limit; - static size_t _s_current_mem; + static std::string _s_mem_limit_str; + static int64_t _s_hard_mem_limit; + static size_t _s_allocator_physical_mem; + static size_t _s_tcmalloc_pageheap_free_bytes; + static size_t _s_tcmalloc_central_bytes; + static size_t _s_tcmalloc_transfer_bytes; + static size_t _s_tcmalloc_thread_bytes; + static size_t _s_allocator_cache_mem; + static std::string _s_allocator_cache_mem_str; }; } // namespace doris diff --git a/be/src/util/perf_counters.cpp b/be/src/util/perf_counters.cpp index fc62255f9d..d82c6c4afd 100644 --- a/be/src/util/perf_counters.cpp +++ b/be/src/util/perf_counters.cpp @@ -43,6 +43,9 @@ namespace doris { static std::unordered_map<std::string, std::string> _process_state; +int64_t PerfCounters::_vm_rss = 0; +std::string PerfCounters::_vm_rss_str = ""; + // This is the order of the counters in /proc/self/io enum PERF_IO_IDX { PROC_IO_READ = 0, @@ -572,6 +575,9 @@ void PerfCounters::refresh_proc_status() { } if (statusinfo.is_open()) statusinfo.close(); + + _vm_rss = parse_bytes("status/VmRSS"); + _vm_rss_str = PrettyPrinter::print(_vm_rss, TUnit::BYTES); } void PerfCounters::get_proc_status(ProcStatus* out) { diff --git a/be/src/util/perf_counters.h b/be/src/util/perf_counters.h index ca130e511c..89ff4c64d3 100644 --- a/be/src/util/perf_counters.h +++ b/be/src/util/perf_counters.h @@ -113,7 +113,8 @@ public: static void refresh_proc_status(); static void get_proc_status(ProcStatus* out); // Return the process actual physical memory in bytes. - static inline int64_t get_vm_rss() { return parse_bytes("status/VmRSS"); } + static inline int64_t get_vm_rss() { return _vm_rss; } + static inline std::string get_vm_rss_str() { return _vm_rss_str; } private: // Copy constructor and assignment not allowed @@ -157,6 +158,9 @@ private: // System perf counters can be grouped together. The OS will update all grouped counters // at the same time. This is useful to better correlate counter values. int _group_fd; + + static int64_t _vm_rss; + static std::string _vm_rss_str; }; } // namespace doris diff --git a/be/src/util/system_metrics.cpp b/be/src/util/system_metrics.cpp index 1cdb50a563..df24281952 100644 --- a/be/src/util/system_metrics.cpp +++ b/be/src/util/system_metrics.cpp @@ -25,6 +25,7 @@ #include "gutil/strtoint.h" // for atoi64 #include "util/doris_metrics.h" #include "util/mem_info.h" +#include "util/perf_counters.h" namespace doris { @@ -313,7 +314,7 @@ void SystemMetrics::_install_memory_metrics(MetricEntity* entity) { } void SystemMetrics::_update_memory_metrics() { - _memory_metrics->memory_allocated_bytes->set_value(MemInfo::current_mem()); + _memory_metrics->memory_allocated_bytes->set_value(PerfCounters::get_vm_rss()); } void SystemMetrics::_install_disk_metrics(const std::set<std::string>& disk_devices) { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org