/* * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #ifndef SHARE_SERVICES_LOWMEMORYDETECTOR_HPP #define SHARE_SERVICES_LOWMEMORYDETECTOR_HPP #include "memory/allocation.hpp" #include "services/memoryPool.hpp" #include "services/memoryService.hpp" #include "services/memoryUsage.hpp" // Low Memory Detection Support // Two memory alarms in the JDK (we called them sensors). // - Heap memory sensor // - Non-heap memory sensor // When the VM detects if the memory usage of a memory pool has reached // or exceeded its threshold, it will trigger the sensor for the type // of the memory pool (heap or nonheap or both). // // If threshold == -1, no low memory detection is supported and // the threshold value is not allowed to be changed. // If threshold == 0, no low memory detection is performed for // that memory pool. The threshold can be set to any non-negative // value. // // The default threshold of the Hotspot memory pools are: // Eden space -1 // Survivor space 1 -1 // Survivor space 2 -1 // Old generation 0 // Perm generation 0 // CodeCache 0 // // For heap memory, detection will be performed when GC finishes // and also in the slow path allocation. // For Code cache, detection will be performed in the allocation // and deallocation. // // May need to deal with hysteresis effect. // // Memory detection code runs in the Notification thread (notificationThread.hpp). class OopClosure; class MemoryPool; class ThresholdSupport : public CHeapObj { private: bool _support_high_threshold; bool _support_low_threshold; size_t _high_threshold; size_t _low_threshold; public: ThresholdSupport(bool support_high, bool support_low) { _support_high_threshold = support_high; _support_low_threshold = support_low; _high_threshold = 0; _low_threshold= 0; } size_t high_threshold() const { return _high_threshold; } size_t low_threshold() const { return _low_threshold; } bool is_high_threshold_supported() { return _support_high_threshold; } bool is_low_threshold_supported() { return _support_low_threshold; } bool is_high_threshold_crossed(MemoryUsage usage) { if (_support_high_threshold && _high_threshold > 0) { return (usage.used() >= _high_threshold); } return false; } bool is_low_threshold_crossed(MemoryUsage usage) { if (_support_low_threshold && _low_threshold > 0) { return (usage.used() < _low_threshold); } return false; } size_t set_high_threshold(size_t new_threshold) { assert(_support_high_threshold, "can only be set if supported"); assert(new_threshold >= _low_threshold, "new_threshold must be >= _low_threshold"); size_t prev = _high_threshold; _high_threshold = new_threshold; return prev; } size_t set_low_threshold(size_t new_threshold) { assert(_support_low_threshold, "can only be set if supported"); assert(new_threshold <= _high_threshold, "new_threshold must be <= _high_threshold"); size_t prev = _low_threshold; _low_threshold = new_threshold; return prev; } }; class SensorInfo : public CHeapObj { private: instanceOop _sensor_obj; bool _sensor_on; size_t _sensor_count; // before the actual sensor on flag and sensor count are set // we maintain the number of pending triggers and clears. // _pending_trigger_count means the number of pending triggers // and the sensor count should be incremented by the same number. int _pending_trigger_count; // _pending_clear_count takes precedence if it's > 0 which // indicates the resulting sensor will be off // Sensor trigger requests will reset this clear count to // indicate the resulting flag should be on. int _pending_clear_count; MemoryUsage _usage; void clear(int count, TRAPS); void trigger(int count, TRAPS); public: SensorInfo(); void set_sensor(instanceOop sensor) { assert(_sensor_obj == NULL, "Should be set only once"); _sensor_obj = sensor; } bool has_pending_requests() { return (_pending_trigger_count > 0 || _pending_clear_count > 0); } int pending_trigger_count() { return _pending_trigger_count; } int pending_clear_count() { return _pending_clear_count; } // When this method is used, the memory usage is monitored // as a gauge attribute. High and low thresholds are designed // to provide a hysteresis mechanism to avoid repeated triggering // of notifications when the attribute value makes small oscillations // around the high or low threshold value. // // The sensor will be triggered if: // (1) the usage is crossing above the high threshold and // the sensor is currently off and no pending // trigger requests; or // (2) the usage is crossing above the high threshold and // the sensor will be off (i.e. sensor is currently on // and has pending clear requests). // // Subsequent crossings of the high threshold value do not cause // any triggers unless the usage becomes less than the low threshold. // // The sensor will be cleared if: // (1) the usage is crossing below the low threshold and // the sensor is currently on and no pending // clear requests; or // (2) the usage is crossing below the low threshold and // the sensor will be on (i.e. sensor is currently off // and has pending trigger requests). // // Subsequent crossings of the low threshold value do not cause // any clears unless the usage becomes greater than or equal // to the high threshold. // // If the current level is between high and low threshold, no change. // void set_gauge_sensor_level(MemoryUsage usage, ThresholdSupport* high_low_threshold); // When this method is used, the memory usage is monitored as a // simple counter attribute. The sensor will be triggered // whenever the usage is crossing the threshold to keep track // of the number of times the VM detects such a condition occurs. // // The sensor will be triggered if: // - the usage is crossing above the high threshold regardless // of the current sensor state. // // The sensor will be cleared if: // (1) the usage is crossing below the low threshold and // the sensor is currently on; or // (2) the usage is crossing below the low threshold and // the sensor will be on (i.e. sensor is currently off // and has pending trigger requests). // void set_counter_sensor_level(MemoryUsage usage, ThresholdSupport* counter_threshold); void process_pending_requests(TRAPS); void oops_do(OopClosure* f); #ifndef PRODUCT // printing on default output stream; void print(); #endif // PRODUCT }; class LowMemoryDetector : public AllStatic { friend class LowMemoryDetectorDisabler; friend class NotificationThread; private: // true if any collected heap has low memory detection enabled static volatile bool _enabled_for_collected_pools; // > 0 if temporary disabed static volatile jint _disabled_count; static void check_memory_usage(); static bool has_pending_requests(); static bool temporary_disabled() { return _disabled_count > 0; } static void disable() { Atomic::inc(&_disabled_count); } static void enable() { Atomic::dec(&_disabled_count); } static void process_sensor_changes(TRAPS); public: static void detect_low_memory(); static void detect_low_memory(MemoryPool* pool); static void detect_after_gc_memory(MemoryPool* pool); static bool is_enabled(MemoryPool* pool) { // low memory detection is enabled for collected memory pools // iff one of the collected memory pool has a sensor and the // threshold set non-zero if (pool->usage_sensor() == NULL) { return false; } else { ThresholdSupport* threshold_support = pool->usage_threshold(); return (threshold_support->is_high_threshold_supported() ? (threshold_support->high_threshold() > 0) : false); } } // indicates if low memory detection is enabled for any collected // memory pools static inline bool is_enabled_for_collected_pools() { return !temporary_disabled() && _enabled_for_collected_pools; } // recompute enabled flag static void recompute_enabled_for_collected_pools(); // low memory detection for collected memory pools. static inline void detect_low_memory_for_collected_pools() { // no-op if low memory detection not enabled if (!is_enabled_for_collected_pools()) { return; } int num_memory_pools = MemoryService::num_memory_pools(); for (int i=0; iis_collected_pool() && is_enabled(pool)) { size_t used = pool->used_in_bytes(); size_t high = pool->usage_threshold()->high_threshold(); if (used > high) { detect_low_memory(pool); } } } } }; class LowMemoryDetectorDisabler: public StackObj { public: LowMemoryDetectorDisabler() { LowMemoryDetector::disable(); } ~LowMemoryDetectorDisabler() { assert(LowMemoryDetector::temporary_disabled(), "should be disabled!"); LowMemoryDetector::enable(); } }; #endif // SHARE_SERVICES_LOWMEMORYDETECTOR_HPP