//TestGCPause.java

import java.util.*;

public class TestGCPause {

    /*
     * Класс для организации обмена сообщениями
     */
    static class Message {
        private long sendTime;

        private long maxPause;

        private boolean messageSent;

        private boolean messageReceived;

        public long getMaxPause() {
            return maxPause;
        }

        public synchronized void sendMessage() throws InterruptedException {
            // отправить сообщение и сохранить время отправки
            messageSent = true;
            sendTime = System.currentTimeMillis();
            notify();

            // ждать пока сообщение не будет получено
            while (!messageReceived)
                wait();

            messageReceived = false;
        }

        public synchronized void waitMessage() throws InterruptedException {
            // ждать сообщение 
            while (!messageSent)
                wait();
            
            // определить время между отправкой и получением сообщения
            long receiveTime = System.currentTimeMillis();
            messageSent = false;

            // сохранить максимальное значение pause
            long pause = receiveTime - sendTime;
            if (pause > maxPause) {
                maxPause = pause;
            }

            // сообщить что сообщение было получено
            messageReceived = true;
            notify();
        }
    }

    // количество сообщений передаваемое во время тестирования
    static final int MESSAGE_NUMBER = 2000000;

    /*
     * Поток отправляющий сообщения
     */
    static class MessageSender extends Thread {
        private Message message;

        public MessageSender(Message message) {
            this.message = message;
        }

        public void run() {
            try {
                for (int i = 0; i < MESSAGE_NUMBER; i++) {
                    message.sendMessage();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /*
     * Поток получающий сообщения
     */
    static class MessageReceiver extends Thread {
        private Message message;

        public MessageReceiver(Message message) {
            this.message = message;
        }

        public void run() {
            try {
                for (int i = 0; i < MESSAGE_NUMBER; i++) {
                    message.waitMessage();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // Класс 'мусор', создающий в конструкторе несколько объектов
    static class Garbage {
        private List list = new LinkedList();

        Garbage() {
            for (int i = 0; i < 5; i++)
                list.add(new byte[1]);
        }
    }

    static List garbageStorage = new ArrayList();

    /*
     * Поток провоцирующий сборку мусора
     */
    static class GarbageProducer extends Thread {

        private volatile boolean stopped;

        public void run() {
            try {
                // период удаления созданного мусора
                long garbageRemovePeriod = 100;
                long lastGarbageRemoveTime = System.currentTimeMillis();

                // количество мусора, создаваемого за одну итерацию 
                int garbageAmount = 50000;

                while (!stopped) {
                    for (int i = 0; i < garbageAmount; i++)
                        garbageStorage.add(new Garbage());

                    // периодически удаляем созданный мусор и даём возможность сборщику мусора очистить память
                    if (System.currentTimeMillis() > (lastGarbageRemoveTime + garbageRemovePeriod)) {
                        garbageStorage.clear();
                        Thread.sleep(100);
                        lastGarbageRemoveTime = System.currentTimeMillis();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        public void stopGarbageProducer() {
            stopped = true;
        }
    }

    static List oldGenGarbageStorage = new ArrayList();

    public static void main(String[] args) throws Exception {
        /*
         * Заполняем память объектами, которые не могут быть удалены во время
         * работы приложения
         */
        long maxMemory = Runtime.getRuntime().maxMemory();
        long targetFreeMem = maxMemory / 2;
        while (Runtime.getRuntime().freeMemory() > targetFreeMem)
            oldGenGarbageStorage.add(new Garbage());

        Message message = new Message();

        GarbageProducer garbageProducer = new GarbageProducer();
        MessageSender messageSender = new MessageSender(message);
        MessageReceiver messageReceiver = new MessageReceiver(message);

        garbageProducer.start();
        messageReceiver.start();
        messageSender.start();

        messageReceiver.join();
        messageSender.join();

        garbageProducer.stopGarbageProducer();
        garbageProducer.join();

        System.out.println("Max pause: " + message.getMaxPause() + "ms");
    }
}