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 13 import my.gc.refc; 14 import my.libc; 15 16 /// Returns: a started instance of MemFree. 17 MemFree memFree() @safe { 18 return MemFree(true); 19 } 20 21 /** Reduces the used memory by the GC and free the heap to the OS. 22 * 23 * To avoid calling this too often the struct have a timer to ensure it is 24 * callled at most ones every minute. 25 * 26 * TODO: maybe add functionality to call it more often when above e.g. 50% memory usage? 27 */ 28 struct MemFree { 29 private static struct Data { 30 bool isRunning; 31 Tid bg; 32 } 33 34 private RefCounted!Data data; 35 36 this(bool startNow) @safe { 37 if (startNow) 38 start; 39 } 40 41 ~this() @trusted { 42 if (data.empty || !data.isRunning) 43 return; 44 45 scope (exit) 46 data.isRunning = false; 47 send(data.bg, Msg.stop); 48 } 49 50 /** Start a background thread to do the work. 51 * 52 * It terminates when the destructor is called. 53 */ 54 void start() @trusted { 55 data = Data(true, spawn(&tick)); 56 } 57 58 } 59 60 private: 61 62 enum Msg { 63 stop, 64 } 65 66 void tick() nothrow { 67 import core.time : dur; 68 import core.memory : GC; 69 70 const tickInterval = 1.dur!"minutes"; 71 72 bool running = true; 73 while (running) { 74 try { 75 receiveTimeout(tickInterval, (Msg x) { running = false; }); 76 } catch (Exception e) { 77 running = false; 78 } 79 80 GC.collect; 81 GC.minimize; 82 malloc_trim(0); 83 } 84 }