@SuppressWarnings({"UseOfSystemOutOrSystemErr", "StringConcatenationInsideStringBufferAppend"})
  static boolean allocateTonsOfMemory(@NotNull StringBuilder log, @NotNull Runnable runWhileWaiting, @NotNull BooleanSupplier until) {
    long freeMemory = Runtime.getRuntime().freeMemory();
    log.append("Free memory: " + freeMemory + "\n");

    int liveChunks = 0;
    ReferenceQueue<Object> queue = new ReferenceQueue<>();

    List<SoftReference<?>> list = new ArrayList<>();
    try {
      for (int i = 0; i < 1000 && !until.getAsBoolean(); i++) {
        runWhileWaiting.run();
        while (queue.poll() != null) {
          liveChunks--;
        }

        // full gc is caused by allocation of large enough array below, SoftReference will be cleared after two full gc
        int bytes = Math.min((int)(Runtime.getRuntime().totalMemory() / 20), Integer.MAX_VALUE / 2);
        log.append("Iteration " + i + ", allocating new byte[" + bytes + "]" +
                   ", live chunks: " + liveChunks +
                   ", free memory: " + Runtime.getRuntime().freeMemory() + "\n");

        list.add(new SoftReference<Object>(new byte[bytes], queue));
        liveChunks++;

        if (i > 0 && i % 100 == 0 && !until.getAsBoolean()) {
          log.append("  Calling System.gc()\n");
          System.gc();
        }
      }
    }
    catch (OutOfMemoryError e) {
      int size = list.size();
      list.clear();
      //noinspection CallToPrintStackTrace
      e.printStackTrace();
      System.err.println("Log: " + log + "freeMemory() now: " + Runtime.getRuntime().freeMemory() + "; list.size(): " + size);
      System.err.println(ThreadDumper.dumpThreadsToString());
      throw e;
    }
    finally {
      // do not leave a chance for our created SoftReference's content to lie around until next full GC
      for (Reference<?> createdReference : list) {
        createdReference.clear();
      }
    }
    return until.getAsBoolean();
  }
@SuppressWarnings({"UseOfSystemOutOrSystemErr", "StringConcatenationInsideStringBufferAppend"})
  static boolean allocateTonsOfMemory(@NotNull StringBuilder log, @NotNull Runnable runWhileWaiting, @NotNull BooleanSupplier until)