Package be.bagofwords.application.memory

Source Code of be.bagofwords.application.memory.MemoryManager

package be.bagofwords.application.memory;

import be.bagofwords.application.CloseableComponent;
import be.bagofwords.application.annotations.EagerBowComponent;
import be.bagofwords.ui.UI;
import be.bagofwords.util.SafeThread;
import be.bagofwords.util.Utils;
import com.sun.management.GarbageCollectionNotificationInfo;

import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import javax.management.openmbean.CompositeData;
import java.io.File;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@EagerBowComponent
public class MemoryManager implements CloseableComponent {

    private final List<MemoryGobbler> memoryGobblers;
    private MemoryStatus memoryStatus;
    private ReentrantLock globalCleanInProgressLock;
    private final CleanupObjectsThread cleanupObjectsThread;
    private boolean dumpHeapToFileWhenMemoryFull;

    public MemoryManager() {
        memoryGobblers = new ArrayList<>();
        globalCleanInProgressLock = new ReentrantLock();
        memoryStatus = MemoryStatus.FREE;
        cleanupObjectsThread = new CleanupObjectsThread();
        cleanupObjectsThread.start();
    }

    public boolean getDumpHeapToFileWhenMemoryFull() {
        return dumpHeapToFileWhenMemoryFull;
    }

    public void setDumpHeapToFileWhenMemoryFull(boolean dumpHeapToFileWhenMemoryFull) {
        this.dumpHeapToFileWhenMemoryFull = dumpHeapToFileWhenMemoryFull;
    }

    public void close() {
        cleanupObjectsThread.terminateAndWait();
    }

    /**
     * This method should be called by methods that consume a lot of memory in order to free up other
     * memory gobblers and prevent an Out-Of-Memory error.
     */

    public void waitForSufficientMemory() {
        if (memoryStatus == MemoryStatus.CRITICAL && !(globalCleanInProgressLock.isLocked() && globalCleanInProgressLock.isHeldByCurrentThread())) {
            //Don't write until enough memory is free
            long start = System.currentTimeMillis();
            long timeOfLastWarning = System.currentTimeMillis();
            while (memoryStatus == MemoryStatus.CRITICAL) {
                Utils.threadSleep(20);
                if (System.currentTimeMillis() - timeOfLastWarning > 30000) {
                    UI.writeWarning("Method has been waiting for more memory for " + (System.currentTimeMillis() - start) + " ms");
                    timeOfLastWarning = System.currentTimeMillis();
                }
            }
        }
    }

    public void registerMemoryGobbler(MemoryGobbler memoryGobbler) {
        synchronized (memoryGobblers) {
            this.memoryGobblers.add(memoryGobbler);
        }
    }

    public MemoryStatus getMemoryStatus() {
        return memoryStatus;
    }

    private class CleanupObjectsThread extends SafeThread {

        private CleanupObjectsThread() {
            super("CleanObjectsThread", true);
        }

        public void runInt() {
            Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

            registerGarbageCollectionListener();

            while (!isTerminateRequested()) {
                try {
                    if (memoryStatus == MemoryStatus.LOW || memoryStatus == MemoryStatus.CRITICAL) {
                        globalCleanInProgressLock.lock();
                        //Low on memory, clean caches
                        if (dumpHeapToFileWhenMemoryFull) {
                            File dumpFile = new File("heap_" + System.currentTimeMillis() + ".bin");
                            HeapDumper.dumpHeap(dumpFile.getAbsolutePath(), false);
                            UI.write("Heap dumped to " + dumpFile.getAbsolutePath());
                        }
                        List<MemoryGobbler> currCollections;
                        synchronized (memoryGobblers) {
                            currCollections = new ArrayList<>(memoryGobblers);
                        }
                        for (MemoryGobbler collection : currCollections) {
                            collection.freeMemory();
                        }
                        memoryStatus = MemoryStatus.FREE;
                        System.gc();
                        globalCleanInProgressLock.unlock();
                    }
                } catch (Throwable exp) {
                    UI.writeError("Exception in CleanObjectsThread!!!", exp);
                }
                Utils.threadSleep(50);
            }
        }

        //copied from http://www.fasterj.com/articles/gcnotifs.shtml

        private void registerGarbageCollectionListener() {
            List<GarbageCollectorMXBean> gcbeans = java.lang.management.ManagementFactory.getGarbageCollectorMXBeans();
            for (GarbageCollectorMXBean gcbean : gcbeans) {
                NotificationEmitter emitter = (NotificationEmitter) gcbean;

                NotificationListener listener = new NotificationListener() {
                    //keep a count of the total time spent in GCs
                    long totalGcDuration = 0;

                    //implement the notifier callback handler
                    @Override
                    public void handleNotification(Notification notification, Object handback) {
                        if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
                            //get the information associated with this notification
                            GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
                            Map<String, MemoryUsage> mem = info.getGcInfo().getMemoryUsageAfterGc();
                            for (Map.Entry<String, MemoryUsage> entry : mem.entrySet()) {
                                String name = entry.getKey();
                                MemoryUsage memoryDetail = entry.getValue();
                                if ("PS Old Gen".equals(name)) {
                                    memoryStatus = findStatus(memoryDetail.getUsed(), memoryDetail.getMax());
                                }
                            }
                            totalGcDuration += info.getGcInfo().getDuration();
                        }
                    }
                };

                //Add the listener
                emitter.addNotificationListener(listener, null, null);
            }
        }
    }

    private MemoryStatus findStatus(long used, long max) {
        double fraction = used / (double) max;
        for (int i = MemoryStatus.values().length - 1; i >= 0; i--) {
            MemoryStatus curr = MemoryStatus.values()[i];
            if (curr.getMinMemoryUsage() <= fraction) {
                return curr;
            }
        }
        return MemoryStatus.FREE;
    }

}
TOP

Related Classes of be.bagofwords.application.memory.MemoryManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.