netty使用了相关的算法计算出比较合适缓冲区大小,整个流程图如下
ReceiveBufferSizePredictor可以根据实际读取的字节大小数设置下次读写叫合适的缓冲区大小。类结构如下
AdaptiveReceiveBufferSizePredictor 提供了一种自适应的计算方式,如下代码所述,当改类初始化的时候,会填充SIZE_TABLE数组。
private static final int[] SIZE_TABLE;
static {
List<Integer> sizeTable = new ArrayList<Integer>();
for (int i = 1; i <= 8; i ++) {
sizeTable.add(i);
}
for (int i = 4; i < 32; i ++) {
long v = 1L << i;
long inc = v >>> 4;
v -= inc << 3;
for (int j = 0; j < 8; j ++) {
v += inc;
if (v > Integer.MAX_VALUE) {
sizeTable.add(Integer.MAX_VALUE);
} else {
sizeTable.add((int) v);
}
}
}
SIZE_TABLE = new int[sizeTable.size()];
// for (int i = 0; i < SIZE_TABLE.length; i ++) {
// SIZE_TABLE[i] = sizeTable.get(i);
// System.out.println(SIZE_TABLE[i]);
// }
}
SIZE_TABLE数据长度为232,从以下数据可以看出
1-8的数据增量为1
8-16增量为1
16-32增量为2
32-64增量为4
64-128增量为8
128-256增量为16
依次类推如算法所述,1-8直接加入到数据,8以后的分段可以如下表示2^m到2^(m+1)之间的增量是2^(m-3)次方。如上算法的结果如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
18
20
22
24
26
28
30
32
36
40
44
48
52
56
60
64
72
80
88
96
104
112
120
128
144
160
176
192
208
224
240
256
288
320
352
384
416
448
480
512
576
640
704
768
832
896
960
1024
1152
1280
1408
1536
1664
1792
1920
2048
2304
2560
2816
3072
3328
3584
3840
4096
4608
5120
5632
6144
6656
7168
7680
8192
9216
10240
11264
12288
13312
14336
15360
16384
18432
20480
22528
24576
26624
28672
30720
32768
36864
40960
45056
49152
53248
57344
61440
65536
73728
81920
90112
98304
106496
114688
122880
131072
147456
163840
180224
196608
212992
229376
245760
262144
294912
327680
360448
393216
425984
458752
491520
524288
589824
655360
720896
786432
851968
917504
983040
1048576
1179648
1310720
1441792
1572864
1703936
1835008
1966080
2097152
2359296
2621440
2883584
3145728
3407872
3670016
3932160
4194304
4718592
5242880
5767168
6291456
6815744
7340032
7864320
8388608
9437184
10485760
11534336
12582912
13631488
14680064
15728640
16777216
18874368
20971520
23068672
25165824
27262976
29360128
31457280
33554432
37748736
41943040
46137344
50331648
54525952
58720256
62914560
67108864
75497472
83886080
92274688
100663296
109051904
117440512
125829120
134217728
150994944
167772160
184549376
201326592
218103808
234881024
251658240
268435456
301989888
335544320
369098752
402653184
436207616
469762048
503316480
536870912
603979776
671088640
738197504
805306368
872415232
939524096
1006632960
1073741824
1207959552
1342177280
1476395008
1610612736
1744830464
1879048192
2013265920
2147483647
初始缓存大小为1024,可以在SIZE_TABLE中找到对应数组位置。当实际读取字节小于1024数组左偏,否则右偏;下次预计的时候根据本次已设置的索引情况来判断;直到超过最大或最小值时,以最值为主。
SocketReceiveBufferAllocator
SocketReceiveBufferAllocator.get(size)处理逻辑如下所述,没什么特别的。值得借鉴下段位移代码部分
Buffer = null 分配DirectBuffer
Buffer.capacity < size 分配DirectBuffer
Buffer.capacity * percent > size && exceedCount=maxExceedCount 分配DirectBuffer
Buffer.capacity * percent > size && exceedCount <maxExceedCount buffer重复使用
Buffer.capacity * percent <= size exceedCount =0;buffer重复使用
分配DirectBuffer 释放已申请的Buffer占用的系统内存;计算size进1024
计算size进1024
// Normalize to multiple of 1024
int q = capacity >>> 10;
int r = capacity & 1023;
if (r != 0) {
q ++;
}
return q << 10;
java.nio.DirectByteBuffer
直接向操作系统请求分配一些资源空间,改空间并不是JAVA推内存。之后的所有写入读取操作都不通过堆内存处理,直接操作系统资源空间。通过unsafe.allocateMemory,setMemory操作资源空间。而最后一行代码,Cleaner是一个PhantomReference实现类
DirectByteBuffer(int cap) { // package-private
super(-1, 0, cap, cap, false);
Bits.reserveMemory(cap);
int ps = Bits.pageSize();
long base = 0;
try {
base = unsafe.allocateMemory(cap + ps);
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(cap);
throw x;
}
unsafe.setMemory(base, cap + ps, (byte) 0);
if (base % ps != 0) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
//关键代码
cleaner = Cleaner.create(this, new Deallocator(base, cap));
从如下代码可以看出Cleaner接受一个runnable对象,clean方法中调用runnable对象的run方法。
private Cleaner(Object obj, Runnable runnable)
{
super(obj, dummyQueue);
next = null;
prev = null;
thunk = runnable;
}
public static Cleaner create(Object obj, Runnable runnable)
{
if(runnable == null)
return null;
else
return add(new Cleaner(obj, runnable));
}
public void clean()
{
if(!remove(this))
return;
try
{
thunk.run();
在这里,Deallocator即为runnable对象,可以看到由unsafe.freeMemory
public void run() {
if (address == 0) {
// Paranoia
return;
}
unsafe.freeMemory(address);
address = 0;
Bits.unreserveMemory(capacity);
}
那clean方法调用时机在哪呢,正如JDK文档中指出当虚拟机确定对象虚可到达时,那么在那时将会被加入pending队列。在Reference中体现,
/* List of References waiting to be enqueued. The collector adds
* References to this list, while the Reference-handler thread removes
* them. This list is protected by the above lock object.
*/
private static Reference pending = null;
Reference的静态语句块中初始化一个后台线程,不断的遍历pending,处理pending中的每个节点
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
}
处理pending代码,注意Cleaner部分
public void run() {
for (;;) {
Reference r;
synchronized (lock) {
if (pending != null) {
r = pending;
Reference rn = r.next;
pending = (rn == r) ? null : rn;
r.next = r;
} else {
try {
lock.wait();
} catch (InterruptedException x) { }
continue;
}
}
// 原来clean是在这处理的
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
}
ReferenceQueue q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
}
}
}
这种由JVM确定的调用时机,其实对开发来讲是被动的,不“靠谱的”,鬼知道它何时释放。所以我们只要争取主动释放。即调用Cleaner的clean即可。
netty中的ByteBufferUtil就干这事。
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.util.internal;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
/**
* This is fork of ElasticSearch's ByteBufferAllocator.Cleaner class
*/
public final class ByteBufferUtil {
private static final boolean CLEAN_SUPPORTED;
private static final Method directBufferCleaner;
private static final Method directBufferCleanerClean;
static {
Method directBufferCleanerX = null;
Method directBufferCleanerCleanX = null;
boolean v;
try {
directBufferCleanerX = Class.forName("java.nio.DirectByteBuffer").getMethod("cleaner");
directBufferCleanerX.setAccessible(true);
directBufferCleanerCleanX = Class.forName("sun.misc.Cleaner").getMethod("clean");
directBufferCleanerCleanX.setAccessible(true);
v = true;
} catch (Exception e) {
v = false;
}
CLEAN_SUPPORTED = v;
directBufferCleaner = directBufferCleanerX;
directBufferCleanerClean = directBufferCleanerCleanX;
}
/**
* Destroy the given {@link ByteBuffer} if possible
*/
public static void destroy(ByteBuffer buffer) {
if (CLEAN_SUPPORTED && buffer.isDirect()) {
try {
Object cleaner = directBufferCleaner.invoke(buffer);
directBufferCleanerClean.invoke(cleaner);
} catch (Exception e) {
// silently ignore exception
}
}
}
private ByteBufferUtil() {
// Utility class
}
}
- 大小: 23.7 KB
- 大小: 26.4 KB
分享到:
相关推荐
微信消息一键已读 微信消息批量已读 微信消息全部已读 1、微信消息怎么批量已读 2、微信消息可以一键已读吗 3、微信如何设置全部已读 目前支持 windows 微信 下载后双击运行即可
【标题】:“未读消息效果”是一种常见的移动应用设计元素,尤其在社交和通信应用中极为常见。这种设计旨在向用户直观地展示他们有多少未查看的新消息,从而提高应用的互动性和用户体验。在本示例中,我们将关注如何...
"桌面未读消息数目提醒"是一个集中的消息通知系统,它可以在桌面上显示所有未读消息的总数,帮助用户快速了解到有多少未处理的消息等待他们查看。这个实用工具通常与操作系统或第三方应用程序集成,它可以监控电子...
【标题】"仿QQ未读消息"是一种在移动应用中广泛采用的设计模式,它模仿了腾讯QQ中的未读消息提示功能,使得用户可以清楚地知道他们错过了哪些新信息。这一功能不仅限于聊天应用程序,现在已被各类应用所采纳,如社交...
在Android应用开发中,模拟QQ和微信的未读消息显示是一项常见的需求,这涉及到通知、UI设计以及用户交互等多个方面。QQ和微信的未读消息显示不仅提供了视觉上的提示,还增强了用户体验,使得用户能够快速识别并处理...
在Android应用开发中,右上角的未读消息红点或者数字是常见的一种设计,用于提示用户有新的未查看信息。这种设计常见于各种社交、邮件、消息类应用,如微信、QQ等。通过实现这一功能,可以提高用户体验,使用户能够...
1. **语音合成(Text-to-Speech, TTS)**:这是自动读QQ消息软件的基础,TTS技术能够将文本数据转换为自然流畅的语音。它涉及到自然语言处理、语音信号处理和音频编码等多个领域的知识。TTS系统通常包括文本预处理、...
群消息已读回执在即时通讯(IM)应用中是一个重要的功能,它涉及到消息的投递、接收、确认和优化等多个方面。本文主要探讨了群消息的处理流程、已读回执的实现以及如何进行系统优化。 首先,理解群消息的处理流程至...
在Android应用开发中,"Android仿微信未读消息数提示显示数字BadgeView大于99条显示99+"是一个常见的需求,特别是在设计具有社交功能的应用时。BadgeView是一种UI组件,通常用于显示未读消息数量或者通知标记,它...
标题中的“桌面未读消息数字提醒”指的是在Android操作系统中,一种能够显示在手机桌面小角上的未读消息计数器。这种功能通常用于帮助用户快速了解他们有多少未处理的通知,比如未读邮件、未接电话、未读短信或者...
本文将深入探讨如何利用自定义View来模仿微信、QQ等社交应用中常见的未读消息数量提示效果,即在图标或头像的右上角显示一个带有数字的红色圆圈。这种提示方式能有效地提醒用户有未查看的信息,提升用户体验。 首先...
在iOS开发中,实现“未读消息”到“已读消息”的功能是常见的需求,尤其在消息通知或者社交应用中。这个"iOS阅读未读消息,未读提示消失"的Demo展示了一个简单的方法来处理这一过程。让我们深入探讨一下相关的知识点...
【压缩包子文件的文件名称】"信利语音拨号来电短消息读号"可能是该自动读短信工具的配套软件或者扩展功能。"信利"可能是一个品牌或开发者的名字,而"语音拨号"意味着除了读取短信外,该工具还可能包含了语音拨号的...
在安卓开发中,数字提醒气泡提示角标是一种常见的用户界面设计元素,它通常用于通知用户有未读消息、更新或其他重要信息。这种设计在许多应用程序中都可以看到,比如QQ桌面,它会在快捷方式或者应用图标上显示一个带...
QQ未读消息气泡拖拽和滑动删除置顶是一种常见的移动应用交互设计,常见于即时通讯软件中,如QQ。这种设计提升了用户体验,让用户能够更直观、便捷地管理未读消息。以下将详细讲解这一功能的技术实现和相关知识点。 ...
这个项目是关于如何在自己的Android应用中实现一个类似微信的底部菜单栏,并且能够显示未读消息的数量。下面我们将深入探讨这个知识点。 首先,底部菜单栏通常由多个Tab构成,每个Tab代表一个不同的功能模块,比如...
当有未读消息或者新内容时,为这些组件添加角标(通常是小红点)是一种常见的提示方式。本篇将详细讲解如何在`ToolBar`和`TabLayout`上实现角标显示未读消息。 首先,我们需要了解`ToolBar`的基本使用。`ToolBar`是...
【标题】"仿QQ拖动删除未读消息"这一技术主要涉及到移动应用开发中的交互设计与实现,尤其在iOS和Android平台上的应用。QQ作为一款流行的即时通讯软件,其独特的用户体验设计,如拖动删除未读消息的功能,给用户带来...
一款由HTML5技术实现的带未读消息提示标记的CSS3菜单,竖向排列的导航菜单,菜单项中可显示类似手机微信、QQ列表中的未读消息标记,虽是一个挺小的功能,但却很主流,现在手机上这种角标效果已经很普遍了。...
在Android开发中,仿QQ拖动删除未读消息个数气泡是一项常见的交互设计,它为用户提供了便捷的清理未读消息的方式。这个功能通过一个可拖动的气泡显示未读消息的数量,用户可以通过拖动气泡将其消除,从而清空对应的...