diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/macos/MacOSHardwareDetector.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/macos/MacOSHardwareDetector.java index 706294fad8..46d89ae49f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/macos/MacOSHardwareDetector.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/macos/MacOSHardwareDetector.java @@ -34,6 +34,8 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -149,4 +151,71 @@ public List detectGraphicsCards() { return Collections.emptyList(); } + + private static final Pattern PAGE_SIZE_PATTERN = Pattern.compile("\\(page size of (?\\d+) bytes\\)"); + + @Override + public long getFreeMemorySize() { + vmStat: + try { + Map stats = SystemUtils.run(List.of("/usr/bin/vm_stat"), + inputStream -> KeyValuePairUtils.loadPairs( + new BufferedReader(new InputStreamReader(inputStream, OperatingSystem.NATIVE_CHARSET)))); + String statistics = stats.get("Mach Virtual Memory Statistics"); + + long pageSize; + long pagesFree; + long pagesInactive; + long pagesSpeculative; + long pagesPurgeable; + + if (statistics != null) { + Matcher matcher = PAGE_SIZE_PATTERN.matcher(statistics); + if (matcher.find()) { + pageSize = Long.parseLong(matcher.group("size")); + } else { + break vmStat; + } + } else { + break vmStat; + } + + String pagesFreeStr = stats.get("Pages free"); + if (pagesFreeStr != null && pagesFreeStr.endsWith(".")) { + pagesFree = Long.parseUnsignedLong(pagesFreeStr, 0, pagesFreeStr.length() - 1, 10); + } else { + break vmStat; + } + + String pagesInactiveStr = stats.get("Pages inactive"); + if (pagesInactiveStr != null && pagesInactiveStr.endsWith(".")) { + pagesInactive = Long.parseUnsignedLong(pagesInactiveStr, 0, pagesInactiveStr.length() - 1, 10); + } else { + break vmStat; + } + + String pagesSpeculativeStr = stats.get("Pages speculative"); + if (pagesSpeculativeStr != null && pagesSpeculativeStr.endsWith(".")) { + pagesSpeculative = Long.parseUnsignedLong(pagesSpeculativeStr, 0, pagesSpeculativeStr.length() - 1, 10); + } else { + break vmStat; + } + + String pagesPurgeableStr = stats.get("Pages purgeable"); + if (pagesPurgeableStr != null && pagesPurgeableStr.endsWith(".")) { + pagesPurgeable = Long.parseUnsignedLong(pagesPurgeableStr, 0, pagesPurgeableStr.length() - 1, 10); + } else { + break vmStat; + } + + long available = (pagesFree + pagesSpeculative + pagesInactive + pagesPurgeable) * pageSize; + if (available > 0) { + return available; + } + } catch (Throwable e) { + LOG.warning("Failed to parse vm_stat output", e); + } + + return super.getFreeMemorySize(); + } }