/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * 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.hazelcast.instance; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.OutOfMemoryHandler; import com.hazelcast.memory.MemoryUnit; import com.hazelcast.spi.annotation.PrivateApi; import com.hazelcast.util.EmptyStatement; import com.hazelcast.util.MemoryInfoAccessor; import com.hazelcast.util.RuntimeMemoryInfoAccessor; /** * Default OutOfMemoryHandler implementation that tries to release local resources (threads, connections, memory) * immediately and disconnects members from the rest of the cluster. */ @PrivateApi public class DefaultOutOfMemoryHandler extends OutOfMemoryHandler { /* * Known OOME error messages: * * java.lang.OutOfMemoryError: Java heap space * java.lang.OutOfMemoryError: PermGen space * java.lang.OutOfMemoryError: GC overhead limit exceeded * java.lang.OutOfMemoryError: unable to create new native thread * java.lang.OutOfMemoryError: nativeGetNewTLA * java.lang.OutOfMemoryError: Requested array size exceeds VM limit * java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space? * java.lang.OutOfMemoryError: <reason> <stack trace> (Native method) */ public static final String FREE_MAX_PERCENTAGE_PROP = "hazelcast.oome.handler.free_max.percentage"; static final String GC_OVERHEAD_LIMIT_EXCEEDED = "GC overhead limit exceeded"; private static final int HUNDRED_PERCENT = 100; private static final int FIFTY_PERCENT = 50; private static final long MAX_TOTAL_DELTA = MemoryUnit.MEGABYTES.toBytes(1); private static final double FREE_MAX_RATIO; static { int percentage = Integer.parseInt(System.getProperty(FREE_MAX_PERCENTAGE_PROP, "10")); if (percentage < 1 || percentage > FIFTY_PERCENT) { throw new IllegalArgumentException("'" + FREE_MAX_PERCENTAGE_PROP + "' should be in [1, 50] range! Current: " + percentage); } FREE_MAX_RATIO = ((double) percentage) / HUNDRED_PERCENT; } private final double freeVersusMaxRatio; private final MemoryInfoAccessor memoryInfoAccessor; public DefaultOutOfMemoryHandler() { this(FREE_MAX_RATIO); } public DefaultOutOfMemoryHandler(double freeVersusMaxRatio) { this.freeVersusMaxRatio = freeVersusMaxRatio; this.memoryInfoAccessor = new RuntimeMemoryInfoAccessor(); } public DefaultOutOfMemoryHandler(double freeVersusMaxRatio, MemoryInfoAccessor memoryInfoAccessor) { this.freeVersusMaxRatio = freeVersusMaxRatio; this.memoryInfoAccessor = memoryInfoAccessor; } @Override public void onOutOfMemory(OutOfMemoryError oome, HazelcastInstance[] hazelcastInstances) { for (HazelcastInstance instance : hazelcastInstances) { if (instance instanceof HazelcastInstanceImpl) { OutOfMemoryHandlerHelper.tryCloseConnections(instance); OutOfMemoryHandlerHelper.tryShutdown(instance); } } try { oome.printStackTrace(System.err); } catch (Throwable ignored) { EmptyStatement.ignore(ignored); } } @Override public boolean shouldHandle(OutOfMemoryError oome) { try { if (GC_OVERHEAD_LIMIT_EXCEEDED.equals(oome.getMessage())) { return true; } long maxMemory = memoryInfoAccessor.getMaxMemory(); long totalMemory = memoryInfoAccessor.getTotalMemory(); // if total-memory has not reached to max-memory // then no need to handle this if (totalMemory < maxMemory - MAX_TOTAL_DELTA) { return false; } // since previous total vs max memory comparison // freeMemory should return the same result // with = (maxMemory - totalMemory + freeMemory) long freeMemory = memoryInfoAccessor.getFreeMemory(); if (freeMemory > maxMemory * freeVersusMaxRatio) { return false; } } catch (Throwable ignored) { EmptyStatement.ignore(ignored); } return true; } }