"We have a bit of a dead code problem," writes R.S., "most of the time, it's different versions (sometimes older, sometimes newer) of the same class that were created as part of a good-intentioned refactoring that was never quite completed."
"And then we have mysterious classes like these."
package com.initech.test; import java.util.LinkedList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.apache.log4j.Logger; /** * 0 475,734 * <p> * 1 475,828 * <p> * 10 476,250 * <p> * 100 480,469 * <p> * 1,000 522,656 * <p> * 10,000 953,359 * <p> * 100,000 5,073,000 * <p> * 1,000,000 46,406,000 * <p> * 10,000,000 458,456,000 * <p> * ~45 bytes per rubbish count * * @author */ public final class RubbishGenerator implements RubbishGeneratorMBean, Runnable { private final static Logger LOG = Logger.getLogger(RubbishGenerator.class); private final AtomicInteger workerCount = new AtomicInteger(1); private final AtomicInteger rubbishPerLoop = new AtomicInteger(10000000); private final AtomicInteger loopDelayMs = new AtomicInteger(1000); private final AtomicBoolean enabled = new AtomicBoolean(false); private final AtomicInteger changeSerialNo = new AtomicInteger(0); private final AtomicLong loopCount = new AtomicLong(0); private ExecutorService workerExecutor; private ExecutorService controllerExecutor; public RubbishGenerator () { } public static void main (final String[] args) { RubbishGenerator g = new RubbishGenerator(); synchronized (g) { try { final Object o = g.generateRubbish(); System.out.println("Rubbish creation completed"); System.gc(); g.wait(1000000); // so its not optimized away System.out.println(o); } catch (InterruptedException e) { e.printStackTrace(); } } } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#getRubbishPerLoop() */ @Override public int getRubbishPerLoop () { return rubbishPerLoop.get(); } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#getLoopDelayMs() */ @Override public int getLoopDelayMs () { return loopDelayMs.get(); } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#getThreadCount() */ @Override public int getWorkerCount () { return workerCount.get(); } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#setRubbishPerLoop(int) */ @Override public void setRubbishPerLoop (final int rubbishPerLoop) { synchronized (this) { if (rubbishPerLoop == this.rubbishPerLoop.get()) { return; } if (rubbishPerLoop < 1) { throw new IllegalArgumentException("byteCount must be > 0, was: " + rubbishPerLoop); } this.rubbishPerLoop.set(rubbishPerLoop); changeSerialNo.incrementAndGet(); this.notifyAll(); } } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#setLoopDelayMs(int) */ @Override public void setLoopDelayMs (final int delayMs) { synchronized (this) { if (delayMs == loopDelayMs.get()) { return; } if (delayMs < 1) { throw new IllegalArgumentException("delayMs must be > 0, was: " + delayMs); } loopDelayMs.set(delayMs); changeSerialNo.incrementAndGet(); this.notifyAll(); } } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#setThreadCount(byte) */ @Override public void setWorkerCount (final int workerCount) { synchronized (this) { if (workerCount == this.workerCount.get()) { return; } if (workerCount < 1) { throw new IllegalArgumentException("workerCount must be > 0, was: " + workerCount); } this.workerCount.set(workerCount); changeSerialNo.incrementAndGet(); this.notifyAll(); } } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#getEnabled() */ @Override public boolean getEnabled () { return enabled.get(); } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#getLoopCount() */ @Override public long getLoopCount () { return loopCount.get(); } /* * (non-Javadoc) * * @see com.initech.test.RubbishGeneratorMBean#setEnabled(boolean) */ @Override public void setEnabled (final boolean enabled) { synchronized (this) { if (enabled == this.enabled.get()) { return; } this.enabled.set(enabled); //changeSerialNo.incrementAndGet(); // a notifyAll wouldn't matter here since we are synchronized and // there aren't any executor services or we are going to shutdown all the // executor services immediately. // this.notifyAll(); if (!enabled) { shutdown(); } else { startup(); } } } private void startup () { LOG.info("Starting up"); synchronized (this) { if (controllerExecutor != null) { throw new IllegalStateException("Logic error: Startup should not be called while a controllerExecutor exists"); } controllerExecutor = Executors.newSingleThreadExecutor(); controllerExecutor.execute(this); } } public void shutdown () { LOG.info("Shutting down"); synchronized (this) { if (workerExecutor != null && !workerExecutor.isShutdown()) { workerExecutor.shutdownNow(); } if (controllerExecutor != null && !controllerExecutor.isShutdown()) { controllerExecutor.shutdownNow(); } workerExecutor = null; controllerExecutor = null; } } /** * Controller reconfigures rubbish collector on changes. */ @Override public void run () { synchronized (this) { while (true) { if (workerExecutor != null) { throw new IllegalStateException("Logic error workerExecutor was not null"); } LOG.info("Starting new workers"); LOG.info(String.format("workerCount=%d, rubbishCountPerLoop=%d, loopDelayMs=%d", workerCount.get(), rubbishPerLoop.get(), loopDelayMs.get())); workerExecutor = Executors.newFixedThreadPool(workerCount.get()); for (int i = workerCount.get(); i > 0; i--) { workerExecutor.execute(new Worker()); } waitForDisableOrConfigChange(); if (!enabled.get()) { LOG.info("Disabled, shutting down controller and workers"); shutdown(); return; } LOG.info("Config changed detected shutting down old workers"); if (workerExecutor != null && !workerExecutor.isShutdown()) { workerExecutor.shutdownNow(); } workerExecutor = null; } } } /** * Must be called from block synchronized on RubbishGenerator instance. */ private void waitForDisableOrConfigChange () { final int serialNo = changeSerialNo.get(); while (enabled.get() && serialNo == changeSerialNo.get()) { try { this.wait(); } catch (InterruptedException e) { LOG.info("Controller interrupted, shutting workers and controller down", e); shutdown(); return; } } } /** * Generates rubbish */ class Worker implements Runnable { final int serialNo = RubbishGenerator.this.changeSerialNo.get(); Object rubbishRef; @Override public void run () { // if the serialNo changes, exit while (serialNo == changeSerialNo.get()) { // null rubbish so don't exhaust memory with large rubbish rubbishRef = null; try { rubbishRef = generateRubbish(); loopCount.incrementAndGet(); } catch (final Exception e) { final String mesg = "Error generating rubbish, shutting worker down"; LOG.error(mesg, e); throw new RuntimeException(mesg, e); } synchronized (RubbishGenerator.this) { if (!enabled.get() || serialNo != changeSerialNo.get()) { break; } try { RubbishGenerator.this.wait(loopDelayMs.get()); } catch (final Exception e) { LOG.info("Exception, exiting", e); return; } } } LOG.info("Configuration changes occurred, shutting down"); } } /** * For each count of rubbish should consume about 40 bytes, 24 for LinkedList * Entry and 16 for Rubbish instance. * <p> * Each entry consumes 24 bytes, 8 bytes for header, 4 bytes for prev, next, * and object, and 4 bytes to pad up to multiple of 8 bytes. * * @return */ private Object generateRubbish () { final LinkedList<Rubbish> rubbish = new LinkedList<Rubbish>(); Rubbish prev = new Rubbish(null); Rubbish cur = new Rubbish(prev); prev.next = cur; for (int i = rubbishPerLoop.get(); i > 0; i--) { rubbish.add(prev); prev = cur; cur = new Rubbish(prev); prev.next = cur; } return rubbish; } /** * 8 byte header + 4 bytes for each pointer = 16 bytes * <p> * The extra linking is to make the rubbish collector have to do some work. */ class Rubbish { Rubbish prev; Rubbish next; Rubbish (final Rubbish prev) { this.prev = prev; } } }
[Advertisement]
BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!