1、問題描述
今天學習Netty
堆緩存和直接緩存遇到一個問題,明明使用的是堆緩存,這麼讀取不到數據呢?打印日誌一看heapBuf.hasArray()
直接返回false
。來下面我們來看看源碼,到底是怎麼回事。
2、問題分析
首先寫一個測試方法,直接向ByteBuf
寫入中國萬歲!
,然後如果是堆內存直接打印即可。源碼如下:
@Test
public void testHeapBuffer2() {
//取得堆內存 (但是默認是 directByDefault=true)
ByteBuf heapBuf = ByteBufAllocator.DEFAULT.buffer();
heapBuf.writeBytes("中國萬歲!".getBytes(UTF_8));
if (heapBuf.hasArray()) {
//取得內部數組
byte[] array = heapBuf.array();
int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
int length = heapBuf.readableBytes();
Logger.info("---------chen------------>" + new String(array, offset, length, UTF_8));
}
System.out.println("heapBuf.hasArray(): " + heapBuf.hasArray());
heapBuf.release();
}
控制檯輸出如下:
09:55:28.154 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
09:55:28.213 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available
09:55:28.216 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available
09:55:28.217 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available
09:55:28.217 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Bits.unaligned: true
09:55:28.218 [main] DEBUG io.netty.util.internal.PlatformDependent - Platform: Windows
09:55:28.218 [main] DEBUG io.netty.util.internal.PlatformDependent - Java version: 8
09:55:28.218 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noUnsafe: false
09:55:28.218 [main] DEBUG io.netty.util.internal.PlatformDependent - sun.misc.Unsafe: available
09:55:28.219 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noJavassist: false
09:55:28.221 [main] DEBUG io.netty.util.internal.PlatformDependent - Javassist: unavailable
09:55:28.221 [main] DEBUG io.netty.util.internal.PlatformDependent - You don't have Javassist in your class path or you don't have enough permission to load dynamically generated classes. Please check the configuration for better performance.
09:55:28.222 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.tmpdir: C:\Users\ADMINI~1\AppData\Local\Temp (java.io.tmpdir)
09:55:28.222 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.bitMode: 64 (sun.arch.data.model)
09:55:28.222 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noPreferDirect: false
09:55:28.225 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: unpooled
09:55:28.226 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
09:55:28.228 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
09:55:28.241 [main] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true
09:55:28.245 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.level: simple
09:55:28.245 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.maxRecords: 4
heapBuf.hasArray(): false
09:55:28.253 [main] DEBUG io.netty.util.internal.Cleaner0 - java.nio.ByteBuffer.cleaner(): available
是的,我們看到了heapBuf.hasArray(): false
。ByteBufAllocator.DEFAULT.buffer();
不是使用的是堆緩存嗎?怎麼會是false
呢?紙上得來終覺淺,絕知此事要躬行。直接看源碼。他的實現方法如下:
@Override
public ByteBuf buffer() {
if (directByDefault) {
return directBuffer();
}
return heapBuffer();
}
嗯,到底是堆內存還是直接內存還和directByDefault
變量有關。我們在查找這個變量是在哪裏賦值的。沒錯就是他的構造函數。
/**
* Create new instance
*
* @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
* a heap buffer
*/
protected AbstractByteBufAllocator(boolean preferDirect) {
directByDefault = preferDirect && PlatformDependent.hasUnsafe();
emptyBuf = new EmptyByteBuf(this);
}
是的,我們還沒有找到答案,因爲他還是和preferDirect
變量相關。繼續往下找。我們發現UnpooledByteBufAllocator
的構造函數設置了這個值。
/**
* Create a new instance
*
* @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
* a heap buffer
*/
public UnpooledByteBufAllocator(boolean preferDirect) {
super(preferDirect);
}
繼續尋找preferDirect
的值。
/**
* Default instance
*/
public static final UnpooledByteBufAllocator DEFAULT =
new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());
真實值是PlatformDependent.directBufferPreferred()
,再次追蹤進去。
/**
* Returns {@code true} if the platform has reliable low-level direct buffer access API and a user has not specified
* {@code -Dio.netty.noPreferDirect} option.
*/
public static boolean directBufferPreferred() {
return DIRECT_BUFFER_PREFERRED;
}
這個DIRECT_BUFFER_PREFERRED
是何方神聖呢?繼續追蹤。
private static final boolean DIRECT_BUFFER_PREFERRED =
HAS_UNSAFE && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false);
終於找到了作妖的源頭了。是的就是他。根據定義我們知道DIRECT_BUFFER_PREFERRED
默認是True
,也就是默認是直接內存。OMG
3、問題解決
既然知道你是直接內存了,那我們直接改屬性不就完了。通過System.setProperty("io.netty.noPreferDirect", "true");
直接設置使用堆緩存。修改代碼如下:
//堆緩衝區
@Test
public void testHeapBuffer() {
System.setProperty("io.netty.noPreferDirect", "true");
//取得堆內存 (但是默認是 directByDefault=true)
ByteBuf heapBuf = ByteBufAllocator.DEFAULT.buffer();
heapBuf.writeBytes("中國萬歲!".getBytes(UTF_8));
if (heapBuf.hasArray()) {
//取得內部數組
byte[] array = heapBuf.array();
int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
int length = heapBuf.readableBytes();
Logger.info("---------chen------------>" + new String(array, offset, length, UTF_8));
}
heapBuf.release();
}
直接運行,查看控制檯
10:11:36.587 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
10:11:36.636 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available
10:11:36.638 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available
10:11:36.639 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available
10:11:36.639 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Bits.unaligned: true
10:11:36.640 [main] DEBUG io.netty.util.internal.PlatformDependent - Platform: Windows
10:11:36.640 [main] DEBUG io.netty.util.internal.PlatformDependent - Java version: 8
10:11:36.640 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noUnsafe: false
10:11:36.640 [main] DEBUG io.netty.util.internal.PlatformDependent - sun.misc.Unsafe: available
10:11:36.641 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noJavassist: false
10:11:36.642 [main] DEBUG io.netty.util.internal.PlatformDependent - Javassist: unavailable
10:11:36.642 [main] DEBUG io.netty.util.internal.PlatformDependent - You don't have Javassist in your class path or you don't have enough permission to load dynamically generated classes. Please check the configuration for better performance.
10:11:36.643 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.tmpdir: C:\Users\ADMINI~1\AppData\Local\Temp (java.io.tmpdir)
10:11:36.643 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.bitMode: 64 (sun.arch.data.model)
10:11:36.643 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noPreferDirect: true
10:11:36.646 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: unpooled
10:11:36.647 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
10:11:36.649 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
10:11:36.659 [main] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true
10:11:36.664 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.level: simple
10:11:36.664 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.maxRecords: 4
[main|BufferTypeTest.testHeapBuffer] |> ---------chen------------>中國萬歲!
[main|BufferTypeTest.testHeapBuffer] |> ---------chenwei------------>中國萬歲!
完美輸出結果。問題解決。
4、總結
書上的代碼直接運行絕大部分是對的,但是總有一些軟件的更新使得作者無能爲力。之前的API是對的,但是之後就廢棄了或修改了是常有的事。所以我們需要跟蹤源代碼。這只是一個小小的問題,如果沒有前輩的無私奉獻,很難想象我們自己一天能學到多少內容。感謝各位前輩的辛勤付出,讓我們少走了很多的彎路!
點個贊再走唄!歡迎留言哦!