博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java直接内存原理
阅读量:6161 次
发布时间:2019-06-21

本文共 2817 字,大约阅读时间需要 9 分钟。

直接内存

上述对直接内存的描述来自《深入理解Java虚拟机》,写明了直接内存不在java堆内,并且java堆内存往外写需要拷贝到native堆。

然后咱们先写个代码看看直接内存分配在哪个区域

import java.nio.ByteBuffer;import java.util.ArrayList;import java.util.List;/** * @author congzhou * @description: * @date: Created in 2019/2/19 21:57 */public class NativeHeapTest {    public static void main(String[] args) throws InterruptedException {        List
byteBufferList = new ArrayList<>(); while (true) { ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10240); byteBufferList.add(byteBuffer); Thread.sleep(100); } }}复制代码

watch命令观察变化内存

我使用的是64位centos7,虚拟地址<00007fffffffffff 是用户空间内存[1]。ByteBuffer.allocateDirect()分配的是用户空间的匿名内存。

再来看一下ByteBuffer.allocateDirect(int capacity)

ByteBuffer.allocateDirect调用了Unsafe.allocateMemory(long var1),该方法是个native方法

UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory0(JNIEnv *env, jobject unsafe, jlong size)) {  size_t sz = (size_t)size;  sz = align_up(sz, HeapWordSize);  void* x = os::malloc(sz, mtOther);  return addr_to_java(x);} UNSAFE_END复制代码

主要就是os::malloc(sz, mtInternal)方法,该方法封装了C++的标准库std::malloc()

void* os::malloc(size_t size, MEMFLAGS flags) {  return os::malloc(size, flags, CALLER_PC);}void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {/*省略代码*/  u_char* ptr;  ptr = (u_char*)::malloc(alloc_size);/*省略代码*/  // we do not track guard memory  return MemTracker::record_malloc((address)ptr, size, memflags, stack, level);}复制代码

(u_char*)::malloc(alloc_size)就是C++标准库函数,传入空间大小返回内存地址。

那么堆内存怎么往外写

openJDK sun.nio.ch.IOUtil.write(),java.nio.channels.SocketChannel#write(java.nio.ByteBuffer)等方法底层实现

static int write(FileDescriptor fd, ByteBuffer src, long position,                     NativeDispatcher nd)        throws IOException    {        if (src instanceof DirectBuffer)            return writeFromNativeBuffer(fd, src, position, nd);        // Substitute a native buffer        int pos = src.position();        int lim = src.limit();        assert (pos <= lim);        int rem = (pos <= lim ? lim - pos : 0);        ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);        try {            bb.put(src);            bb.flip();            // Do not update src until we see how many bytes were written            src.position(pos);            int n = writeFromNativeBuffer(fd, bb, position, nd);            if (n > 0) {                // now update src                src.position(pos + n);            }            return n;        } finally {            Util.offerFirstTemporaryDirectBuffer(bb);        }    }复制代码

上述代码直观的展示了java堆内存首先拷贝到了直接内存,然后再把地址传给I/O函数。

java GC三大类算法,除了标记清除,标记整理和复制算法都会移动对象,并且如果直接把java堆地址传给I/O函数则需要保证I/O操作过程中该块内存不变化,则需要暂停GC,所以JDK实现使用拷贝的方式。

操作系统I/O过程中,需要把数据从用户态拷贝到内核态,然后再输出到I/O设备,所以从java堆内存输出到I/O设备需经过两次拷贝,而direct memory在native 堆上,所以只需经过一次拷贝。

[1]

[2]
[3]
[4]
[5]

转载地址:http://azgfa.baihongyu.com/

你可能感兴趣的文章
用tar和split将文件分包压缩
查看>>
[BTS] Could not find stored procedure 'mp_sap_check_tid'
查看>>
PLSQL DBMS_DDL.ALTER_COMPILE
查看>>
Activity生命周期
查看>>
高仿UC浏览器弹出菜单效果
查看>>
Ubuntu忘记密码,进不了系统的解决方法
查看>>
[原创]白盒测试技术思维导图
查看>>
<<Information Store and Management>> 读书笔记 之八
查看>>
Windows 8 开发之设置合约
查看>>
闲说HeartBeat心跳包和TCP协议的KeepAlive机制
查看>>
MoSQL
查看>>
Hibernate多对一外键单向关联(Annotation配置)
查看>>
《CLR via C#》读书笔记 之 方法
查看>>
设计模式:组合模式(Composite Pattern)
查看>>
ContentValues 和HashTable区别
查看>>
LogicalDOC 6.6.2 发布,文档管理系统
查看>>
给PowerShell脚本传递参数
查看>>
实战2——Hadoop的日志分析
查看>>
利用FIFO进行文件拷贝一例
查看>>
Ecshop安装过程中的的问题:cls_image::gd_version()和不支持JPEG
查看>>