| /* |
| * Copyright 2000-2013 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.openapi.util; |
| |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.util.ConcurrencyUtil; |
| import com.intellij.util.containers.WeakList; |
| |
| import javax.management.Notification; |
| import javax.management.NotificationEmitter; |
| import javax.management.NotificationListener; |
| import java.lang.management.ManagementFactory; |
| import java.lang.management.MemoryNotificationInfo; |
| import java.lang.management.MemoryPoolMXBean; |
| import java.lang.management.MemoryType; |
| import java.util.List; |
| import java.util.concurrent.ArrayBlockingQueue; |
| import java.util.concurrent.ThreadPoolExecutor; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * @author Eugene Zhuravlev |
| * Date: Aug 24, 2010 |
| */ |
| public class LowMemoryWatcher { |
| private static final long MEM_THRESHOLD = 5 /*MB*/ * 1024 * 1024; |
| |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.LowMemoryWatcher"); |
| |
| private static final List<LowMemoryWatcher> ourInstances = new WeakList<LowMemoryWatcher>(); |
| private static final ThreadPoolExecutor ourExecutor = new ThreadPoolExecutor(0, 1, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2), ConcurrencyUtil.newNamedThreadFactory("LowMemoryWatcher janitor")); |
| private static boolean ourSubmitted; |
| private static final Runnable ourJanitor = new Runnable() { |
| @Override |
| public void run() { |
| try { |
| for (LowMemoryWatcher watcher : ourInstances) { |
| try { |
| watcher.myRunnable.run(); |
| } |
| catch (Throwable e) { |
| LOG.info(e); |
| } |
| } |
| } |
| finally { |
| synchronized (ourJanitor) { |
| //noinspection AssignmentToStaticFieldFromInstanceMethod |
| ourSubmitted = false; |
| } |
| } |
| } |
| }; |
| |
| private final Runnable myRunnable; |
| |
| static { |
| for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) { |
| if (bean.getType() == MemoryType.HEAP && bean.isUsageThresholdSupported()) { |
| long threshold = bean.getUsage().getMax() - MEM_THRESHOLD; |
| if (threshold > 0) { |
| bean.setUsageThreshold(threshold); |
| bean.setCollectionUsageThreshold(threshold); |
| } |
| } |
| } |
| ((NotificationEmitter)ManagementFactory.getMemoryMXBean()).addNotificationListener(new NotificationListener() { |
| @Override |
| public void handleNotification(Notification n, Object hb) { |
| if (MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED.equals(n.getType()) || MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED.equals(n.getType())) { |
| synchronized (ourJanitor) { |
| if (!ourSubmitted) { |
| //noinspection AssignmentToStaticFieldFromInstanceMethod |
| ourSubmitted = true; |
| ourExecutor.submit(ourJanitor); |
| } |
| } |
| } |
| } |
| }, null, null); |
| } |
| |
| /** |
| * Registers a runnable to run on low memory events |
| * @return a LowMemoryWatcher instance holding the runnable. This instance should be kept in memory while the |
| * low memory notification functionality is needed. As soon as it's garbage-collected, the runnable won't receive any further notifications. |
| */ |
| public static LowMemoryWatcher register(Runnable runnable) { |
| return new LowMemoryWatcher(runnable); |
| } |
| |
| /** |
| * Registers a runnable to run on low memory events. The notifications will be issued until parentDisposable is disposed. |
| */ |
| public static void register(Runnable runnable, Disposable parentDisposable) { |
| final Ref<LowMemoryWatcher> watcher = Ref.create(new LowMemoryWatcher(runnable)); |
| Disposer.register(parentDisposable, new Disposable() { |
| @Override |
| public void dispose() { |
| watcher.get().stop(); |
| watcher.set(null); |
| } |
| }); |
| } |
| |
| private LowMemoryWatcher(Runnable runnable) { |
| myRunnable = runnable; |
| ourInstances.add(this); |
| } |
| |
| public void stop() { |
| ourInstances.remove(this); |
| } |
| |
| } |