001/*
002 * Copyright (C) Photon Vision.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package org.photonvision.common.util;
019
020public class MemoryManager {
021    private static final long MEGABYTE_FACTOR = 1024L * 1024L;
022
023    private int collectionThreshold;
024    private long collectionPeriodMillis = -1;
025
026    private double lastUsedMb = 0;
027    private long lastCollectionMillis = 0;
028
029    public MemoryManager(int collectionThreshold) {
030        this.collectionThreshold = collectionThreshold;
031    }
032
033    public MemoryManager(int collectionThreshold, long collectionPeriodMillis) {
034        this.collectionThreshold = collectionThreshold;
035        this.collectionPeriodMillis = collectionPeriodMillis;
036    }
037
038    public void setCollectionThreshold(int collectionThreshold) {
039        this.collectionThreshold = collectionThreshold;
040    }
041
042    public void setCollectionPeriodMillis(long collectionPeriodMillis) {
043        this.collectionPeriodMillis = collectionPeriodMillis;
044    }
045
046    private static long getUsedMemory() {
047        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
048    }
049
050    private static double getUsedMemoryMB() {
051        return ((double) getUsedMemory() / MEGABYTE_FACTOR);
052    }
053
054    private void collect() {
055        System.gc();
056        System.runFinalization();
057    }
058
059    public void run() {
060        run(false);
061    }
062
063    public void run(boolean print) {
064        var usedMem = getUsedMemoryMB();
065
066        if (usedMem != lastUsedMb) {
067            lastUsedMb = usedMem;
068            if (print) System.out.printf("Memory usage: %.2fMB\n", usedMem);
069        }
070
071        boolean collectionThresholdPassed = usedMem >= collectionThreshold;
072        boolean collectionPeriodPassed =
073                collectionPeriodMillis != -1
074                        && (System.currentTimeMillis() - lastCollectionMillis >= collectionPeriodMillis);
075
076        if (collectionThresholdPassed || collectionPeriodPassed) {
077            collect();
078            lastCollectionMillis = System.currentTimeMillis();
079            if (print) {
080                System.out.printf("Garbage collected at %.2fMB\n", usedMem);
081            }
082        }
083    }
084}