1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.buffer;
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 final class PoolChunk<T> implements PoolChunkMetric {
104
105 private static final int INTEGER_SIZE_MINUS_ONE = Integer.SIZE - 1;
106
107 final PoolArena<T> arena;
108 final T memory;
109 final boolean unpooled;
110 final int offset;
111
112 private final byte[] memoryMap;
113 private final byte[] depthMap;
114 private final PoolSubpage<T>[] subpages;
115
116 private final int subpageOverflowMask;
117 private final int pageSize;
118 private final int pageShifts;
119 private final int maxOrder;
120 private final int chunkSize;
121 private final int log2ChunkSize;
122 private final int maxSubpageAllocs;
123
124 private final byte unusable;
125
126 private int freeBytes;
127
128 PoolChunkList<T> parent;
129 PoolChunk<T> prev;
130 PoolChunk<T> next;
131
132
133
134
135 PoolChunk(PoolArena<T> arena, T memory, int pageSize, int maxOrder, int pageShifts, int chunkSize, int offset) {
136 unpooled = false;
137 this.arena = arena;
138 this.memory = memory;
139 this.pageSize = pageSize;
140 this.pageShifts = pageShifts;
141 this.maxOrder = maxOrder;
142 this.chunkSize = chunkSize;
143 this.offset = offset;
144 unusable = (byte) (maxOrder + 1);
145 log2ChunkSize = log2(chunkSize);
146 subpageOverflowMask = ~(pageSize - 1);
147 freeBytes = chunkSize;
148
149 assert maxOrder < 30 : "maxOrder should be < 30, but is: " + maxOrder;
150 maxSubpageAllocs = 1 << maxOrder;
151
152
153 memoryMap = new byte[maxSubpageAllocs << 1];
154 depthMap = new byte[memoryMap.length];
155 int memoryMapIndex = 1;
156 for (int d = 0; d <= maxOrder; ++ d) {
157 int depth = 1 << d;
158 for (int p = 0; p < depth; ++ p) {
159
160 memoryMap[memoryMapIndex] = (byte) d;
161 depthMap[memoryMapIndex] = (byte) d;
162 memoryMapIndex ++;
163 }
164 }
165
166 subpages = newSubpageArray(maxSubpageAllocs);
167 }
168
169
170 PoolChunk(PoolArena<T> arena, T memory, int size, int offset) {
171 unpooled = true;
172 this.arena = arena;
173 this.memory = memory;
174 this.offset = offset;
175 memoryMap = null;
176 depthMap = null;
177 subpages = null;
178 subpageOverflowMask = 0;
179 pageSize = 0;
180 pageShifts = 0;
181 maxOrder = 0;
182 unusable = (byte) (maxOrder + 1);
183 chunkSize = size;
184 log2ChunkSize = log2(chunkSize);
185 maxSubpageAllocs = 0;
186 }
187
188 @SuppressWarnings("unchecked")
189 private PoolSubpage<T>[] newSubpageArray(int size) {
190 return new PoolSubpage[size];
191 }
192
193 @Override
194 public int usage() {
195 final int freeBytes;
196 synchronized (arena) {
197 freeBytes = this.freeBytes;
198 }
199 return usage(freeBytes);
200 }
201
202 private int usage(int freeBytes) {
203 if (freeBytes == 0) {
204 return 100;
205 }
206
207 int freePercentage = (int) (freeBytes * 100L / chunkSize);
208 if (freePercentage == 0) {
209 return 99;
210 }
211 return 100 - freePercentage;
212 }
213
214 long allocate(int normCapacity) {
215 if ((normCapacity & subpageOverflowMask) != 0) {
216 return allocateRun(normCapacity);
217 } else {
218 return allocateSubpage(normCapacity);
219 }
220 }
221
222
223
224
225
226
227
228
229
230 private void updateParentsAlloc(int id) {
231 while (id > 1) {
232 int parentId = id >>> 1;
233 byte val1 = value(id);
234 byte val2 = value(id ^ 1);
235 byte val = val1 < val2 ? val1 : val2;
236 setValue(parentId, val);
237 id = parentId;
238 }
239 }
240
241
242
243
244
245
246
247
248 private void updateParentsFree(int id) {
249 int logChild = depth(id) + 1;
250 while (id > 1) {
251 int parentId = id >>> 1;
252 byte val1 = value(id);
253 byte val2 = value(id ^ 1);
254 logChild -= 1;
255
256 if (val1 == logChild && val2 == logChild) {
257 setValue(parentId, (byte) (logChild - 1));
258 } else {
259 byte val = val1 < val2 ? val1 : val2;
260 setValue(parentId, val);
261 }
262
263 id = parentId;
264 }
265 }
266
267
268
269
270
271
272
273
274 private int allocateNode(int d) {
275 int id = 1;
276 int initial = - (1 << d);
277 byte val = value(id);
278 if (val > d) {
279 return -1;
280 }
281 while (val < d || (id & initial) == 0) {
282 id <<= 1;
283 val = value(id);
284 if (val > d) {
285 id ^= 1;
286 val = value(id);
287 }
288 }
289 byte value = value(id);
290 assert value == d && (id & initial) == 1 << d : String.format("val = %d, id & initial = %d, d = %d",
291 value, id & initial, d);
292 setValue(id, unusable);
293 updateParentsAlloc(id);
294 return id;
295 }
296
297
298
299
300
301
302
303 private long allocateRun(int normCapacity) {
304 int d = maxOrder - (log2(normCapacity) - pageShifts);
305 int id = allocateNode(d);
306 if (id < 0) {
307 return id;
308 }
309 freeBytes -= runLength(id);
310 return id;
311 }
312
313
314
315
316
317
318
319
320 private long allocateSubpage(int normCapacity) {
321
322
323 PoolSubpage<T> head = arena.findSubpagePoolHead(normCapacity);
324 synchronized (head) {
325 int d = maxOrder;
326 int id = allocateNode(d);
327 if (id < 0) {
328 return id;
329 }
330
331 final PoolSubpage<T>[] subpages = this.subpages;
332 final int pageSize = this.pageSize;
333
334 freeBytes -= pageSize;
335
336 int subpageIdx = subpageIdx(id);
337 PoolSubpage<T> subpage = subpages[subpageIdx];
338 if (subpage == null) {
339 subpage = new PoolSubpage<T>(head, this, id, runOffset(id), pageSize, normCapacity);
340 subpages[subpageIdx] = subpage;
341 } else {
342 subpage.init(head, normCapacity);
343 }
344 return subpage.allocate();
345 }
346 }
347
348
349
350
351
352
353
354
355
356 void free(long handle) {
357 int memoryMapIdx = memoryMapIdx(handle);
358 int bitmapIdx = bitmapIdx(handle);
359
360 if (bitmapIdx != 0) {
361 PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)];
362 assert subpage != null && subpage.doNotDestroy;
363
364
365
366 PoolSubpage<T> head = arena.findSubpagePoolHead(subpage.elemSize);
367 synchronized (head) {
368 if (subpage.free(head, bitmapIdx & 0x3FFFFFFF)) {
369 return;
370 }
371 }
372 }
373 freeBytes += runLength(memoryMapIdx);
374 setValue(memoryMapIdx, depth(memoryMapIdx));
375 updateParentsFree(memoryMapIdx);
376 }
377
378 void initBuf(PooledByteBuf<T> buf, long handle, int reqCapacity) {
379 int memoryMapIdx = memoryMapIdx(handle);
380 int bitmapIdx = bitmapIdx(handle);
381 if (bitmapIdx == 0) {
382 byte val = value(memoryMapIdx);
383 assert val == unusable : String.valueOf(val);
384 buf.init(this, handle, runOffset(memoryMapIdx) + offset, reqCapacity, runLength(memoryMapIdx),
385 arena.parent.threadCache());
386 } else {
387 initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity);
388 }
389 }
390
391 void initBufWithSubpage(PooledByteBuf<T> buf, long handle, int reqCapacity) {
392 initBufWithSubpage(buf, handle, bitmapIdx(handle), reqCapacity);
393 }
394
395 private void initBufWithSubpage(PooledByteBuf<T> buf, long handle, int bitmapIdx, int reqCapacity) {
396 assert bitmapIdx != 0;
397
398 int memoryMapIdx = memoryMapIdx(handle);
399
400 PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)];
401 assert subpage.doNotDestroy;
402 assert reqCapacity <= subpage.elemSize;
403
404 buf.init(
405 this, handle,
406 runOffset(memoryMapIdx) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize + offset,
407 reqCapacity, subpage.elemSize, arena.parent.threadCache());
408 }
409
410 private byte value(int id) {
411 return memoryMap[id];
412 }
413
414 private void setValue(int id, byte val) {
415 memoryMap[id] = val;
416 }
417
418 private byte depth(int id) {
419 return depthMap[id];
420 }
421
422 private static int log2(int val) {
423
424 return INTEGER_SIZE_MINUS_ONE - Integer.numberOfLeadingZeros(val);
425 }
426
427 private int runLength(int id) {
428
429 return 1 << log2ChunkSize - depth(id);
430 }
431
432 private int runOffset(int id) {
433
434 int shift = id ^ 1 << depth(id);
435 return shift * runLength(id);
436 }
437
438 private int subpageIdx(int memoryMapIdx) {
439 return memoryMapIdx ^ maxSubpageAllocs;
440 }
441
442 private static int memoryMapIdx(long handle) {
443 return (int) handle;
444 }
445
446 private static int bitmapIdx(long handle) {
447 return (int) (handle >>> Integer.SIZE);
448 }
449
450 @Override
451 public int chunkSize() {
452 return chunkSize;
453 }
454
455 @Override
456 public int freeBytes() {
457 synchronized (arena) {
458 return freeBytes;
459 }
460 }
461
462 @Override
463 public String toString() {
464 final int freeBytes;
465 synchronized (arena) {
466 freeBytes = this.freeBytes;
467 }
468
469 return new StringBuilder()
470 .append("Chunk(")
471 .append(Integer.toHexString(System.identityHashCode(this)))
472 .append(": ")
473 .append(usage(freeBytes))
474 .append("%, ")
475 .append(chunkSize - freeBytes)
476 .append('/')
477 .append(chunkSize)
478 .append(')')
479 .toString();
480 }
481
482 void destroy() {
483 arena.destroyChunk(this);
484 }
485 }