package com.tns; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.util.WeakHashMap; public class GcListener { private final ReferenceQueue<Object> q; private final Thread gcThread; private final Thread monitorThread; private final int throttleTime; private final int monitorInterval; private final double freeMemoryRatio; private final WeakHashMap<Runtime, Object> subscribers; private boolean firstStart = true; private static volatile GcListener instance; private class GcMonitor implements Runnable { private long lastUpdateTime = 0; private final long throttleTime; public GcMonitor(int throttleTime) { this.throttleTime = throttleTime * 1000000; } public void run() { while (true) { try { PhantomReference<Object> ref = createRef(); PhantomReference<Objects> o = (PhantomReference<Objects>)GcListener.this.q.remove(); long currentUpdateTime = System.nanoTime(); if ((currentUpdateTime - lastUpdateTime) > throttleTime) { GcListener.this.notifyGc(); lastUpdateTime = currentUpdateTime; } } catch (InterruptedException e) { e.printStackTrace(); } } } private PhantomReference<Object> createRef() { PhantomReference<Object> ref = new PhantomReference<Object>(new Object(), GcListener.this.q); return ref; } } private class MemoryMonitor implements Runnable { private final int timeInterval; private final double freeMemoryRatio; private final java.lang.Runtime runtime; public MemoryMonitor(int timeInterval, double freeMemoryRatio) { this.timeInterval = timeInterval; this.freeMemoryRatio = freeMemoryRatio; this.runtime = java.lang.Runtime.getRuntime(); } public void run() { while (true) { try { long freeMemory = MemoryMonitor.this.runtime.freeMemory(); long totalMemory = MemoryMonitor.this.runtime.totalMemory(); long maxMemory = MemoryMonitor.this.runtime.maxMemory(); double ratio = ((double)(maxMemory - totalMemory + freeMemory)) / ((double)maxMemory); if (ratio < freeMemoryRatio) { GcListener.this.notifyGc(); } Thread.sleep(timeInterval); } catch (InterruptedException e) { e.printStackTrace(); } } } } private GcListener(int throttleTime, int monitorInterval, double freeMemoryRatio) { this.throttleTime = throttleTime; this.monitorInterval = monitorInterval; this.freeMemoryRatio = freeMemoryRatio; this.subscribers = new WeakHashMap<Runtime, Object>(); if (throttleTime > 0) { q = new ReferenceQueue<Object>(); gcThread = new Thread(new GcMonitor(throttleTime)); gcThread.setName("NativeScript GC thread"); gcThread.setDaemon(true); } else { q = null; gcThread = null; } if (monitorInterval > 0) { monitorThread = new Thread(new MemoryMonitor(monitorInterval, freeMemoryRatio)); monitorThread.setName("NativeScript monitor thread"); monitorThread.setDaemon(true); } else { monitorThread = null; } } /** * @param throttleTime throttle time in milliseconds * @param monitorInterval time in milliseconds */ public static GcListener getInstance(int throttleTime, int monitorInterval, double freeMemoryRatio) { if (instance == null) { synchronized (GcListener.class) { if (instance == null) { instance = new GcListener(throttleTime, monitorInterval, freeMemoryRatio); } } } return instance; } public static void subscribe(Runtime runtime) { synchronized (instance) { if (instance.firstStart) { instance.start(); instance.firstStart = false; } instance.subscribers.put(runtime, null); } } public static void unsubscribe(Runtime runtime) { synchronized (instance) { instance.subscribers.remove(runtime); } } private void start() { if (gcThread != null) { gcThread.start(); } if (monitorThread != null) { monitorThread.start(); } } private void notifyGc() { synchronized (instance) { for (Runtime runtime: subscribers.keySet()) { runtime.notifyGc(); } } } }