feat: new memory settings
This commit is contained in:
@@ -296,7 +296,11 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
.setProfileName(Metadata.TITLE)
|
||||
.setGameArguments(StringUtils.tokenize(vs.getMinecraftArgs()))
|
||||
.setJavaArguments(StringUtils.tokenize(vs.getJavaArgs()))
|
||||
.setMaxMemory(vs.getMaxMemory())
|
||||
.setMaxMemory((int)(getAllocatedMemory(
|
||||
vs.getMaxMemory(),
|
||||
OperatingSystem.getPhysicalMemoryStatus().orElse(OperatingSystem.PhysicalMemoryStatus.INVALID).getAvailable(),
|
||||
vs.isAutoMemory()
|
||||
) / 1024 / 1024))
|
||||
.setMinMemory(vs.getMinMemory())
|
||||
.setMetaspace(Lang.toIntOrNull(vs.getPermSize()))
|
||||
.setWidth(vs.getWidth())
|
||||
@@ -400,4 +404,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
return versions.containsKey(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static long getAllocatedMemory(long minimum, long available, boolean auto) {
|
||||
return auto ? Math.max(minimum, (long) (available * 0.8)) : minimum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +237,20 @@ public final class VersionSetting implements Cloneable {
|
||||
minMemoryProperty.set(minMemory);
|
||||
}
|
||||
|
||||
private final BooleanProperty autoMemory = new SimpleBooleanProperty(this, "autoMemory", true);
|
||||
|
||||
public boolean isAutoMemory() {
|
||||
return autoMemory.get();
|
||||
}
|
||||
|
||||
public BooleanProperty autoMemoryProperty() {
|
||||
return autoMemory;
|
||||
}
|
||||
|
||||
public void setAutoMemory(boolean autoMemory) {
|
||||
this.autoMemory.set(autoMemory);
|
||||
}
|
||||
|
||||
private final StringProperty preLaunchCommandProperty = new SimpleStringProperty(this, "precalledCommand", "");
|
||||
|
||||
public StringProperty preLaunchCommandProperty() {
|
||||
@@ -558,6 +572,7 @@ public final class VersionSetting implements Cloneable {
|
||||
permSizeProperty.addListener(listener);
|
||||
maxMemoryProperty.addListener(listener);
|
||||
minMemoryProperty.addListener(listener);
|
||||
autoMemory.addListener(listener);
|
||||
preLaunchCommandProperty.addListener(listener);
|
||||
javaArgsProperty.addListener(listener);
|
||||
minecraftArgsProperty.addListener(listener);
|
||||
@@ -589,6 +604,7 @@ public final class VersionSetting implements Cloneable {
|
||||
versionSetting.setPermSize(getPermSize());
|
||||
versionSetting.setMaxMemory(getMaxMemory());
|
||||
versionSetting.setMinMemory(getMinMemory());
|
||||
versionSetting.setAutoMemory(isAutoMemory());
|
||||
versionSetting.setPreLaunchCommand(getPreLaunchCommand());
|
||||
versionSetting.setJavaArgs(getJavaArgs());
|
||||
versionSetting.setMinecraftArgs(getMinecraftArgs());
|
||||
@@ -619,6 +635,7 @@ public final class VersionSetting implements Cloneable {
|
||||
obj.addProperty("minecraftArgs", src.getMinecraftArgs());
|
||||
obj.addProperty("maxMemory", src.getMaxMemory() <= 0 ? OperatingSystem.SUGGESTED_MEMORY : src.getMaxMemory());
|
||||
obj.addProperty("minMemory", src.getMinMemory());
|
||||
obj.addProperty("autoMemory", src.isAutoMemory());
|
||||
obj.addProperty("permSize", src.getPermSize());
|
||||
obj.addProperty("width", src.getWidth());
|
||||
obj.addProperty("height", src.getHeight());
|
||||
@@ -659,6 +676,7 @@ public final class VersionSetting implements Cloneable {
|
||||
vs.setMinecraftArgs(Optional.ofNullable(obj.get("minecraftArgs")).map(JsonElement::getAsString).orElse(""));
|
||||
vs.setMaxMemory(maxMemoryN);
|
||||
vs.setMinMemory(Optional.ofNullable(obj.get("minMemory")).map(JsonElement::getAsInt).orElse(null));
|
||||
vs.setAutoMemory(Optional.ofNullable(obj.get("autoMemory")).map(JsonElement::getAsBoolean).orElse(true));
|
||||
vs.setPermSize(Optional.ofNullable(obj.get("permSize")).map(JsonElement::getAsString).orElse(""));
|
||||
vs.setWidth(Optional.ofNullable(obj.get("width")).map(JsonElement::getAsJsonPrimitive).map(this::parseJsonPrimitive).orElse(0));
|
||||
vs.setHeight(Optional.ofNullable(obj.get("height")).map(JsonElement::getAsJsonPrimitive).map(this::parseJsonPrimitive).orElse(0));
|
||||
|
||||
@@ -21,20 +21,17 @@ import com.jfoenix.controls.*;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.game.GameDirectoryType;
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||
import org.jackhuang.hmcl.game.NativesDirectoryType;
|
||||
import org.jackhuang.hmcl.game.ProcessPriority;
|
||||
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
||||
@@ -47,8 +44,10 @@ import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
@@ -58,6 +57,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -76,7 +76,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
private final VBox rootPane;
|
||||
private final JFXTextField txtWidth;
|
||||
private final JFXTextField txtHeight;
|
||||
private final JFXTextField txtMaxMemory;
|
||||
private final JFXTextField txtJVMArgs;
|
||||
private final JFXTextField txtGameArgs;
|
||||
private final JFXTextField txtMetaspace;
|
||||
@@ -87,8 +86,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
private final ComponentList componentList;
|
||||
private final ComponentList iconPickerItemWrapper;
|
||||
private final JFXComboBox<LauncherVisibility> cboLauncherVisibility;
|
||||
private final JFXCheckBox chkAutoAllocate;
|
||||
private final JFXCheckBox chkFullscreen;
|
||||
private final Label lblPhysicalMemory;
|
||||
private final JFXToggleButton chkNoJVMArgs;
|
||||
private final JFXToggleButton chkNoGameCheck;
|
||||
private final JFXToggleButton chkNoJVMCheck;
|
||||
@@ -105,6 +104,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
|
||||
private final InvalidationListener javaListener = any -> initJavaSubtitle();
|
||||
|
||||
private boolean uiVisible = false;
|
||||
private final IntegerProperty maxMemoryProperty = new SimpleIntegerProperty();
|
||||
private final ObjectProperty<OperatingSystem.PhysicalMemoryStatus> memoryStatusProperty = new SimpleObjectProperty<>(OperatingSystem.PhysicalMemoryStatus.INVALID);
|
||||
|
||||
public VersionSettingsPage() {
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
scrollPane.setFitToHeight(true);
|
||||
@@ -163,21 +166,120 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
gameDirItem.setCustomText(i18n("settings.custom"));
|
||||
gameDirItem.setDirectory(true);
|
||||
|
||||
BorderPane maxMemoryPane = new BorderPane();
|
||||
VBox maxMemoryPane = new VBox(8);
|
||||
{
|
||||
VBox vbox = new VBox();
|
||||
maxMemoryPane.setLeft(vbox);
|
||||
Label maxMemoryLabel = new Label(i18n("settings.max_memory"));
|
||||
lblPhysicalMemory = new Label();
|
||||
lblPhysicalMemory.getStyleClass().add("subtitle-label");
|
||||
vbox.getChildren().setAll(maxMemoryLabel, lblPhysicalMemory);
|
||||
Label title = new Label(i18n("settings.memory"));
|
||||
VBox.setMargin(title, new Insets(0, 0, 8, 0));
|
||||
|
||||
txtMaxMemory = new JFXTextField();
|
||||
maxMemoryPane.setRight(txtMaxMemory);
|
||||
BorderPane.setAlignment(txtMaxMemory, Pos.CENTER_RIGHT);
|
||||
FXUtils.setValidateWhileTextChanged(txtMaxMemory, true);
|
||||
FXUtils.setLimitWidth(txtMaxMemory, 300);
|
||||
txtMaxMemory.setValidators(new NumberValidator(i18n("input.number"), false));
|
||||
chkAutoAllocate = new JFXCheckBox(i18n("settings.memory.auto_allocate"));
|
||||
VBox.setMargin(chkAutoAllocate, new Insets(0, 0, 8, 5));
|
||||
|
||||
HBox lowerBoundPane = new HBox();
|
||||
lowerBoundPane.setAlignment(Pos.CENTER);
|
||||
VBox.setMargin(lowerBoundPane, new Insets(8, 0, 0, 16));
|
||||
{
|
||||
Label label = new Label();
|
||||
label.textProperty().bind(Bindings.createStringBinding(() -> {
|
||||
if (chkAutoAllocate.isSelected()) {
|
||||
return i18n("settings.memory.lower_bound");
|
||||
} else {
|
||||
return i18n("settings.memory");
|
||||
}
|
||||
}, chkAutoAllocate.selectedProperty()));
|
||||
|
||||
JFXSlider slider = new JFXSlider(0, 1, 0);
|
||||
HBox.setMargin(slider, new Insets(0, 16, 0, 16));
|
||||
HBox.setHgrow(slider, Priority.ALWAYS);
|
||||
slider.setValueFactory(self -> Bindings.createStringBinding(() -> (int)(self.getValue() * 100) + "%", self.valueProperty()));
|
||||
AtomicBoolean changedByTextField = new AtomicBoolean(false);
|
||||
FXUtils.onChangeAndOperate(maxMemoryProperty, maxMemory -> {
|
||||
changedByTextField.set(true);
|
||||
slider.setValue(maxMemory.intValue() * 1.0 / OperatingSystem.TOTAL_MEMORY);
|
||||
changedByTextField.set(false);
|
||||
});
|
||||
slider.valueProperty().addListener((value, oldVal, newVal) -> {
|
||||
if (changedByTextField.get()) return;
|
||||
maxMemoryProperty.set((int)(value.getValue().doubleValue() * OperatingSystem.TOTAL_MEMORY));
|
||||
});
|
||||
|
||||
JFXTextField txtMaxMemory = new JFXTextField();
|
||||
FXUtils.setLimitWidth(txtMaxMemory, 60);
|
||||
FXUtils.setValidateWhileTextChanged(txtMaxMemory, true);
|
||||
txtMaxMemory.textProperty().bindBidirectional(maxMemoryProperty, SafeStringConverter.fromInteger());
|
||||
txtMaxMemory.setValidators(new NumberValidator(i18n("input.number"), false));
|
||||
|
||||
lowerBoundPane.getChildren().setAll(label, slider, txtMaxMemory, new Label("MB"));
|
||||
}
|
||||
|
||||
BorderPane titlePane = new BorderPane();
|
||||
VBox.setMargin(titlePane, new Insets(0, 0, 0, 16));
|
||||
{
|
||||
Label left = new Label(i18n("settings.memory.used_per_total"));
|
||||
left.getStyleClass().add("subtitle-label");
|
||||
titlePane.setLeft(left);
|
||||
|
||||
Label right = new Label();
|
||||
right.textProperty().bind(Bindings.createStringBinding(() -> {
|
||||
if (chkAutoAllocate.isSelected()) {
|
||||
return i18n("settings.memory.allocate.auto");
|
||||
} else {
|
||||
return i18n("settings.memory.allocate.manual");
|
||||
}
|
||||
}, chkAutoAllocate.selectedProperty()));
|
||||
right.getStyleClass().add("subtitle-label");
|
||||
titlePane.setRight(right);
|
||||
}
|
||||
|
||||
StackPane progressBarPane = new StackPane();
|
||||
progressBarPane.setAlignment(Pos.CENTER_LEFT);
|
||||
VBox.setMargin(progressBarPane, new Insets(0, 0, 0, 16));
|
||||
{
|
||||
progressBarPane.setMinHeight(4);
|
||||
progressBarPane.getStyleClass().add("memory-total");
|
||||
|
||||
StackPane usedMemory = new StackPane();
|
||||
usedMemory.getStyleClass().add("memory-used");
|
||||
usedMemory.maxWidthProperty().bind(Bindings.createDoubleBinding(() ->
|
||||
progressBarPane.getWidth() *
|
||||
(memoryStatusProperty.get().getUsed() * 1.0 / memoryStatusProperty.get().getTotal()), progressBarPane.widthProperty(),
|
||||
memoryStatusProperty));
|
||||
StackPane allocateMemory = new StackPane();
|
||||
allocateMemory.getStyleClass().add("memory-allocate");
|
||||
allocateMemory.maxWidthProperty().bind(Bindings.createDoubleBinding(() ->
|
||||
progressBarPane.getWidth() *
|
||||
Math.min(1.0,
|
||||
(double)(HMCLGameRepository.getAllocatedMemory(maxMemoryProperty.get() * 1024L * 1024L, memoryStatusProperty.get().getAvailable(), chkAutoAllocate.isSelected())
|
||||
+ memoryStatusProperty.get().getUsed()) / memoryStatusProperty.get().getTotal()), progressBarPane.widthProperty(),
|
||||
maxMemoryProperty, memoryStatusProperty, chkAutoAllocate.selectedProperty()));
|
||||
|
||||
progressBarPane.getChildren().setAll(allocateMemory, usedMemory);
|
||||
}
|
||||
|
||||
BorderPane digitalPane = new BorderPane();
|
||||
VBox.setMargin(digitalPane, new Insets(0, 0, 0, 16));
|
||||
{
|
||||
Label lblPhysicalMemory = new Label();
|
||||
lblPhysicalMemory.getStyleClass().add("memory-label");
|
||||
digitalPane.setLeft(lblPhysicalMemory);
|
||||
lblPhysicalMemory.textProperty().bind(Bindings.createStringBinding(() -> {
|
||||
return i18n("settings.memory.used_per_total.format", memoryStatusProperty.get().getUsedGB(), memoryStatusProperty.get().getTotalGB());
|
||||
}, memoryStatusProperty));
|
||||
|
||||
Label lblAllocateMemory = new Label();
|
||||
lblAllocateMemory.textProperty().bind(Bindings.createStringBinding(() -> {
|
||||
long maxMemory = Lang.parseInt(maxMemoryProperty.get(), 0) * 1024L * 1024L;
|
||||
return i18n(memoryStatusProperty.get().hasAvailable() && maxMemory > memoryStatusProperty.get().getAvailable()
|
||||
? (chkAutoAllocate.isSelected() ? "settings.memory.allocate.format.auto.exceeded" : "settings.memory.allocate.format.manual.exceeded")
|
||||
: (chkAutoAllocate.isSelected() ? "settings.memory.allocate.format.auto" : "settings.memory.allocate.format.manual"),
|
||||
OperatingSystem.PhysicalMemoryStatus.toGigaBytes(maxMemory),
|
||||
OperatingSystem.PhysicalMemoryStatus.toGigaBytes(HMCLGameRepository.getAllocatedMemory(maxMemory, memoryStatusProperty.get().getAvailable(), chkAutoAllocate.isSelected())),
|
||||
OperatingSystem.PhysicalMemoryStatus.toGigaBytes(memoryStatusProperty.get().getAvailable()));
|
||||
}, memoryStatusProperty, maxMemoryProperty, chkAutoAllocate.selectedProperty()));
|
||||
lblAllocateMemory.getStyleClass().add("memory-label");
|
||||
digitalPane.setRight(lblAllocateMemory);
|
||||
}
|
||||
|
||||
maxMemoryPane.getChildren().setAll(title, chkAutoAllocate, lowerBoundPane, titlePane, progressBarPane, digitalPane);
|
||||
}
|
||||
|
||||
BorderPane launcherVisibilityPane = new BorderPane();
|
||||
@@ -368,7 +470,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
lblPhysicalMemory.setText(i18n("settings.physical_memory") + ": " + OperatingSystem.TOTAL_MEMORY + "MB");
|
||||
memoryStatusProperty.set(OperatingSystem.getPhysicalMemoryStatus().orElse(OperatingSystem.PhysicalMemoryStatus.INVALID));
|
||||
|
||||
Task.supplyAsync(JavaVersion::getJavas).thenAcceptAsync(Schedulers.javafx(), list -> {
|
||||
javaItem.loadChildren(list.stream()
|
||||
@@ -434,7 +536,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
if (lastVersionSetting != null) {
|
||||
FXUtils.unbindInt(txtWidth, lastVersionSetting.widthProperty());
|
||||
FXUtils.unbindInt(txtHeight, lastVersionSetting.heightProperty());
|
||||
FXUtils.unbindInt(txtMaxMemory, lastVersionSetting.maxMemoryProperty());
|
||||
maxMemoryProperty.unbindBidirectional(lastVersionSetting.maxMemoryProperty());
|
||||
FXUtils.unbindString(javaItem.getTxtCustom(), lastVersionSetting.javaDirProperty());
|
||||
FXUtils.unbindString(gameDirItem.getTxtCustom(), lastVersionSetting.gameDirProperty());
|
||||
FXUtils.unbindString(nativesDirItem.getTxtCustom(), lastVersionSetting.nativesDirProperty());
|
||||
@@ -444,6 +546,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
FXUtils.unbindString(txtWrapper, lastVersionSetting.wrapperProperty());
|
||||
FXUtils.unbindString(txtPrecallingCommand, lastVersionSetting.preLaunchCommandProperty());
|
||||
FXUtils.unbindString(txtServerIP, lastVersionSetting.serverIpProperty());
|
||||
FXUtils.unbindBoolean(chkAutoAllocate, lastVersionSetting.autoMemoryProperty());
|
||||
FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty());
|
||||
FXUtils.unbindBoolean(chkNoGameCheck, lastVersionSetting.notCheckGameProperty());
|
||||
FXUtils.unbindBoolean(chkNoJVMCheck, lastVersionSetting.notCheckJVMProperty());
|
||||
@@ -469,7 +572,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
// bind new data fields
|
||||
FXUtils.bindInt(txtWidth, versionSetting.widthProperty());
|
||||
FXUtils.bindInt(txtHeight, versionSetting.heightProperty());
|
||||
FXUtils.bindInt(txtMaxMemory, versionSetting.maxMemoryProperty());
|
||||
maxMemoryProperty.bindBidirectional(versionSetting.maxMemoryProperty());
|
||||
FXUtils.bindString(javaItem.getTxtCustom(), versionSetting.javaDirProperty());
|
||||
FXUtils.bindString(gameDirItem.getTxtCustom(), versionSetting.gameDirProperty());
|
||||
FXUtils.bindString(nativesDirItem.getTxtCustom(), versionSetting.nativesDirProperty());
|
||||
@@ -479,6 +582,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
FXUtils.bindString(txtWrapper, versionSetting.wrapperProperty());
|
||||
FXUtils.bindString(txtPrecallingCommand, versionSetting.preLaunchCommandProperty());
|
||||
FXUtils.bindString(txtServerIP, versionSetting.serverIpProperty());
|
||||
FXUtils.bindBoolean(chkAutoAllocate, versionSetting.autoMemoryProperty());
|
||||
FXUtils.bindBoolean(chkFullscreen, versionSetting.fullscreenProperty());
|
||||
FXUtils.bindBoolean(chkNoGameCheck, versionSetting.notCheckGameProperty());
|
||||
FXUtils.bindBoolean(chkNoJVMCheck, versionSetting.notCheckJVMProperty());
|
||||
|
||||
@@ -42,6 +42,22 @@
|
||||
-fx-text-fill: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.memory-label {
|
||||
-fx-font-size: 14px;
|
||||
}
|
||||
|
||||
.memory-used {
|
||||
-fx-background-color: -fx-base-darker-color;
|
||||
}
|
||||
|
||||
.memory-allocate {
|
||||
-fx-background-color: -fx-base-check-color;
|
||||
}
|
||||
|
||||
.memory-total {
|
||||
-fx-background-color: -fx-base-rippler-color;
|
||||
}
|
||||
|
||||
.update-label {
|
||||
-fx-text-fill: red;
|
||||
}
|
||||
@@ -530,23 +546,12 @@
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.jfx-slider-style {
|
||||
.jfx-slider {
|
||||
-jfx-indicator-position: right;
|
||||
}
|
||||
|
||||
.jfx-slider-style > .thumb {
|
||||
-fx-background-color: #03a9f4;
|
||||
}
|
||||
|
||||
.jfx-slider-style .track {
|
||||
-fx-background-color: #ff5252;
|
||||
-fx-pref-height: 5px;
|
||||
-fx-pref-width: 5px;
|
||||
}
|
||||
|
||||
.jfx-slider-style .sliderValue {
|
||||
-fx-stroke: WHITE;
|
||||
-fx-font-size: 10.0;
|
||||
.jfx-slider .thumb {
|
||||
-fx-background-color: -fx-base-color;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -520,8 +520,17 @@ settings.launcher.proxy.socks=Socks
|
||||
settings.launcher.proxy.username=账户
|
||||
settings.launcher.theme=主题
|
||||
|
||||
settings.min_memory=最小内存(MB)
|
||||
settings.max_memory=最大内存(MB)
|
||||
settings.memory=游戏内存
|
||||
settings.memory.allocate.auto=最低分配 / 实际分配
|
||||
settings.memory.allocate.manual=游戏分配
|
||||
settings.memory.allocate.format.auto=%1$.1f GB / %2$.1f GB
|
||||
settings.memory.allocate.format.auto.exceeded=%1$.1f GB / %2$.1f GB (可用 %3$.1f GB)
|
||||
settings.memory.allocate.format.manual=%1$.1f GB
|
||||
settings.memory.allocate.format.manual.exceeded=%1$.1f GB (可用 %3$.1f GB)
|
||||
settings.memory.auto_allocate=自动分配
|
||||
settings.memory.lower_bound=最低分配
|
||||
settings.memory.used_per_total=已使用 / 总内存
|
||||
settings.memory.used_per_total.format=%1$.1f GB / %2$.1f GB
|
||||
settings.physical_memory=物理内存大小
|
||||
settings.show_log=查看日志
|
||||
settings.tabs.installers=自动安装
|
||||
|
||||
@@ -17,12 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform;
|
||||
|
||||
import javax.management.JMException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -108,7 +103,8 @@ public enum OperatingSystem {
|
||||
else
|
||||
CURRENT_OS = UNKNOWN;
|
||||
|
||||
TOTAL_MEMORY = getTotalPhysicalMemorySize()
|
||||
TOTAL_MEMORY = getPhysicalMemoryStatus()
|
||||
.map(PhysicalMemoryStatus::getTotal)
|
||||
.map(bytes -> (int) (bytes / 1024 / 1024))
|
||||
.orElse(1024);
|
||||
|
||||
@@ -133,15 +129,22 @@ public enum OperatingSystem {
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<Long> getTotalPhysicalMemorySize() {
|
||||
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
||||
try {
|
||||
Object attribute = mBeanServer.getAttribute(new ObjectName("java.lang", "type", "OperatingSystem"), "TotalPhysicalMemorySize");
|
||||
if (attribute instanceof Long) {
|
||||
return Optional.of((Long) attribute);
|
||||
public static Optional<PhysicalMemoryStatus> getPhysicalMemoryStatus() {
|
||||
java.lang.management.OperatingSystemMXBean bean = java.lang.management.ManagementFactory.getOperatingSystemMXBean();
|
||||
if (bean instanceof com.sun.management.OperatingSystemMXBean) {
|
||||
com.sun.management.OperatingSystemMXBean sunBean =
|
||||
(com.sun.management.OperatingSystemMXBean)
|
||||
java.lang.management.ManagementFactory.getOperatingSystemMXBean();
|
||||
|
||||
if (CURRENT_OS == LINUX) {
|
||||
// On Linux, real amount of memory that is free for using is "available" size of memory,
|
||||
// which also includes size of memory for caching that can be make use of.
|
||||
// But available size of memory cannot be obtained by OperatingSystemMXBean interface.
|
||||
// So we simply disable reporting free physical memory size on Linux.
|
||||
return Optional.of(new PhysicalMemoryStatus(sunBean.getTotalPhysicalMemorySize(), -1));
|
||||
}
|
||||
} catch (JMException e) {
|
||||
return Optional.empty();
|
||||
|
||||
return Optional.of(new PhysicalMemoryStatus(sunBean.getTotalPhysicalMemorySize(), sunBean.getFreePhysicalMemorySize()));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -203,4 +206,48 @@ public enum OperatingSystem {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class PhysicalMemoryStatus {
|
||||
private final long total;
|
||||
private final long available;
|
||||
|
||||
public PhysicalMemoryStatus(long total, long available) {
|
||||
this.total = total;
|
||||
this.available = available;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public double getTotalGB() {
|
||||
return toGigaBytes(total);
|
||||
}
|
||||
|
||||
public long getUsed() {
|
||||
return hasAvailable() ? total - available : 0;
|
||||
}
|
||||
|
||||
public double getUsedGB() {
|
||||
return toGigaBytes(getUsed());
|
||||
}
|
||||
|
||||
public long getAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
public double getAvailableGB() {
|
||||
return toGigaBytes(available);
|
||||
}
|
||||
|
||||
public boolean hasAvailable() {
|
||||
return available >= 0;
|
||||
}
|
||||
|
||||
public static double toGigaBytes(long bytes) {
|
||||
return bytes / 1024. / 1024. / 1024.;
|
||||
}
|
||||
|
||||
public static final PhysicalMemoryStatus INVALID = new PhysicalMemoryStatus(0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user