我们来看底层实现:对于所有DirectByteBuffer的读写,都用到了Unsafe类的public native void putByte(Object o, long offset, byte x);
方法,底层实现是:
unsafe.cpp:
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 \
那么这个获取地址的方法是啥样子呢?
unsafe.cpp:
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: