diff --git a/util/util.go b/util/util.go
index 385534e2e6..e0fa929d41 100644
--- a/util/util.go
+++ b/util/util.go
@@ -21,7 +21,6 @@ package util
import (
"fmt"
- "runtime"
"syscall"
)
@@ -74,18 +73,7 @@ func GetCurrentProcessTimes() (utime int64, stime int64, err error) {
return
}
-// GetTotalMemory gets total system memory on Linux systems
+// GetTotalMemory gets total system memory
func GetTotalMemory() uint64 {
- switch runtime.GOOS {
- case "linux":
- // Use sysinfo on Linux
- var si syscall.Sysinfo_t
- err := syscall.Sysinfo(&si)
- if err != nil {
- return 0
- }
- return si.Totalram
- default:
- return 0
- }
+ return getTotalMemory()
}
diff --git a/util/util_darwin.go b/util/util_darwin.go
new file mode 100644
index 0000000000..7c08acdefe
--- /dev/null
+++ b/util/util_darwin.go
@@ -0,0 +1,34 @@
+// Copyright (C) 2019-2025 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package util
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func getTotalMemory() uint64 {
+ out, err := syscall.Sysctl("hw.memsize")
+ if err != nil {
+ return 0
+ }
+ b := []byte(out)
+ if len(b) < 8 {
+ b = append(b, 0)
+ }
+ return *(*uint64)(unsafe.Pointer(&b[0]))
+}
diff --git a/util/util_linux.go b/util/util_linux.go
new file mode 100644
index 0000000000..f958ebdf5c
--- /dev/null
+++ b/util/util_linux.go
@@ -0,0 +1,31 @@
+// Copyright (C) 2019-2025 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package util
+
+import (
+ "syscall"
+)
+
+func getTotalMemory() uint64 {
+ var si syscall.Sysinfo_t
+ err := syscall.Sysinfo(&si)
+ if err != nil {
+ return 0
+ }
+ // support 32-bit systems where Totalram is uint32
+ return uint64(si.Totalram) * uint64(si.Unit)
+}
diff --git a/util/util_test.go b/util/util_test.go
new file mode 100644
index 0000000000..f33b5c5f95
--- /dev/null
+++ b/util/util_test.go
@@ -0,0 +1,32 @@
+// Copyright (C) 2019-2025 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package util
+
+import (
+ "testing"
+
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetTotalMemory(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ mem := GetTotalMemory()
+ require.Greater(t, mem, uint64(0))
+}
diff --git a/util/util_windows.go b/util/util_windows.go
index c14c527256..e1e1b7924b 100644
--- a/util/util_windows.go
+++ b/util/util_windows.go
@@ -21,6 +21,7 @@ import (
"math"
"syscall"
"time"
+ "unsafe"
)
/* misc */
@@ -70,7 +71,38 @@ func filetimeToDuration(ft *syscall.Filetime) time.Duration {
return time.Duration(n * 100)
}
-// GetTotalMemory gets total system memory, returns 0 on Windows
+// GetTotalMemory gets total system memory on Windows
func GetTotalMemory() uint64 {
- return 0
+ var memoryStatusEx MemoryStatusEx
+ memoryStatusEx.dwLength = uint32(unsafe.Sizeof(memoryStatusEx))
+
+ if err := globalMemoryStatusEx(&memoryStatusEx); err != nil {
+ return 0
+ }
+ return memoryStatusEx.ullTotalPhys
+}
+
+type MemoryStatusEx struct {
+ dwLength uint32
+ dwMemoryLoad uint32
+ ullTotalPhys uint64
+ ullAvailPhys uint64
+ ullTotalPageFile uint64
+ ullAvailPageFile uint64
+ ullTotalVirtual uint64
+ ullAvailVirtual uint64
+ ullAvailExtendedVirtual uint64
+}
+
+var (
+ modkernel32 = syscall.NewLazyDLL("kernel32.dll")
+ procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx")
+)
+
+func globalMemoryStatusEx(memoryStatusEx *MemoryStatusEx) error {
+ ret, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(memoryStatusEx)))
+ if ret == 0 {
+ return syscall.GetLastError()
+ }
+ return nil
}