001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.io.util; 019 020import java.lang.management.ManagementFactory; 021import java.lang.management.MemoryType; 022import java.lang.management.MemoryUsage; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.hadoop.hbase.HConstants; 025import org.apache.hadoop.hbase.regionserver.MemStoreLAB; 026import org.apache.hadoop.hbase.util.Pair; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * Util class to calculate memory size for memstore(on heap, off heap), block cache(L1, L2) of RS. 033 */ 034@InterfaceAudience.Private 035public class MemorySizeUtil { 036 037 public static final String MEMSTORE_SIZE_KEY = "hbase.regionserver.global.memstore.size"; 038 public static final String MEMSTORE_SIZE_OLD_KEY = 039 "hbase.regionserver.global.memstore.upperLimit"; 040 public static final String MEMSTORE_SIZE_LOWER_LIMIT_KEY = 041 "hbase.regionserver.global.memstore.size.lower.limit"; 042 public static final String MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY = 043 "hbase.regionserver.global.memstore.lowerLimit"; 044 // Max global off heap memory that can be used for all memstores 045 // This should be an absolute value in MBs and not percent. 046 public static final String OFFHEAP_MEMSTORE_SIZE_KEY = 047 "hbase.regionserver.offheap.global.memstore.size"; 048 049 public static final float DEFAULT_MEMSTORE_SIZE = 0.4f; 050 // Default lower water mark limit is 95% size of memstore size. 051 public static final float DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT = 0.95f; 052 053 private static final Logger LOG = LoggerFactory.getLogger(MemorySizeUtil.class); 054 // a constant to convert a fraction to a percentage 055 private static final int CONVERT_TO_PERCENTAGE = 100; 056 057 private static final String JVM_HEAP_EXCEPTION = "Got an exception while attempting to read " 058 + "information about the JVM heap. Please submit this log information in a bug report and " 059 + "include your JVM settings, specifically the GC in use and any -XX options. Consider " 060 + "restarting the service."; 061 062 /** 063 * Return JVM memory statistics while properly handling runtime exceptions from the JVM. 064 * @return a memory usage object, null if there was a runtime exception. (n.b. you could also get 065 * -1 values back from the JVM) 066 * @see MemoryUsage 067 */ 068 public static MemoryUsage safeGetHeapMemoryUsage() { 069 MemoryUsage usage = null; 070 try { 071 usage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); 072 } catch (RuntimeException exception) { 073 LOG.warn(JVM_HEAP_EXCEPTION, exception); 074 } 075 return usage; 076 } 077 078 /** 079 * Checks whether we have enough heap memory left out after portion for Memstore and Block cache. 080 * We need atleast 20% of heap left out for other RS functions. n 081 */ 082 public static void checkForClusterFreeHeapMemoryLimit(Configuration conf) { 083 if (conf.get(MEMSTORE_SIZE_OLD_KEY) != null) { 084 LOG.warn(MEMSTORE_SIZE_OLD_KEY + " is deprecated by " + MEMSTORE_SIZE_KEY); 085 } 086 float globalMemstoreSize = getGlobalMemStoreHeapPercent(conf, false); 087 int gml = (int) (globalMemstoreSize * CONVERT_TO_PERCENTAGE); 088 float blockCacheUpperLimit = getBlockCacheHeapPercent(conf); 089 int bcul = (int) (blockCacheUpperLimit * CONVERT_TO_PERCENTAGE); 090 if ( 091 CONVERT_TO_PERCENTAGE - (gml + bcul) 092 < (int) (CONVERT_TO_PERCENTAGE * HConstants.HBASE_CLUSTER_MINIMUM_MEMORY_THRESHOLD) 093 ) { 094 throw new RuntimeException("Current heap configuration for MemStore and BlockCache exceeds " 095 + "the threshold required for successful cluster operation. " 096 + "The combined value cannot exceed 0.8. Please check " 097 + "the settings for hbase.regionserver.global.memstore.size and " 098 + "hfile.block.cache.size in your configuration. " 099 + "hbase.regionserver.global.memstore.size is " + globalMemstoreSize 100 + " hfile.block.cache.size is " + blockCacheUpperLimit); 101 } 102 } 103 104 /** 105 * Retrieve global memstore configured size as percentage of total heap. nn 106 */ 107 public static float getGlobalMemStoreHeapPercent(final Configuration c, 108 final boolean logInvalid) { 109 float limit = 110 c.getFloat(MEMSTORE_SIZE_KEY, c.getFloat(MEMSTORE_SIZE_OLD_KEY, DEFAULT_MEMSTORE_SIZE)); 111 if (limit > 0.8f || limit <= 0.0f) { 112 if (logInvalid) { 113 LOG.warn("Setting global memstore limit to default of " + DEFAULT_MEMSTORE_SIZE 114 + " because supplied value outside allowed range of (0 -> 0.8]"); 115 } 116 limit = DEFAULT_MEMSTORE_SIZE; 117 } 118 return limit; 119 } 120 121 /** 122 * Retrieve configured size for global memstore lower water mark as fraction of global memstore 123 * size. 124 */ 125 public static float getGlobalMemStoreHeapLowerMark(final Configuration conf, 126 boolean honorOldConfig) { 127 String lowMarkPercentStr = conf.get(MEMSTORE_SIZE_LOWER_LIMIT_KEY); 128 if (lowMarkPercentStr != null) { 129 float lowMarkPercent = Float.parseFloat(lowMarkPercentStr); 130 if (lowMarkPercent > 1.0f) { 131 LOG.error("Bad configuration value for " + MEMSTORE_SIZE_LOWER_LIMIT_KEY + ": " 132 + lowMarkPercent + ". Using 1.0f instead."); 133 lowMarkPercent = 1.0f; 134 } 135 return lowMarkPercent; 136 } 137 if (!honorOldConfig) return DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT; 138 String lowerWaterMarkOldValStr = conf.get(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY); 139 if (lowerWaterMarkOldValStr != null) { 140 LOG.warn(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " is deprecated. Instead use " 141 + MEMSTORE_SIZE_LOWER_LIMIT_KEY); 142 float lowerWaterMarkOldVal = Float.parseFloat(lowerWaterMarkOldValStr); 143 float upperMarkPercent = getGlobalMemStoreHeapPercent(conf, false); 144 if (lowerWaterMarkOldVal > upperMarkPercent) { 145 lowerWaterMarkOldVal = upperMarkPercent; 146 LOG.error("Value of " + MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " (" + lowerWaterMarkOldVal 147 + ") is greater than global memstore limit (" + upperMarkPercent + ") set by " 148 + MEMSTORE_SIZE_KEY + "/" + MEMSTORE_SIZE_OLD_KEY + ". Setting memstore lower limit " 149 + "to " + upperMarkPercent); 150 } 151 return lowerWaterMarkOldVal / upperMarkPercent; 152 } 153 return DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT; 154 } 155 156 /** Returns Pair of global memstore size and memory type(ie. on heap or off heap). */ 157 public static Pair<Long, MemoryType> getGlobalMemStoreSize(Configuration conf) { 158 long offheapMSGlobal = conf.getLong(OFFHEAP_MEMSTORE_SIZE_KEY, 0);// Size in MBs 159 if (offheapMSGlobal > 0) { 160 // Off heap memstore size has not relevance when MSLAB is turned OFF. We will go with making 161 // this entire size split into Chunks and pooling them in MemstoreLABPoool. We dont want to 162 // create so many on demand off heap chunks. In fact when this off heap size is configured, we 163 // will go with 100% of this size as the pool size 164 if (MemStoreLAB.isEnabled(conf)) { 165 // We are in offheap Memstore use 166 long globalMemStoreLimit = (long) (offheapMSGlobal * 1024 * 1024); // Size in bytes 167 return new Pair<>(globalMemStoreLimit, MemoryType.NON_HEAP); 168 } else { 169 // Off heap max memstore size is configured with turning off MSLAB. It makes no sense. Do a 170 // warn log and go with on heap memstore percentage. By default it will be 40% of Xmx 171 LOG.warn("There is no relevance of configuring '" + OFFHEAP_MEMSTORE_SIZE_KEY + "' when '" 172 + MemStoreLAB.USEMSLAB_KEY + "' is turned off." 173 + " Going with on heap global memstore size ('" + MEMSTORE_SIZE_KEY + "')"); 174 } 175 } 176 return new Pair<>(getOnheapGlobalMemStoreSize(conf), MemoryType.HEAP); 177 } 178 179 /** 180 * Returns the onheap global memstore limit based on the config 181 * 'hbase.regionserver.global.memstore.size'. n * @return the onheap global memstore limt 182 */ 183 public static long getOnheapGlobalMemStoreSize(Configuration conf) { 184 long max = -1L; 185 final MemoryUsage usage = safeGetHeapMemoryUsage(); 186 if (usage != null) { 187 max = usage.getMax(); 188 } 189 float globalMemStorePercent = getGlobalMemStoreHeapPercent(conf, true); 190 return ((long) (max * globalMemStorePercent)); 191 } 192 193 /** 194 * Retrieve configured size for on heap block cache as percentage of total heap. n 195 */ 196 public static float getBlockCacheHeapPercent(final Configuration conf) { 197 // L1 block cache is always on heap 198 float l1CachePercent = conf.getFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 199 HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT); 200 return l1CachePercent; 201 } 202 203 /** 204 * @param conf used to read cache configs 205 * @return the number of bytes to use for LRU, negative if disabled. 206 * @throws IllegalArgumentException if HFILE_BLOCK_CACHE_SIZE_KEY is > 1.0 207 */ 208 public static long getOnHeapCacheSize(final Configuration conf) { 209 float cachePercentage = conf.getFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 210 HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT); 211 if (cachePercentage <= 0.0001f) { 212 return -1; 213 } 214 if (cachePercentage > 1.0) { 215 throw new IllegalArgumentException( 216 HConstants.HFILE_BLOCK_CACHE_SIZE_KEY + " must be between 0.0 and 1.0, and not > 1.0"); 217 } 218 long max = -1L; 219 final MemoryUsage usage = safeGetHeapMemoryUsage(); 220 if (usage != null) { 221 max = usage.getMax(); 222 } 223 float onHeapCacheFixedSize = 224 (float) conf.getLong(HConstants.HFILE_ONHEAP_BLOCK_CACHE_FIXED_SIZE_KEY, 225 HConstants.HFILE_ONHEAP_BLOCK_CACHE_FIXED_SIZE_DEFAULT) / max; 226 // Calculate the amount of heap to give the heap. 227 return (onHeapCacheFixedSize > 0 && onHeapCacheFixedSize < cachePercentage) 228 ? (long) (max * onHeapCacheFixedSize) 229 : (long) (max * cachePercentage); 230 } 231 232 /** 233 * @param conf used to read config for bucket cache size. 234 * @return the number of bytes to use for bucket cache, negative if disabled. 235 */ 236 public static long getBucketCacheSize(final Configuration conf) { 237 // Size configured in MBs 238 float bucketCacheSize = conf.getFloat(HConstants.BUCKET_CACHE_SIZE_KEY, 0F); 239 if (bucketCacheSize < 1) { 240 throw new IllegalArgumentException("Bucket Cache should be minimum 1 MB in size." 241 + "Configure 'hbase.bucketcache.size' with > 1 value"); 242 } 243 return (long) (bucketCacheSize * 1024 * 1024); 244 } 245 246}