Skip to content
6 changes: 6 additions & 0 deletions PCL.Core/App/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,11 @@ partial class HideConfigGroup
/// </summary>
[ConfigItem<int>("LaunchRamCustom", 15, ConfigSource.Local)] public partial int CustomMemorySize { get; set; }

/// <summary>
/// 自定义初始堆大小(-Xms)的滑块刻度值,0 表示不主动设置。仅在手动分配模式下生效,且实际值不会超过最大堆。
/// </summary>
[ConfigItem<int>("LaunchRamCustomInitial", 0, ConfigSource.Local)] public partial int CustomInitialMemorySize { get; set; }

/// <summary>
/// 是否固定堆大小:启用后额外追加 -Xms 并使其等于 -Xmx,隐式禁用内存归还以降低延迟抖动、利于 ZGC。见 #3282。
/// </summary>
Expand Down Expand Up @@ -619,6 +624,7 @@ partial class HideConfigGroup
[ConfigItem<bool>("VersionUseDebugLog4j2Config", false)] public partial ArgConfig<bool> UseDebugLof4j2Config { get; }
[ConfigItem<int>("VersionRamType", 2)] public partial ArgConfig<int> MemorySolution { get; }
[ConfigItem<int>("VersionRamCustom", 15)] public partial ArgConfig<int> CustomMemorySize { get; }
[ConfigItem<int>("VersionRamCustomInitial", 0)] public partial ArgConfig<int> CustomInitialMemorySize { get; }
[ConfigItem<string>("VersionArgumentTitle", "")] public partial ArgConfig<string> Title { get; }
[ConfigItem<bool>("VersionArgumentTitleEmpty", false)] public partial ArgConfig<bool> UseGlobalTitle { get; }
[ConfigItem<string>("VersionArgumentInfo", "")] public partial ArgConfig<string> TypeInfo { get; }
Expand Down
5 changes: 5 additions & 0 deletions PCL.Core/App/Localization/Languages/en-GB.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,11 @@
<sys:String x:Key="Setup.Launch.Memory.Auto.ToolTip">Dynamically adjust the memory allocated to the game based on the installed mods and available system memory.</sys:String>
<sys:String x:Key="Setup.Launch.Memory.AvailableSuffix">Available: {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.GameAllocation">Game allocation</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max">Maximum memory (-Xmx)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max.ToolTip">The maximum heap memory the game can use</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial">Initial memory (-Xms)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial.ToolTip">The heap memory pre-allocated when the game starts, which can be lower than the maximum heap (-Xmx). Set to 0 to let the JVM decide. Only takes effect when memory allocation is set to Custom, and has no effect when Lock memory allocation is enabled.</sys:String>
<sys:String x:Key="Setup.Launch.Memory.InitialSuffix">initial {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Title">Game memory</sys:String>
<sys:String x:Key="Setup.Launch.Memory.UsedTotal">Used / installed memory</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Warning.Java32Bit">32-bit Java can only allocate up to 1 GiB of memory. It is strongly recommended to use 64-bit Java!</sys:String>
Expand Down
5 changes: 5 additions & 0 deletions PCL.Core/App/Localization/Languages/en-US.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,11 @@
<sys:String x:Key="Setup.Launch.Memory.Auto.ToolTip">Dynamically adjust the memory allocated to the game based on the installed mods and available system memory.</sys:String>
<sys:String x:Key="Setup.Launch.Memory.AvailableSuffix">Available: {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.GameAllocation">Game allocation</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max">Maximum memory (-Xmx)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max.ToolTip">The maximum heap memory the game can use</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial">Initial memory (-Xms)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial.ToolTip">The heap memory pre-allocated when the game starts, which can be lower than the maximum heap (-Xmx). Set to 0 to let the JVM decide. Only takes effect when memory allocation is set to Custom, and has no effect when Lock memory allocation is enabled.</sys:String>
<sys:String x:Key="Setup.Launch.Memory.InitialSuffix">initial {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Title">Game memory</sys:String>
<sys:String x:Key="Setup.Launch.Memory.UsedTotal">Used / installed memory</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Warning.Java32Bit">32-bit Java can only allocate up to 1 GiB of memory. It is strongly recommended to use 64-bit Java!</sys:String>
Expand Down
5 changes: 5 additions & 0 deletions PCL.Core/App/Localization/Languages/zh-CN.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,11 @@
<sys:String x:Key="Setup.Launch.Memory.Auto.ToolTip">根据安装的模组量与电脑剩余内存,动态调整为游戏分配的内存</sys:String>
<sys:String x:Key="Setup.Launch.Memory.AvailableSuffix">可用 {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.GameAllocation">游戏分配</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max">最大内存(-Xmx)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max.ToolTip">游戏可使用的最大堆内存大小</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial">初始内存(-Xms)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial.ToolTip">游戏启动时预先分配的堆内存大小,可以低于最大堆(-Xmx)。设为 0 表示不主动设置,由 JVM 自行决定。仅在手动配置内存分配时生效,启用「锁定内存分配」时不生效。</sys:String>
<sys:String x:Key="Setup.Launch.Memory.InitialSuffix">初始 {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Title">游戏内存</sys:String>
<sys:String x:Key="Setup.Launch.Memory.UsedTotal">已使用内存 / 已安装内存</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Warning.Java32Bit">32 位 Java 只能分配最多 1 GiB 的内存。强烈建议换用 64 位的 Java!</sys:String>
Expand Down
5 changes: 5 additions & 0 deletions PCL.Core/App/Localization/Languages/zh-TW.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,11 @@
<sys:String x:Key="Setup.Launch.Memory.Auto.ToolTip">根據安裝的模組量與電腦剩餘記憶體,動態調整為遊戲分配的記憶體</sys:String>
<sys:String x:Key="Setup.Launch.Memory.AvailableSuffix">可用 {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.GameAllocation">遊戲分配</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max">最大記憶體(-Xmx)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Max.ToolTip">遊戲可使用的最大堆積記憶體大小</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial">初始記憶體(-Xms)</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Initial.ToolTip">遊戲啟動時預先分配的堆積記憶體大小,可以低於最大堆(-Xmx)。設為 0 表示不主動設定,由 JVM 自行決定。僅在手動配置記憶體分配時生效,啟用「鎖定記憶體分配」時不生效。</sys:String>
<sys:String x:Key="Setup.Launch.Memory.InitialSuffix">初始 {0} GiB</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Title">遊戲記憶體</sys:String>
<sys:String x:Key="Setup.Launch.Memory.UsedTotal">已使用記憶體 / 已安裝記憶體</sys:String>
<sys:String x:Key="Setup.Launch.Memory.Warning.Java32Bit">32 位元 Java 只能分配最多 1 GiB 的記憶體。強烈建議換用 64 位元的 Java!</sys:String>
Expand Down
37 changes: 31 additions & 6 deletions Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2135,10 +2135,22 @@ internal static void SecretLaunchJvmArgs(ref List<string> dataList)
var maxHeapArg = Math.Floor(totalRamMb).ToString(CultureInfo.InvariantCulture);
dataList.Add("-Xmn" + Math.Floor(totalRamMb * 0.15).ToString(CultureInfo.InvariantCulture) + "m");
dataList.Add("-Xmx" + maxHeapArg + "m");
// #3282: 固定堆大小时追加 -Xms 使其等于 -Xmx(复用同一数值以保持一致),隐式禁用内存归还降低延迟抖动、利于 ZGC。
// 若 dataList 中已存在 -Xms(例如用户自定义参数已设)则跳过,避免重复/冲突。
if (Config.Launch.LockMemory && !dataList.Any(d => d.Contains("-Xms", StringComparison.OrdinalIgnoreCase)))
dataList.Add("-Xms" + maxHeapArg + "m");
if (!dataList.Any(d => d.Contains("-Xms", StringComparison.OrdinalIgnoreCase)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply configured Xms over manifest defaults

For instances using the modern arguments.jvm format, McLaunchArgumentsJvmNew populates dataList from the version and inherited JSON before calling this method. If that manifest contains an -Xms default, this guard prevents the newly configured initial-memory value from being added even though HasCustomXms only treats the user's effective custom JVM arguments as an override; the settings UI therefore shows the configured value while launches continue using the manifest value. Distinguish user-supplied overrides from manifest defaults, or replace the manifest's -Xms when applying this setting.

Useful? React with 👍 / 👎.

{
if (Config.Launch.LockMemory)
{
// #3282: 固定堆大小时追加 -Xms 使其等于 -Xmx(复用同一数值以保持一致),隐式禁用内存归还降低延迟抖动、利于 ZGC。
dataList.Add("-Xms" + maxHeapArg + "m");
}
else
{
// #3511: 支持自定义初始堆大小(-Xms),可低于最大堆。
var initialRam = PageInstanceSetup.GetInitialRam(ModInstanceList.McMcInstanceSelected);
if (initialRam.HasValue)
dataList.Add("-Xms" + Math.Floor(initialRam.Value * 1024d).ToString(CultureInfo.InvariantCulture) + "m");
}
}
if (!dataList.Any(d => d.Contains("-Dlog4j2.formatMsgNoLookups=true")))
dataList.Add("-Dlog4j2.formatMsgNoLookups=true");
}
Expand Down Expand Up @@ -2467,10 +2479,23 @@ private static string McLaunchArgumentsJvmOld(McInstance instance)
var maxHeapArg = Math.Floor(PageInstanceSetup.GetRam(ModInstanceList.McMcInstanceSelected,
!mcLaunchJavaSelected.Installation.Is64Bit) * 1024d);
dataList.Add("-Xmx" + maxHeapArg + "m");
// #3282: 固定堆大小时追加 -Xms 使其等于 -Xmx(复用同一数值以保持一致),隐式禁用内存归还降低延迟抖动、利于 ZGC。
// 若 dataList 中已存在 -Xms(例如用户自定义参数已设)则跳过,避免重复/冲突。
if (Config.Launch.LockMemory && !dataList.Any(d => d.Contains("-Xms", StringComparison.OrdinalIgnoreCase)))
dataList.Add("-Xms" + maxHeapArg + "m");
if (!dataList.Any(d => d.Contains("-Xms", StringComparison.OrdinalIgnoreCase)))
{
if (Config.Launch.LockMemory)
{
// #3282: 固定堆大小时追加 -Xms 使其等于 -Xmx(复用同一数值以保持一致),隐式禁用内存归还降低延迟抖动、利于 ZGC。
dataList.Add("-Xms" + maxHeapArg + "m");
}
else
{
// #3511: 支持自定义初始堆大小(-Xms),可低于最大堆。
var initialRam = PageInstanceSetup.GetInitialRam(ModInstanceList.McMcInstanceSelected,
!mcLaunchJavaSelected.Installation.Is64Bit);
if (initialRam.HasValue)
dataList.Add("-Xms" + Math.Floor(initialRam.Value * 1024d).ToString(CultureInfo.InvariantCulture) + "m");
}
}
dataList.Add("\"-Djava.library.path=" + GetNativesFolder() + "\"");
dataList.Add("-cp ${classpath}"); // 把支持库添加进启动参数表

Expand Down
35 changes: 30 additions & 5 deletions Plain Craft Launcher 2/Pages/PageInstance/PageInstanceSetup.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="9" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

Expand All @@ -124,19 +125,26 @@
Width="110"
x:Name="RadioRamType0"
Tag="VersionRamType/0"
Margin="0,0,20,0"
Margin="0,0,20,9"
ToolTip="{DynamicResource Setup.Launch.Memory.Auto.ToolTip}"
ToolTipService.Placement="Right"
ToolTipService.HorizontalOffset="-10"
ToolTipService.VerticalOffset="-3" />

<local:MyRadioBox
Grid.Row="3" Grid.Column="0"
Grid.Row="2" Grid.Column="0"
Text="{DynamicResource Common.Option.Customize}"
Width="110"
x:Name="RadioRamType1"
Tag="VersionRamType/1"
Margin="0,0,20,0" />
Margin="0,0,20,9" />

<TextBlock
Grid.Row="3" Grid.Column="0"
Text="{DynamicResource Setup.Launch.Memory.Max}"
VerticalAlignment="Center"
Margin="0,0,20,9"
ToolTip="{DynamicResource Setup.Launch.Memory.Max.ToolTip}" />

<local:MySlider
Grid.Row="3" Grid.Column="1"
Expand All @@ -145,7 +153,24 @@
Tag="VersionRamCustom"
MaxValue="49"
Value="13"
Margin="0,0,12,0" />
Margin="0,0,12,9" />

<TextBlock
Grid.Row="4" Grid.Column="0"
Text="{DynamicResource Setup.Launch.Memory.Initial}"
VerticalAlignment="Center"
Margin="0,0,20,0"
ToolTip="{DynamicResource Setup.Launch.Memory.Initial.ToolTip}" />

<local:MySlider
Grid.Row="4" Grid.Column="1"
IsEnabled="False"
x:Name="SliderRamInitialCustom"
Tag="VersionRamCustomInitial"
MaxValue="49"
Value="0"
Margin="0,0,12,0"
ToolTip="{DynamicResource Setup.Launch.Memory.Initial.ToolTip}" />
</Grid>
<Grid x:Name="PanRamDisplay" Margin="0,11,2,0" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public PageInstanceSetup()
RadioRamType0.Check += RadioBoxChange;
RadioRamType1.Check += RadioBoxChange;
SliderRamCustom.Change += SliderChange;
SliderRamInitialCustom.Change += SliderChange;

ComboServerLoginRequire.SelectionChanged += ComboServerLogin_Changed;
TextServerAuthServer.TextChanged += TextBoxChange;
Expand Down Expand Up @@ -107,6 +108,7 @@ public void Reload()
var ramType = Config.Instance.MemorySolution[PageInstanceLeft.McInstance.PathInstance];
((MyRadioBox)FindName("RadioRamType" + ramType)).Checked = true;
SliderRamCustom.Value = Config.Instance.CustomMemorySize[PageInstanceLeft.McInstance.PathInstance];
SliderRamInitialCustom.Value = Config.Instance.CustomInitialMemorySize[PageInstanceLeft.McInstance.PathInstance];
RamType(ramType);

// 服务器
Expand Down Expand Up @@ -248,6 +250,8 @@ public void RamType(int type)
if (SliderRamCustom is null)
return;
SliderRamCustom.IsEnabled = type == 1;
// 锁定内存为全局配置,启用时 -Xms 恒等于 -Xmx,初始内存设置不生效
SliderRamInitialCustom.IsEnabled = type == 1 && !Config.Launch.LockMemory;
}

/// <summary>
Expand Down Expand Up @@ -277,7 +281,12 @@ public void RefreshRam(bool showAnim)
else
SliderRamCustom.MaxValue = (int)Math.Round(Math.Floor((ramTotal - 16d) / 2d) + 33d);
// 设置文本
LabRamGame.Text = $"{Lang.Number(ramGame, "N1")} GiB{(ramGame != ramGameActual ? $" ({Lang.Text("Setup.Launch.Memory.AvailableSuffix", Lang.Number(ramGameActual, "N1"))})" : "")}";
var ramInitial = GetInitialRam(PageInstanceLeft.McInstance);
var initialText = ramInitial.HasValue ? Lang.Text("Setup.Launch.Memory.InitialSuffix", Lang.Number(ramInitial.Value, "N1")) : "";
var availableText = ramGame != ramGameActual ? Lang.Text("Setup.Launch.Memory.AvailableSuffix", Lang.Number(ramGameActual, "N1")) : "";
var suffixText = !string.IsNullOrEmpty(initialText) && !string.IsNullOrEmpty(availableText) ? initialText + ", " : initialText;
suffixText += availableText;
LabRamGame.Text = $"{Lang.Number(ramGame, "N1")} GiB{(suffixText.Length > 0 ? $" ({suffixText})" : "")}";
LabRamUsed.Text = $"{Lang.Number(ramUsed, "N1")} GiB";
LabRamTotal.Text = $" / {Lang.Number(ramTotal, "N1")} GiB";
LabRamWarn.Visibility =
Expand Down Expand Up @@ -524,14 +533,7 @@ public static double GetRam(McInstance version, bool? is32BitJava = default)
{
// 手动配置
var value = Config.Instance.CustomMemorySize[instancePath];
if (value <= 12)
ramGive = value * 0.1d + 0.3d;
else if (value <= 25)
ramGive = (value - 12) * 0.5d + 1.5d;
else if (value <= 33)
ramGive = (value - 25) * 1 + 8;
else
ramGive = (value - 33) * 2 + 16;
ramGive = PageSetupLaunch.GetRamFromTick(value);
}

// 若使用 32 位 Java,则限制为 1G
Expand All @@ -540,6 +542,32 @@ public static double GetRam(McInstance version, bool? is32BitJava = default)
return ramGive;
}

/// <summary>
/// 获取当前设置的初始堆大小(-Xms)。单位为 GB;未启用自定义初始大小时返回 null。
/// </summary>
public static double? GetInitialRam(McInstance version, bool? is32BitJava = default)
{
// 锁定内存为全局配置,启用时 -Xms 恒等于 -Xmx,由锁定内存逻辑处理
if (Config.Launch.LockMemory)
return null;
// 自定义 JVM 参数含 -Xms 时由其决定实际初始堆,不再显示滑块值
if (PageSetupLaunch.HasCustomXms(version))
return null;
var instancePath = version?.PathInstance;
switch (Config.Instance.MemorySolution[instancePath])
{
case 2:
return PageSetupLaunch.GetInitialRam(version, true, is32BitJava);
case 1:
var initialTick = Config.Instance.CustomInitialMemorySize[instancePath];
if (initialTick <= 0)
return null;
return Math.Min(PageSetupLaunch.GetRamFromTick(initialTick), GetRam(version, is32BitJava));
default:
return null;
}
}

#endregion

#region 服务器
Expand Down
Loading
Loading