| /* | |
| * Copyright (c) 2021, 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_MEMORY_METASPACECRITICALALLOCATION_HPP | |
| #define SHARE_MEMORY_METASPACECRITICALALLOCATION_HPP | |
| #include "memory/allStatic.hpp" | |
| #include "memory/metaspace.hpp" | |
| class MetadataAllocationRequest; | |
| class ClassLoaderData; | |
| // == Critical allocation support == | |
| // | |
| // The critical allocation support has the purpose of preventing starvation of failed | |
| // metadata allocations that need a GC, in particular for concurrent GCs. | |
| // A "critical" allocation request is registered, then a concurrent full GC is executed. | |
| // When there is any critical allocation present in the system, allocations compete for | |
| // a global lock, so that allocations can be shut out from the concurrent purge() call, | |
| // which takes the same lock. The reasoning is that we gather all the critical allocations | |
| // that are one more failure away from throwing metaspace OOM, in a queue before the GC, | |
| // then free up metaspace due to class unloading in the purge() operation of that GC, | |
| // and satisfy the registered critical allocations. This allows the critical allocations | |
| // to get precedence over normal metaspace allocations, so that the critical allocations | |
| // that are about to throw, do not get starved by other metaspace allocations that have | |
| // not gone through the same dance. | |
| // | |
| // The solution has an intended accuracy of not one allocation, but one per thread. What | |
| // I mean by that, is that the allocations are allowed to throw if they got starved by | |
| // one metaspace allocation per thread, even though a more complicated dance could have | |
| // survived that situation in theory. The motivation is that we are at this point so close | |
| // to being out of memory, and the VM is not having a good time, so the user really ought | |
| // to increase the amount of available metaspace anyway, instead of GC:ing around more | |
| // to satisfy a very small number of additional allocations. But it does solve pathological | |
| // unbounded starvation scenarios where OOM can get thrown even though most of metaspace | |
| // is full of dead metadata. | |
| // | |
| // The contract for this to work for a given GC is that GCCause::_metadata_GC_clear_soft_refs | |
| // yields a full synchronous GC that unloads metaspace. And it is only intended to be used | |
| // by GCs with concurrent class unloading. | |
| class MetaspaceCriticalAllocation : public AllStatic { | |
| friend class MetadataAllocationRequest; | |
| static volatile bool _has_critical_allocation; | |
| static MetadataAllocationRequest* _requests_head; | |
| static MetadataAllocationRequest* _requests_tail; | |
| static void unlink(MetadataAllocationRequest* curr, MetadataAllocationRequest* prev); | |
| static void add(MetadataAllocationRequest* request); | |
| static void remove(MetadataAllocationRequest* request); | |
| static bool try_allocate_critical(MetadataAllocationRequest* request); | |
| static void wait_for_purge(MetadataAllocationRequest* request); | |
| public: | |
| static void block_if_concurrent_purge(); | |
| static void process(); | |
| static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size, Metaspace::MetadataType type); | |
| }; | |
| #endif // SHARE_MEMORY_METASPACECRITICALALLOCATION_HPP |