1 /** 2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 Trim the heap size by periodically force the GC to collect unused memory, free 7 it and then tell malloc to further free it back to the OS. 8 */ 9 module my.gc.memfree; 10 11 import std.concurrency : send, spawn, receiveTimeout, Tid; 12 import std.datetime : SysTime, Clock, dur; 13 14 import my.gc.refc; 15 16 /// Returns: a started instance of MemFree. 17 RefCounted!MemFree memFree() @safe { 18 MemFree inst; 19 inst.start; 20 return RefCounted!MemFree(inst); 21 } 22 23 /** Reduces the used memory by the GC and free the heap to the OS. 24 * 25 * To avoid calling this too often the struct have a timer to ensure it is 26 * callled at most ones every minute. 27 * 28 * TODO: maybe add functionality to call it more often when above e.g. 50% memory usage? 29 */ 30 struct MemFree { 31 private { 32 bool isRunning; 33 Tid bg; 34 } 35 36 ~this() @trusted { 37 if (!isRunning) 38 return; 39 40 scope (exit) 41 isRunning = false; 42 send(bg, Msg.stop); 43 } 44 45 /** Start a background thread to do the work. 46 * 47 * It terminates when the destructor is called. 48 */ 49 void start() @trusted { 50 bg = spawn(&tick); 51 isRunning = true; 52 } 53 54 } 55 56 private: 57 58 enum Msg { 59 stop, 60 } 61 62 void tick() nothrow { 63 import core.thread : Thread; 64 import core.time : dur; 65 import core.memory : GC; 66 67 const tickInterval = 1.dur!"minutes"; 68 69 bool running = true; 70 SysTime next = Clock.currTime + tickInterval; 71 while (running) { 72 try { 73 receiveTimeout(tickInterval, (Msg x) { running = false; }); 74 } catch (Exception e) { 75 running = false; 76 } 77 78 GC.collect; 79 GC.minimize; 80 malloc_trim(0); 81 } 82 } 83 84 // malloc_trim - release free memory from the heap 85 extern (C) int malloc_trim(size_t pad) nothrow @system;