| /* |
| * Copyright (c) 2003, 2012, 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_VM_SERVICES_LOWMEMORYDETECTOR_HPP |
| #define SHARE_VM_SERVICES_LOWMEMORYDETECTOR_HPP |
| |
| #include "memory/allocation.hpp" |
| #include "services/memoryPool.hpp" |
| #include "services/memoryService.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 Service thread (serviceThread.hpp). |
| |
| class OopClosure; |
| class MemoryPool; |
| |
| class ThresholdSupport : public CHeapObj<mtInternal> { |
| 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<mtInternal> { |
| 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 threhsold, 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 ServiceThread; |
| 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; i<num_memory_pools; i++) { |
| MemoryPool* pool = MemoryService::get_memory_pool(i); |
| |
| // if low memory detection is enabled then check if the |
| // current used exceeds the high threshold |
| if (pool->is_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_VM_SERVICES_LOWMEMORYDETECTOR_HPP |