Как воспроизвести Java OutOfMemoryError - превышен верхний предел GC

Мой подход состоял в том, чтобы создать сотни тысяч локальных коллекций и заполнить их случайными строками, что-то вроде этого:

    SecureRandom random = new SecureRandom();
    for(int i = 0 ; i < 100000 ; i++){
        HashMap<String, String> map = new HashMap<String, String>();
        for(int j = 0 ; j < 30 ; j++){
            map.put(new BigInteger(130, random).toString(32), new BigInteger(130, random).toString(32));
        }
    }

Я также предоставил параметр -XX: + UseGCOverheadLimit jvm, но не могу получить ошибку. Есть ли простой и надежный способ/взломать эту ошибку?

Ответ 1

Поскольку вы не приняли никакого ответа, я предполагаю, что никто из них не работал на вас. Вот один из них. Но сначала рассмотрим условия, которые вызывают эту ошибку:

Параллельный коллектор выкинет OutOfMemoryError, если слишком много времени тратится на сбор мусора: если более 98% общего времени тратится на сбор мусора и восстанавливается менее 2% кучи

Итак, вы должны потреблять почти всю кучу, держать ее выделенной, а затем выделять много мусора. Ввод большого количества материалов в Map не сделает этого для вас.

public static void main(String[] argv)
throws Exception
{
    List<Object> fixedData = consumeAvailableMemory();
    while (true)
    {
        Object data = new byte[64 * 1024 - 1];
    }
}


private static List<Object> consumeAvailableMemory()
throws Exception
{
    LinkedList<Object> holder = new LinkedList<Object>();
    while (true)
    {
        try
        {
            holder.add(new byte[128 * 1024]);
        }
        catch (OutOfMemoryError ex)
        {
            holder.removeLast();
            return holder;
        }
    }
}

Метод consumeAvailableMemory() заполняет кучу относительно небольшими фрагментами памяти. "Относительно мало" важно, потому что JVM будет помещать "большие" объекты (512 k байт в моем опыте) непосредственно в поколение поколений, оставив молодое поколение пустым.

После того, как я использовал большую часть кучи, я просто выделяю и отбрасываю. Меньший размер блока на этом этапе важен: я знаю, что у меня будет достаточно памяти для хотя бы одного распределения, но, вероятно, не более двух. Это будет держать GC активным.

Запуск этого вызывает желаемую ошибку за секунду:

> java -Xms1024m -Xmx1024m GCOverheadTrigger
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at GCOverheadTrigger.main(GCOverheadTrigger.java:12)

И, для полноты, здесь JVM, который я использую:

> java -version
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)

И теперь мой вопрос для вас: почему в мире вы хотели бы это сделать?

Ответ 2

Это:

HashMap<String, String> map = new HashMap<String, String>();

находится в пределах цикла и нет внешних (долгосрочных) ссылок на карту, созданную при повторении цикла. Следовательно, каждая карта будет иметь право на сбор мусора в конце каждой итерации цикла.

Вам нужно создать коллекцию объектов вне цикла и использовать цикл для заполнения этой коллекции.

Ответ 3

Я думаю, что это должно сделать трюк... если вы запустите его достаточно долго:

HashMap<Long, String> map = new HashMap<Long, String>();
for (long i = 0; true; i++) {
    for (int j = 0; j < 100; j++) {
        String s = "" + j;
        map.put(i, s);
    }
}

То, что я делаю, медленно наращивает количество ненужного мусора, одновременно создавая значительное количество мусора. Если это выполняется до тех пор, пока не мусор не заполнит почти всю кучу, GC достигнет точки, в которой% времени, затраченного на сбор мусора, превышает порог.

Ответ 4

Если вы создали так много объектов, используя новое ключевое слово, а не мусор, собранный для объектов без ссылок, он будет вызывать ошибку кучного пространства, потому что для создания новых объектов недостаточно памяти. Пример: -

public class heapSpaceError {

   public void method() {
        int value = 10;
        for (int i = 0; i<100; i++) {
             int count = 5;
             int[] a = new int[value];
             value = value * 5;
             System.out.println(a);
        }
   }

   public static void main(String[] args) {
          heapSpaceError error = new heapSpaceError();
          error.method();
   }
}

Ссылка Как сгенерировать StackOverflowError и OutOfMemoryError программно в Java