我們來看底層實現:對於所有DirectByteBuffer的讀寫,都用到了Unsafe類的public native void putByte(Object o, long offset, byte x);
方法,底層實現是:
UNSAFE_ENTRY(void, Unsafe_SetNative##Type(JNIEnv *env, jobject unsafe, jlong addr, java_type x)) \
UnsafeWrapper("Unsafe_SetNative"#Type); \
JavaThread* t = JavaThread::current(); \
t->set_doing_unsafe_access(true); \
//獲取地址
void* p = addr_from_java(addr); \
//設置值
*(volatile native_type*)p = x; \
t->set_doing_unsafe_access(false); \
UNSAFE_END \
那麼這個獲取地址的方法是啥樣子呢?
inline void* addr_from_java(jlong addr) {
// This assert fails in a variety of ways on 32-bit systems.
// It is impossible to predict whether native code that converts
// pointers to longs will sign-extend or zero-extend the addresses.
//assert(addr == (uintptr_t)addr, "must not be odd high bits");
//轉換爲int
return (void*)(uintptr_t)addr;
}
這裏我們看到,轉換地址會被強制轉換爲int類型,所以只能映射 2GB - 1B 。
但是爲何-XX:MaxDirectMemory
可以指定比2G
大的值呢?因爲對於分配的直接內存中的 buffer,有對一個 BitMap 管理他們的基址,可以保證映射出對的地址,類似於堆內存的基址映射。但是對於文件映射內存,JVM 沒有維護這麼一個基址,或者說覺得沒必要(一般不會有直接操作這麼大文件的這麼大內容的需求,大於2GB-1B
我們多映射兩次自己維護就行了)。
微信搜索“我的編程喵”關注公衆號,每日一刷,輕鬆提升技術,斬獲各種offer: