// 内存预检验与"分配失败"兜底(配合 `-fno-exceptions` 构建)。 // // 背景:本项目关闭了 C++ 异常(`.xdata`+`.pdata` 占产物体积约 9%,关闭后**实测省 27,136 B**: // 909,824 → 882,688;再叠加 LTO 共减 120,832 B)。关闭后 `std::bad_alloc` 不再可捕获, // 任何分配失败都会直接 terminate。因此: // 1. 凡"按数据规模预留大块内存"的地方改用 `MemReserveIfRoom()`(预检验); // 2. 万一仍有遗漏,由 `MemInstallTerminateHandler()` 给出可见提示而不是静默消失。 #include "MemProbe.hpp" #include #include #include #include namespace jdi { namespace { /** @brief 可用内存查询的缓存有效期(毫秒)。 */ constexpr std::uint64_t kMemCacheMs = 200; /** * @brief 预留后必须保留的可用内存比例(1/8)。 * * 不把可用内存吃光:Windows 在物理内存接近耗尽时会大量换页, * 表现为"整个程序卡死"而不是"分配失败",对用户更糟。 */ constexpr std::size_t kMemHeadroomRatio = 8; std::atomic g_memCachedAt{0}; std::atomic g_memCachedBytes{0}; /** * @brief terminate 处理:给出可见提示后退出。 * * ⚠️ 这里**不能**用 MessageBoxW 之外的任何复杂机制:此刻堆可能已不可用, * 连 `std::wstring` 都不该构造(字面量是静态数据,安全)。 */ void OnTerminate() { MessageBoxW(nullptr, L"内存不足:本次操作需要的内存超过系统可用量。\n\n" L"建议:减少同时显示的网格数或信号数,或关闭占用内存较大的程序后重试。", L"Just DataInspector", MB_ICONERROR | MB_OK | MB_SETFOREGROUND); std::_Exit(EXIT_FAILURE); } } // namespace std::size_t MemAvailablePhysical() { const std::uint64_t now = GetTickCount64(); const std::uint64_t last = g_memCachedAt.load(std::memory_order_relaxed); if (last != 0 && (now - last) < kMemCacheMs) { const std::size_t cached = g_memCachedBytes.load(std::memory_order_relaxed); if (cached != 0) { return cached; } } MEMORYSTATUSEX status = {}; status.dwLength = sizeof(status); std::size_t available = std::numeric_limits::max(); if (GlobalMemoryStatusEx(&status) != FALSE) { available = static_cast(status.ullAvailPhys); } g_memCachedBytes.store(available, std::memory_order_relaxed); g_memCachedAt.store(now, std::memory_order_relaxed); return available; } bool MemCanAllocate(std::size_t bytes) { if (bytes == 0) { return true; } const std::size_t available = MemAvailablePhysical(); if (available == std::numeric_limits::max()) { return true; // 查询失败:按"内存充足"处理(宁可尝试也不要误拒) } const std::size_t headroom = available / kMemHeadroomRatio; return bytes <= (available - std::min(available, headroom)); } void MemInstallTerminateHandler() { static bool installed = false; if (installed) { return; } installed = true; std::set_terminate(&OnTerminate); } } // namespace jdi