Netty源碼分析-PlatformDependent內存管理

 

PlatformDependent類當中的常量,定義了允許使用的堆外內存最大值,該值可以通過-XX:MaxDirectMemorySize=2G設置。

private static final long MAX_DIRECT_MEMORY = maxDirectMemory0();

 

在netty中,如果使用了堆外內存,Netty會進行統計,如果超過最大限制會拋出異常。

   private static void incrementMemoryCounter(int capacity) {
        if (DIRECT_MEMORY_COUNTER != null) {
            for (;;) {
                long usedMemory = DIRECT_MEMORY_COUNTER.get();
                long newUsedMemory = usedMemory + capacity;
                if (newUsedMemory > DIRECT_MEMORY_LIMIT) {
                    throw new OutOfDirectMemoryError("failed to allocate " + capacity
                            + " byte(s) of direct memory (used: " + usedMemory + ", max: " + DIRECT_MEMORY_LIMIT + ')');
                }
                if (DIRECT_MEMORY_COUNTER.compareAndSet(usedMemory, newUsedMemory)) {
                    break;
                }
            }
        }
    }

    private static void decrementMemoryCounter(int capacity) {
        if (DIRECT_MEMORY_COUNTER != null) {
            long usedMemory = DIRECT_MEMORY_COUNTER.addAndGet(-capacity);
            assert usedMemory >= 0;
        }
    }

我們在Eclipse-Run Configure中設置JVM參數  -XX:MaxDirectMemorySize=1K,稍後我們分配10K的空間,系統會報內存溢出錯誤。

	public static void main(String[] args) throws Exception {
		ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
		ByteBuf buf = allocator.directBuffer(10240);
		buf.release();
	}

 

PlatformDependent的DIRECT_MEMORY_COUNTER常量實時記錄了Netty堆外內存的使用情況。

這個字段是私有的,我們可以使用反射拿到該指定進行打印或監控,寫一個程序,沒秒打印值。

public class DirectMemoryReporter {

	private AtomicLong directMomery;
	
	public DirectMemoryReporter() {
		Field field = ReflectionUtils.findField(PlatformDependent.class, "DIRECT_MEMORY_COUNTER");
		field.setAccessible(true);
		
		try {
			directMomery = (AtomicLong) field.get(PlatformDependent.class);
		} catch (Exception e) {
		}
		
		GlobalEventExecutor.INSTANCE.scheduleAtFixedRate(this::doReport, 0, 1, TimeUnit.SECONDS);
	}
	
	public void doReport() {
		System.out.println("netty_directMomery_log \uff1a " + directMomery.get());
	}
}

首先我們使用非池化的UnpooledByteBufAllocator,創建2個ByteBuf對象,它們優先都會使用堆外內存,我們可以看到推外內存使用了200個字節。

public static void main(String[] args) throws Exception {
		DirectMemoryReporter directMemoryReporter = new DirectMemoryReporter();
		
		UnpooledByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
		ByteBuf buf1 = allocator.buffer(100);
		ByteBuf buf2 = allocator.directBuffer(100);
		System.out.println(buf1.getClass());
	}
}

netty_directMomery_log \uff1a 0
class io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf
netty_directMomery_log \uff1a 200
netty_directMomery_log \uff1a 200
netty_directMomery_log \uff1a 200

 

然後我們使用PooledByteBufAllocator測試,可以看到它首選分配了16M內存,然後若干個堆外ByteBuf對象,共享這16M內存,效率得到提升,也無需受到java-GC的干擾。

public class ByteBufDemo {

	public static void main(String[] args) throws Exception {
		DirectMemoryReporter directMemoryReporter = new DirectMemoryReporter();
		
		PooledByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
		ByteBuf buf1 = allocator.buffer(100);
		ByteBuf buf2 = allocator.directBuffer(100);
		System.out.println(buf1.getClass());
	}

}

netty_directMomery_log \uff1a 0
class io.netty.buffer.PooledUnsafeDirectByteBuf
netty_directMomery_log \uff1a 16777216
netty_directMomery_log \uff1a 16777216
netty_directMomery_log \uff1a 16777216

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