Netty堆緩存問題

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(): falseByteBufAllocator.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是對的,但是之後就廢棄了或修改了是常有的事。所以我們需要跟蹤源代碼。這只是一個小小的問題,如果沒有前輩的無私奉獻,很難想象我們自己一天能學到多少內容。感謝各位前輩的辛勤付出,讓我們少走了很多的彎路!

點個贊再走唄!歡迎留言哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章