/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.util;

import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.TestOnly;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;

public class GCUtil {
  /**
   * Try to force VM to collect all the garbage along with soft- and weak-references.
   * Method doesn't guarantee to succeed, and should not be used in the production code.
   */
  @TestOnly
  public static void tryForceGC() {
    tryGcSoftlyReachableObjects();
    WeakReference<Object> weakReference = new WeakReference<Object>(new Object());
    do {
      System.gc();
    }
    while (weakReference.get() != null);
  }

  /**
   * Try to force VM to collect soft references if possible.
   * Method doesn't guarantee to succeed, and should not be used in the production code.
   */
  @TestOnly
  public static void tryGcSoftlyReachableObjects() {
    ReferenceQueue<Object> q = new ReferenceQueue<Object>();
    SoftReference<Object> ref = new SoftReference<Object>(new Object(), q);
    ArrayList<Object> list = ContainerUtil.newArrayListWithCapacity(100 + useReference(ref));
    for (int i = 0; i < 100; i++) {
      System.gc();
      if (q.poll() != null) {
        break;
      }
      TimeoutUtil.sleep(10);
      long bytes = Math.min(Runtime.getRuntime().freeMemory() / 2, Integer.MAX_VALUE / 2);
      list.add(new SoftReference<byte[]>(new byte[(int)bytes]));

      // use ref is important as to loop to finish with several iterations: long runs of the method (~80 run of PsiModificationTrackerTest)
      // discovered 'ref' being collected and loop iterated 100 times taking a lot of time
      list.ensureCapacity(list.size() + useReference(ref));
    }
  }

  private static int useReference(SoftReference<Object> ref) {
    Object o = ref.get();
    return o == null ? 0 : Math.abs(o.hashCode()) % 10;
  }
}

@@ -622,16 +622,61 @@
 .util;%0A%0A
+import com.intellij.openapi.util.SystemInfo;%0A
 import c
@@ -718,16 +718,16 @@
 erUtil;%0A
-
 import o
@@ -863,32 +863,65 @@
 .WeakReference;%0A
+import java.lang.reflect.Method;%0A
 import java.util
@@ -1611,24 +1611,54 @@
 Objects() %7B%0A
+    tryClearBeanInfoCache();%0A%0A
     Referenc
@@ -2557,24 +2557,24 @@
  ref.get();%0A
-
     return o
@@ -2620,11 +2620,747 @@
  10;%0A  %7D
+%0A%0A  private static final boolean ourHasBeanInfoCache = SystemInfo.isJavaVersionAtLeast(%221.7%22);%0A%0A  private static void tryClearBeanInfoCache() %7B%0A    if (ourHasBeanInfoCache) %7B%0A      try %7B%0A        Class%3C?%3E aClass = Class.forName(%22java.beans.ThreadGroupContext%22);%0A        Method getContextMethod = aClass.getDeclaredMethod(%22getContext%22);%0A        getContextMethod.setAccessible(true);%0A        Object contextForThreadGroup = getContextMethod.invoke(null);%0A        Method clearBeanInfoCacheMethod = contextForThreadGroup.getClass().getDeclaredMethod(%22clearBeanInfoCache%22);%0A        clearBeanInfoCacheMethod.setAccessible(true);%0A        clearBeanInfoCacheMethod.invoke(contextForThreadGroup);%0A      %7D%0A      catch (Throwable ignore) %7B%7D%0A    %7D%0A  %7D
 %0A%7D%0A

purge bean info cache on low memory