fix: memory leak when switching profiles

This commit is contained in:
Tunglies 2026-01-28 02:31:30 +08:00
parent c57a962109
commit bd338cb5de
No known key found for this signature in database
GPG Key ID: B9B01B389469B3E8
4 changed files with 75 additions and 50 deletions

View File

@ -6,6 +6,7 @@
- 修复无网络时无限请求 IP 归属查询 - 修复无网络时无限请求 IP 归属查询
- 修复 WebDAV 页面重试逻辑 - 修复 WebDAV 页面重试逻辑
- 修复 Linux 通过 GUI 安装服务模式权限不符合预期 - 修复 Linux 通过 GUI 安装服务模式权限不符合预期
- 修复切换订阅所导致的内存持续增长
<details> <details>
<summary><strong> ✨ 新增功能 </strong></summary> <summary><strong> ✨ 新增功能 </strong></summary>

View File

@ -189,6 +189,10 @@ impl Config {
pub async fn generate() -> Result<()> { pub async fn generate() -> Result<()> {
let (config, exists_keys, logs) = enhance::enhance().await; let (config, exists_keys, logs) = enhance::enhance().await;
Self::runtime().await.edit_draft(|d| {
d.clean_all();
});
Self::runtime().await.edit_draft(|d| { Self::runtime().await.edit_draft(|d| {
*d = IRuntime { *d = IRuntime {
config: Some(config), config: Some(config),

View File

@ -22,6 +22,13 @@ impl IRuntime {
Self::default() Self::default()
} }
#[inline]
pub fn clean_all(&mut self) {
self.config = None;
self.exists_keys.clear();
self.chain_logs.clear();
}
// 这里只更改 allow-lan | ipv6 | log-level | tun // 这里只更改 allow-lan | ipv6 | log-level | tun
#[inline] #[inline]
pub fn patch_config(&mut self, patch: &Mapping) { pub fn patch_config(&mut self, patch: &Mapping) {

View File

@ -13,6 +13,7 @@ use self::{
seq::{SeqMap, use_seq}, seq::{SeqMap, use_seq},
tun::use_tun, tun::use_tun,
}; };
use crate::process::AsyncHandler;
use crate::utils::dirs; use crate::utils::dirs;
use crate::{config::Config, utils::tmpl}; use crate::{config::Config, utils::tmpl};
use crate::{config::IVerge, constants}; use crate::{config::IVerge, constants};
@ -33,6 +34,7 @@ struct ConfigValues {
socks_enabled: bool, socks_enabled: bool,
http_enabled: bool, http_enabled: bool,
enable_dns_settings: bool, enable_dns_settings: bool,
enable_external_controller: bool,
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
redir_enabled: bool, redir_enabled: bool,
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -108,13 +110,22 @@ async fn get_config_values() -> ConfigValues {
.. ..
} = *verge_arc; } = *verge_arc;
let (clash_core, enable_tun, enable_builtin, socks_enabled, http_enabled, enable_dns_settings) = ( let (
clash_core,
enable_tun,
enable_builtin,
socks_enabled,
http_enabled,
enable_dns_settings,
enable_external_controller,
) = (
Some(verge_arc.get_valid_clash_core()), Some(verge_arc.get_valid_clash_core()),
enable_tun_mode.unwrap_or(false), enable_tun_mode.unwrap_or(false),
enable_builtin_enhanced.unwrap_or(true), enable_builtin_enhanced.unwrap_or(true),
verge_socks_enabled.unwrap_or(false), verge_socks_enabled.unwrap_or(false),
verge_http_enabled.unwrap_or(false), verge_http_enabled.unwrap_or(false),
enable_dns_settings.unwrap_or(false), enable_dns_settings.unwrap_or(false),
verge_arc.enable_external_controller.unwrap_or(false),
); );
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
@ -134,6 +145,7 @@ async fn get_config_values() -> ConfigValues {
socks_enabled, socks_enabled,
http_enabled, http_enabled,
enable_dns_settings, enable_dns_settings,
enable_external_controller,
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
redir_enabled, redir_enabled,
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -378,11 +390,12 @@ fn process_profile_items(
(config, exists_keys, result_map) (config, exists_keys, result_map)
} }
async fn merge_default_config( fn merge_default_config(
mut config: Mapping, mut config: Mapping,
clash_config: Mapping, clash_config: Mapping,
socks_enabled: bool, socks_enabled: bool,
http_enabled: bool, http_enabled: bool,
enable_external_controller: bool,
#[cfg(not(target_os = "windows"))] redir_enabled: bool, #[cfg(not(target_os = "windows"))] redir_enabled: bool,
#[cfg(target_os = "linux")] tproxy_enabled: bool, #[cfg(target_os = "linux")] tproxy_enabled: bool,
) -> Mapping { ) -> Mapping {
@ -433,19 +446,8 @@ async fn merge_default_config(
} }
} }
// 处理 external-controller 键的开关逻辑 // 处理 external-controller 键的开关逻辑
if key.as_str() == Some("external-controller") { if key.as_str() == Some("external-controller") && !enable_external_controller {
let enable_external_controller = Config::verge() config.insert(key, "".into());
.await
.latest_arc()
.enable_external_controller
.unwrap_or(false);
if enable_external_controller {
config.insert(key, value);
} else {
// 如果禁用了外部控制器,设置为空字符串
config.insert(key, "".into());
}
} else { } else {
config.insert(key, value); config.insert(key, value);
} }
@ -602,6 +604,7 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
socks_enabled, socks_enabled,
http_enabled, http_enabled,
enable_dns_settings, enable_dns_settings,
enable_external_controller,
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
redir_enabled, redir_enabled,
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -620,48 +623,58 @@ pub async fn enhance() -> (Mapping, HashSet<String>, HashMap<String, ResultLog>)
let global_script = profile.global_script; let global_script = profile.global_script;
let profile_name = profile.profile_name; let profile_name = profile.profile_name;
// process globals let (config, exists_keys_set, result_map) = AsyncHandler::spawn_blocking(move || {
let (config, exists_keys, result_map) = process_global_items(config, global_merge, global_script, &profile_name); // process globals
let (config, exists_keys, result_map) =
process_global_items(config, global_merge, global_script, &profile_name);
// process profile-specific items // process profile-specific items
let (config, exists_keys, result_map) = process_profile_items( let (config, exists_keys, result_map) = process_profile_items(
config, config,
exists_keys, exists_keys,
result_map, result_map,
rules_item, rules_item,
proxies_item, proxies_item,
groups_item, groups_item,
merge_item, merge_item,
script_item, script_item,
&profile_name, &profile_name,
); );
// merge default clash config // merge default clash config
let config = merge_default_config( let config = merge_default_config(
config, config,
clash_config, clash_config,
socks_enabled, socks_enabled,
http_enabled, http_enabled,
#[cfg(not(target_os = "windows"))] enable_external_controller,
redir_enabled, #[cfg(not(target_os = "windows"))]
#[cfg(target_os = "linux")] redir_enabled,
tproxy_enabled, #[cfg(target_os = "linux")]
) tproxy_enabled,
.await; );
// builtin scripts // builtin scripts
let mut config = apply_builtin_scripts(config, clash_core, enable_builtin); let mut config = apply_builtin_scripts(config, clash_core.clone(), enable_builtin);
config = cleanup_proxy_groups(config); config = cleanup_proxy_groups(config);
config = use_tun(config, enable_tun); config = use_tun(config, enable_tun);
config = use_sort(config); config = use_sort(config);
let mut exists_keys_set = HashSet::new();
exists_keys_set.extend(exists_keys);
(config, exists_keys_set, result_map)
})
.await
.unwrap_or_else(|e| {
logging!(error, Type::Core, "enhance task join error: {}", e);
(Mapping::new(), HashSet::new(), HashMap::new())
});
// dns settings // dns settings
config = apply_dns_settings(config, enable_dns_settings).await; let config = apply_dns_settings(config, enable_dns_settings).await;
let mut exists_keys_set = HashSet::new();
exists_keys_set.extend(exists_keys);
(config, exists_keys_set, result_map) (config, exists_keys_set, result_map)
} }