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 */ 018 019package org.apache.hadoop.hbase.io; 020 021import static org.apache.hadoop.hbase.io.ByteBuffAllocator.HEAP; 022import static org.apache.hadoop.hbase.io.ByteBuffAllocator.getHeapAllocationRatio; 023import static org.junit.Assert.assertEquals; 024import static org.junit.Assert.assertFalse; 025import static org.junit.Assert.assertTrue; 026import static org.junit.Assert.fail; 027 028import java.nio.ByteBuffer; 029 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.nio.ByteBuff; 033import org.apache.hadoop.hbase.nio.MultiByteBuff; 034import org.apache.hadoop.hbase.nio.SingleByteBuff; 035import org.apache.hadoop.hbase.testclassification.RPCTests; 036import org.apache.hadoop.hbase.testclassification.SmallTests; 037import org.junit.Assert; 038import org.junit.ClassRule; 039import org.junit.Test; 040import org.junit.experimental.categories.Category; 041 042@Category({ RPCTests.class, SmallTests.class }) 043public class TestByteBuffAllocator { 044 045 @ClassRule 046 public static final HBaseClassTestRule CLASS_RULE = 047 HBaseClassTestRule.forClass(TestByteBuffAllocator.class); 048 049 @Test 050 public void testAllocateByteBuffToReadInto() { 051 int maxBuffersInPool = 10; 052 int bufSize = 6 * 1024; 053 ByteBuffAllocator alloc = new ByteBuffAllocator(true, maxBuffersInPool, bufSize, bufSize / 6); 054 assertEquals(0, alloc.getUsedBufferCount()); 055 056 ByteBuff buff = alloc.allocate(10 * bufSize); 057 assertEquals(61440, alloc.getPoolAllocationBytes()); 058 assertEquals(0, alloc.getHeapAllocationBytes()); 059 assertEquals(10, alloc.getUsedBufferCount()); 060 buff.release(); 061 // When the request size is less than 1/6th of the pool buffer size. We should use on demand 062 // created on heap Buffer 063 buff = alloc.allocate(200); 064 assertTrue(buff.hasArray()); 065 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 066 assertEquals(maxBuffersInPool, alloc.getTotalBufferCount()); 067 assertEquals(61440, alloc.getPoolAllocationBytes()); 068 assertEquals(200, alloc.getHeapAllocationBytes()); 069 assertEquals(10, alloc.getUsedBufferCount()); 070 buff.release(); 071 // When the request size is > 1/6th of the pool buffer size. 072 buff = alloc.allocate(1024); 073 assertFalse(buff.hasArray()); 074 assertEquals(maxBuffersInPool - 1, alloc.getFreeBufferCount()); 075 assertEquals(67584, alloc.getPoolAllocationBytes()); 076 assertEquals(200, alloc.getHeapAllocationBytes()); 077 assertEquals(10, alloc.getUsedBufferCount()); 078 buff.release();// ByteBuff Recycler#free should put back the BB to pool. 079 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 080 // Request size> pool buffer size 081 buff = alloc.allocate(7 * 1024); 082 assertFalse(buff.hasArray()); 083 assertTrue(buff instanceof MultiByteBuff); 084 ByteBuffer[] bbs = buff.nioByteBuffers(); 085 assertEquals(2, bbs.length); 086 assertTrue(bbs[0].isDirect()); 087 assertTrue(bbs[1].isDirect()); 088 assertEquals(6 * 1024, bbs[0].limit()); 089 assertEquals(1024, bbs[1].limit()); 090 assertEquals(maxBuffersInPool - 2, alloc.getFreeBufferCount()); 091 assertEquals(79872, alloc.getPoolAllocationBytes()); 092 assertEquals(200, alloc.getHeapAllocationBytes()); 093 assertEquals(10, alloc.getUsedBufferCount()); 094 buff.release(); 095 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 096 097 buff = alloc.allocate(6 * 1024 + 200); 098 assertFalse(buff.hasArray()); 099 assertTrue(buff instanceof MultiByteBuff); 100 bbs = buff.nioByteBuffers(); 101 assertEquals(2, bbs.length); 102 assertTrue(bbs[0].isDirect()); 103 assertFalse(bbs[1].isDirect()); 104 assertEquals(6 * 1024, bbs[0].limit()); 105 assertEquals(200, bbs[1].limit()); 106 assertEquals(maxBuffersInPool - 1, alloc.getFreeBufferCount()); 107 assertEquals(86016, alloc.getPoolAllocationBytes()); 108 assertEquals(400, alloc.getHeapAllocationBytes()); 109 assertEquals(10, alloc.getUsedBufferCount()); 110 buff.release(); 111 assertEquals(maxBuffersInPool, alloc.getFreeBufferCount()); 112 113 alloc.allocate(bufSize * (maxBuffersInPool - 1)); 114 assertEquals(141312, alloc.getPoolAllocationBytes()); 115 assertEquals(400, alloc.getHeapAllocationBytes()); 116 assertEquals(10, alloc.getUsedBufferCount()); 117 118 buff = alloc.allocate(20 * 1024); 119 assertFalse(buff.hasArray()); 120 assertTrue(buff instanceof MultiByteBuff); 121 bbs = buff.nioByteBuffers(); 122 assertEquals(2, bbs.length); 123 assertTrue(bbs[0].isDirect()); 124 assertFalse(bbs[1].isDirect()); 125 assertEquals(6 * 1024, bbs[0].limit()); 126 assertEquals(14 * 1024, bbs[1].limit()); 127 assertEquals(0, alloc.getFreeBufferCount()); 128 assertEquals(147456, alloc.getPoolAllocationBytes()); 129 assertEquals(14736, alloc.getHeapAllocationBytes()); 130 assertEquals(10, alloc.getUsedBufferCount()); 131 132 buff.release(); 133 assertEquals(1, alloc.getFreeBufferCount()); 134 alloc.allocateOneBuffer(); 135 assertEquals(153600, alloc.getPoolAllocationBytes()); 136 assertEquals(14736, alloc.getHeapAllocationBytes()); 137 assertEquals(10, alloc.getUsedBufferCount()); 138 139 buff = alloc.allocate(7 * 1024); 140 assertTrue(buff.hasArray()); 141 assertTrue(buff instanceof SingleByteBuff); 142 assertEquals(7 * 1024, buff.nioByteBuffers()[0].limit()); 143 assertEquals(153600, alloc.getPoolAllocationBytes()); 144 assertEquals(21904, alloc.getHeapAllocationBytes()); 145 assertEquals(10, alloc.getUsedBufferCount()); 146 buff.release(); 147 } 148 149 @Test 150 public void testNegativeAllocatedSize() { 151 int maxBuffersInPool = 10; 152 ByteBuffAllocator allocator = new ByteBuffAllocator(true, maxBuffersInPool, 6 * 1024, 1024); 153 try { 154 allocator.allocate(-1); 155 fail("Should throw exception when size < 0"); 156 } catch (IllegalArgumentException e) { 157 // expected exception 158 } 159 ByteBuff bb = allocator.allocate(0); 160 assertEquals(0, allocator.getHeapAllocationBytes()); 161 bb.release(); 162 } 163 164 @Test 165 public void testAllocateOneBuffer() { 166 // Allocate from on-heap 167 ByteBuffAllocator allocator = HEAP; 168 ByteBuff buf = allocator.allocateOneBuffer(); 169 assertTrue(buf.hasArray()); 170 assertEquals(ByteBuffAllocator.DEFAULT_BUFFER_SIZE, buf.remaining()); 171 buf.release(); 172 173 // Allocate from off-heap 174 int bufSize = 10; 175 allocator = new ByteBuffAllocator(true, 1, 10, 3); 176 buf = allocator.allocateOneBuffer(); 177 assertFalse(buf.hasArray()); 178 assertEquals(buf.remaining(), bufSize); 179 // The another one will be allocated from on-heap because the pool has only one ByteBuffer, 180 // and still not be cleaned. 181 ByteBuff buf2 = allocator.allocateOneBuffer(); 182 assertTrue(buf2.hasArray()); 183 assertEquals(buf2.remaining(), bufSize); 184 // free the first one 185 buf.release(); 186 // The next one will be off-heap again. 187 buf = allocator.allocateOneBuffer(); 188 assertFalse(buf.hasArray()); 189 assertEquals(buf.remaining(), bufSize); 190 buf.release(); 191 } 192 193 @Test 194 public void testReferenceCount() { 195 int bufSize = 64; 196 ByteBuffAllocator alloc = new ByteBuffAllocator(true, 2, bufSize, 3); 197 ByteBuff buf1 = alloc.allocate(bufSize * 2); 198 assertFalse(buf1.hasArray()); 199 // The next one will be allocated from heap 200 ByteBuff buf2 = alloc.allocateOneBuffer(); 201 assertTrue(buf2.hasArray()); 202 203 // duplicate the buf2, if the dup released, buf2 will also be released (SingleByteBuffer) 204 ByteBuff dup2 = buf2.duplicate(); 205 dup2.release(); 206 assertEquals(0, buf2.refCnt()); 207 assertEquals(0, dup2.refCnt()); 208 assertEquals(0, alloc.getFreeBufferCount()); 209 assertException(dup2::position); 210 assertException(buf2::position); 211 212 // duplicate the buf1, if the dup1 released, buf1 will also be released (MultipleByteBuffer) 213 ByteBuff dup1 = buf1.duplicate(); 214 dup1.release(); 215 assertEquals(0, buf1.refCnt()); 216 assertEquals(0, dup1.refCnt()); 217 assertEquals(2, alloc.getFreeBufferCount()); 218 assertException(dup1::position); 219 assertException(buf1::position); 220 221 // slice the buf3, if the slice3 released, buf3 will also be released (SingleByteBuffer) 222 ByteBuff buf3 = alloc.allocateOneBuffer(); 223 assertFalse(buf3.hasArray()); 224 ByteBuff slice3 = buf3.slice(); 225 slice3.release(); 226 assertEquals(0, buf3.refCnt()); 227 assertEquals(0, slice3.refCnt()); 228 assertEquals(2, alloc.getFreeBufferCount()); 229 230 // slice the buf4, if the slice4 released, buf4 will also be released (MultipleByteBuffer) 231 ByteBuff buf4 = alloc.allocate(bufSize * 2); 232 assertFalse(buf4.hasArray()); 233 ByteBuff slice4 = buf4.slice(); 234 slice4.release(); 235 assertEquals(0, buf4.refCnt()); 236 assertEquals(0, slice4.refCnt()); 237 assertEquals(2, alloc.getFreeBufferCount()); 238 239 // Test multiple reference for the same ByteBuff (SingleByteBuff) 240 ByteBuff buf5 = alloc.allocateOneBuffer(); 241 ByteBuff slice5 = buf5.duplicate().duplicate().duplicate().slice().slice(); 242 slice5.release(); 243 assertEquals(0, buf5.refCnt()); 244 assertEquals(0, slice5.refCnt()); 245 assertEquals(2, alloc.getFreeBufferCount()); 246 assertException(slice5::position); 247 assertException(buf5::position); 248 249 // Test multiple reference for the same ByteBuff (SingleByteBuff) 250 ByteBuff buf6 = alloc.allocate(bufSize >> 2); 251 ByteBuff slice6 = buf6.duplicate().duplicate().duplicate().slice().slice(); 252 slice6.release(); 253 assertEquals(0, buf6.refCnt()); 254 assertEquals(0, slice6.refCnt()); 255 assertEquals(2, alloc.getFreeBufferCount()); 256 257 // Test retain the parent SingleByteBuff (duplicate) 258 ByteBuff parent = alloc.allocateOneBuffer(); 259 ByteBuff child = parent.duplicate(); 260 child.retain(); 261 parent.release(); 262 assertEquals(1, child.refCnt()); 263 assertEquals(1, parent.refCnt()); 264 assertEquals(1, alloc.getFreeBufferCount()); 265 parent.release(); 266 assertEquals(0, child.refCnt()); 267 assertEquals(0, parent.refCnt()); 268 assertEquals(2, alloc.getFreeBufferCount()); 269 270 // Test retain parent MultiByteBuff (duplicate) 271 parent = alloc.allocate(bufSize << 1); 272 child = parent.duplicate(); 273 child.retain(); 274 parent.release(); 275 assertEquals(1, child.refCnt()); 276 assertEquals(1, parent.refCnt()); 277 assertEquals(0, alloc.getFreeBufferCount()); 278 parent.release(); 279 assertEquals(0, child.refCnt()); 280 assertEquals(0, parent.refCnt()); 281 assertEquals(2, alloc.getFreeBufferCount()); 282 283 // Test retain the parent SingleByteBuff (slice) 284 parent = alloc.allocateOneBuffer(); 285 child = parent.slice(); 286 child.retain(); 287 parent.release(); 288 assertEquals(1, child.refCnt()); 289 assertEquals(1, parent.refCnt()); 290 assertEquals(1, alloc.getFreeBufferCount()); 291 parent.release(); 292 assertEquals(0, child.refCnt()); 293 assertEquals(0, parent.refCnt()); 294 assertEquals(2, alloc.getFreeBufferCount()); 295 296 // Test retain parent MultiByteBuff (slice) 297 parent = alloc.allocate(bufSize << 1); 298 child = parent.slice(); 299 child.retain(); 300 parent.release(); 301 assertEquals(1, child.refCnt()); 302 assertEquals(1, parent.refCnt()); 303 assertEquals(0, alloc.getFreeBufferCount()); 304 parent.release(); 305 assertEquals(0, child.refCnt()); 306 assertEquals(0, parent.refCnt()); 307 assertEquals(2, alloc.getFreeBufferCount()); 308 } 309 310 @Test 311 public void testReverseRef() { 312 int bufSize = 64; 313 ByteBuffAllocator alloc = new ByteBuffAllocator(true, 1, bufSize, 3); 314 ByteBuff buf1 = alloc.allocate(bufSize); 315 ByteBuff dup1 = buf1.duplicate(); 316 assertEquals(1, buf1.refCnt()); 317 assertEquals(1, dup1.refCnt()); 318 buf1.release(); 319 assertEquals(0, buf1.refCnt()); 320 assertEquals(0, dup1.refCnt()); 321 assertEquals(1, alloc.getFreeBufferCount()); 322 assertException(buf1::position); 323 assertException(dup1::position); 324 } 325 326 @Test 327 public void testByteBuffUnsupportedMethods() { 328 int bufSize = 64; 329 ByteBuffAllocator alloc = new ByteBuffAllocator(true, 1, bufSize, 3); 330 ByteBuff buf = alloc.allocate(bufSize); 331 assertException(() -> buf.retain(2)); 332 assertException(() -> buf.release(2)); 333 assertException(() -> buf.touch()); 334 assertException(() -> buf.touch(new Object())); 335 } 336 337 private void assertException(Runnable r) { 338 try { 339 r.run(); 340 fail(); 341 } catch (Exception e) { 342 // expected exception. 343 } 344 } 345 346 @Test 347 public void testDeprecatedConfigs() { 348 Configuration conf = new Configuration(); 349 conf.setInt(ByteBuffAllocator.DEPRECATED_MAX_BUFFER_COUNT_KEY, 10); 350 conf.setInt(ByteBuffAllocator.DEPRECATED_BUFFER_SIZE_KEY, 1024); 351 ByteBuffAllocator allocator = ByteBuffAllocator.create(conf, true); 352 Assert.assertEquals(1024, allocator.getBufferSize()); 353 Assert.assertEquals(10, allocator.getTotalBufferCount()); 354 355 conf = new Configuration(); 356 conf.setInt(ByteBuffAllocator.MAX_BUFFER_COUNT_KEY, 11); 357 conf.setInt(ByteBuffAllocator.BUFFER_SIZE_KEY, 2048); 358 allocator = ByteBuffAllocator.create(conf, true); 359 Assert.assertEquals(2048, allocator.getBufferSize()); 360 Assert.assertEquals(11, allocator.getTotalBufferCount()); 361 362 conf = new Configuration(); 363 conf.setBoolean(ByteBuffAllocator.DEPRECATED_ALLOCATOR_POOL_ENABLED_KEY, false); 364 Assert.assertFalse(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, true)); 365 conf.setBoolean(ByteBuffAllocator.DEPRECATED_ALLOCATOR_POOL_ENABLED_KEY, true); 366 Assert.assertTrue(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, false)); 367 conf.setBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, true); 368 Assert.assertTrue(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, false)); 369 conf.setBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, false); 370 Assert.assertFalse(conf.getBoolean(ByteBuffAllocator.ALLOCATOR_POOL_ENABLED_KEY, true)); 371 } 372 373 @Test 374 public void testHeapAllocationRatio() { 375 Configuration conf = new Configuration(); 376 conf.setInt(ByteBuffAllocator.MAX_BUFFER_COUNT_KEY, 11); 377 conf.setInt(ByteBuffAllocator.BUFFER_SIZE_KEY, 2048); 378 ByteBuffAllocator alloc1 = ByteBuffAllocator.create(conf, true); 379 Assert.assertEquals(getHeapAllocationRatio(alloc1), 0.0f, 1e-6); 380 alloc1.allocate(1); 381 Assert.assertEquals(getHeapAllocationRatio(alloc1), 1.0f, 1e-6); 382 383 alloc1.allocate(2048 / 6 - 1); 384 Assert.assertEquals(getHeapAllocationRatio(alloc1), 1.0f, 1e-6); 385 386 alloc1.allocate(24); 387 alloc1.allocate(1024); 388 Assert.assertEquals(getHeapAllocationRatio(alloc1), 24 / (24f + 2048), 1e-6); 389 Assert.assertEquals(getHeapAllocationRatio(alloc1), 0.0f, 1e-6); 390 391 // Allocate something from HEAP 392 HEAP.allocate(1024); 393 alloc1.allocate(24); 394 alloc1.allocate(1024); 395 Assert.assertEquals(getHeapAllocationRatio(HEAP, alloc1), (1024f + 24) / (1024f + 24 + 2048), 396 1e-6); 397 Assert.assertEquals(getHeapAllocationRatio(HEAP, alloc1), 0.0f, 1e-6); 398 399 // Check duplicated heap allocator, say even if we passed (HEAP, HEAP, alloc1), it will only 400 // caculate the allocation from (HEAP, alloc1). 401 HEAP.allocate(1024); 402 alloc1.allocate(1024); 403 Assert.assertEquals(getHeapAllocationRatio(HEAP, HEAP, alloc1), 1024f / (1024f + 2048f), 1e-6); 404 } 405}