import javax.realtime.*; import com.sun.rtsjx.*; /** * Example of self made RTGC policies. */ public class GCPolicyTest { static int normalPriority; static int maxPriority; static volatile FullyConcurrentGarbageCollector rtgc; static { try { // check that the RTGC is active GarbageCollector gc = javax.realtime.RealtimeSystem.currentGC(); if (gc instanceof FullyConcurrentGarbageCollector) { rtgc = (FullyConcurrentGarbageCollector) gc; } else { System.out.println("RTGC must be activated for this test"); System.exit(1); } // read the priorities from the JVM, in case they were changed // on the command line normalPriority = FullyConcurrentGarbageCollector.getNormalPriority(); maxPriority = FullyConcurrentGarbageCollector.getCriticalPriority(); } catch (Exception e) { e.printStackTrace(); } } // mechanism to change the default GC priority static void changeNormalPriority(int priority) { FullyConcurrentGarbageCollector.set("NormalPriority",priority); } // mechanism to change the default GC priority static void changeCriticalPriority(int priority) { FullyConcurrentGarbageCollector.set("CriticalPriority",priority); } /** * Implementation of a preemptive RTGC policy. * *
It temporarily boosts the RTGC more * often, avoiding a final long boost when * memory is nearly exhausted but preventing * soft real-time threads from achieveing very * low latencies on low-loaded systems. */ static class PreemptionPolicy extends RealtimeThread { static int boosted_millis; static int normal_millis; static boolean activated = false; static boolean running = false; static boolean deleted = false; static Object lock = new Object(); static PreemptionPolicy singleton; private PreemptionPolicy() { // set run at a priority higher than the RTGC this.setPriority(maxPriority + 1); this.setDaemon(true); } /** * Activate the policy or reconfigure it. * * @param boosted duration of the boost, in milliseconds. * @param normal delay before the next boost. */ public static void activate(int boosted, int normal) { if (singleton == null) { synchronized(lock) { if (singleton == null) { singleton = new PreemptionPolicy(); } } } synchronized(singleton) { PreemptionPolicy.boosted_millis = boosted; PreemptionPolicy.normal_millis = normal; if (! running) { activated=true; running=true; singleton.start(); } else { if (! activated) { activated = true; singleton.notifyAll(); } } } } /** * Temporarily cancel this policy. * *
this allows to go back to the default RTGC behavior * when the system isnot heavily loaded. */ public static void deactivate() { activated=false; } /** * Stops the policy and free the ressources. */ public static void delete() { deleted = true; synchronized(singleton) { singleton.notifyAll(); } } /** * Control code. */ public void run() { synchronized (this) { // initialize with current time AbsoluteTime abs = Clock.getRealtimeClock().getTime(); AbsoluteTime now = Clock.getRealtimeClock().getTime(); while (! deleted) { if (! activated) { try { this.wait(); } catch (InterruptedException e) { } Clock.getRealtimeClock().getTime(abs); } else { // restart a new GC cycle if idle FullyConcurrentGarbageCollector.startAsyncGC(normalPriority); abs.add(boosted_millis,0,abs); Clock.getRealtimeClock().getTime(now); if (abs.compareTo(now) == 1) { // boost the GC threads changeNormalPriority(maxPriority-1); try { // boost it at most boosted_millis HighResolutionTime.waitForObject(this,abs); } catch (InterruptedException e) { } // go back to unboosted state changeNormalPriority(normalPriority); } abs.add(normal_millis,0,abs); try { HighResolutionTime.waitForObject(this,abs); } catch (InterruptedException e) { } } } } } } /** * Implementation of a constrained RTGC policy. * *
It prevents the RTGC from preempting soft * real-time threads during long periods. The danger * is that the RTGC may not get enough CPU cycles with * respect to the memory required by the soft real-time * threads. * *
Another way to define this policy is * that the soft-real time thread periodicaly * become critical ones. */ static class ConstrainedPolicy extends RealtimeThread { static int boosted_millis; static int normal_millis; static boolean activated = false; static boolean running = false; static boolean deleted = false; static Object lock = new Object(); static ConstrainedPolicy singleton; private ConstrainedPolicy() { // set run at a priority higher than the RTGC this.setPriority(maxPriority + 1); this.setDaemon(true); } /** * Activate the policy or reconfigure it. * * @param boosted duration of the boost, in milliseconds. * @param normal delay before the next boost. */ public static void activate(int boosted, int normal) { if (singleton == null) { synchronized(lock) { if (singleton == null) { singleton = new ConstrainedPolicy(); } } } synchronized(singleton) { ConstrainedPolicy.boosted_millis = boosted; ConstrainedPolicy.normal_millis = normal; if (! running) { activated=true; running=true; singleton.start(); } else { if (! activated) { activated = true; singleton.notifyAll(); } } } } /** * Temporarily cancel this policy. * *
this allows to go back to the default RTGC behavior * when the system isnot heavily loaded. */ public static void deactivate() { activated=false; } /** * Stops the policy and free the ressources. */ public static void delete() { deleted = true; synchronized(singleton) { singleton.notifyAll(); } } /** * Control code. */ public void run() { synchronized (this) { // initialize with current time AbsoluteTime abs = Clock.getRealtimeClock().getTime(); AbsoluteTime now = Clock.getRealtimeClock().getTime(); while (! deleted) { if (! activated) { try { this.wait(); } catch (InterruptedException e) { } Clock.getRealtimeClock().getTime(abs); } else { // restart a new GC cycle if idle FullyConcurrentGarbageCollector.startAsyncGC(normalPriority); abs.add(boosted_millis,0,abs); Clock.getRealtimeClock().getTime(now); if (abs.compareTo(now) == 1) { // allow normal boost changeCriticalPriority(maxPriority); try { HighResolutionTime.waitForObject(this,abs); } catch (InterruptedException e) { } // prevent the boost changeCriticalPriority(normalPriority); } abs.add(normal_millis,0,abs); try { HighResolutionTime.waitForObject(this,abs); } catch (InterruptedException e) { } } } } } } /** * Notification handler to print RTGC priority changes. */ static class PriorityHandler extends AsyncEventHandler { public PriorityHandler() { setSchedulingParameters(new PriorityParameters(maxPriority+2)); } public void handleAsyncEvent() { int prio = FullyConcurrentGarbageCollector.getCurrentPriority(); System.out.println("GC prio set to "+prio); } } public static final boolean SHOW_PRIO_CHANGES = true; public static void main(String[] args) { if (SHOW_PRIO_CHANGES) { // add a trace to see GC priority changes FullyConcurrentGarbageCollector.addPriorityHandler(new PriorityHandler()); } System.out.println("** Activating the preemption policy **"); // activate the preemptive mechanism PreemptionPolicy.activate(10,10); int GC_LOOPS = 3; // stop after a few GC cycles int init_count = FullyConcurrentGarbageCollector.getInvocationCount(); int max_gc_count = init_count+GC_LOOPS; while (FullyConcurrentGarbageCollector.getInvocationCount() < max_gc_count) { // active wait } // deactivate the policy PreemptionPolicy.deactivate(); System.out.println("** Activating the constrained policy **"); // activate the contraining policy ConstrainedPolicy.activate(10,10); // stop after a few GC cycles init_count = FullyConcurrentGarbageCollector.getInvocationCount(); max_gc_count = init_count+GC_LOOPS; while (FullyConcurrentGarbageCollector.getInvocationCount() < max_gc_count) { // active wait } System.out.println("** Force the GC to be boosted early **"); // this tell the GC to always consider that memory is scarce // and that it should run at the boosted priority FullyConcurrentGarbageCollector.set("CriticalMinFreeBytes", Runtime.getRuntime().maxMemory()); // stop after a few GC cycles init_count = FullyConcurrentGarbageCollector.getInvocationCount(); max_gc_count = init_count+GC_LOOPS; while (FullyConcurrentGarbageCollector.getInvocationCount() < max_gc_count) { // active wait } // deactivate the policy ConstrainedPolicy.deactivate(); } }