java堆内堆外内存困惑

  1. 操作系统IO分为 直接IO和缓存IO
    可以参考:
    1. https://www.icode9.com/content-4-450323.html
    2. https://blog.youkuaiyun.com/lzh1179805109/article/details/79883523
  2. 简单的理解缓存IO就是:可以把缓存IO认为是操作系统给磁盘增加的缓存,这样的话,用户进程的数据write和read其实针对的就是内核的page cache(磁盘缓存),至于page cache什么时候和磁盘同步,这个在此不多介绍,可以搜索一下,默认是脏页的同步。
  3. Linux系统默认就是使用的缓存IO,所以咱们代码里的数据读写,也就会发生至少两次拷贝:一、从用户进程地址空间拷贝到内核空间的page cache;二、从内核page cache拷贝到磁盘(此处还涉及到寻址);
  4. 那么对于Java程序来说:因为使用了JVM,其实就更复杂了。因为JVM相对于操作系统就是用户进程,但是JVM里面又划分了一个JVM的堆,所以其实所谓的堆外空间的IO才是上面讲的 用户进程地址空间拷贝到内核缓存。那么jvm堆内的数据的IO呢?参照这篇文章https://blog.youkuaiyun.com/qq_21125183/article/details/88701156下面作者给读者的评论,是说
    1. 正常IO是两次拷贝,但是由于Java 中堆的GC机制,在IO操作时会导致地址变化移动(但是C调用IO时要求地址是不能发生变化,否则出错),所以会拷贝到堆外内存,从而存在堆内与堆外多一次的复制。
    2. 也就是因为jvm因为通过调用native方法,从而使用的C语言封装的系统调用,从而必须要满足C语言调用IO的限制(要求IO时地址不能发生变化),所以jvm因为堆内内存可能发生gc,导致数据使用的地址变化,从而在进行IO的时候,需要有堆内堆外拷贝的过程,拷贝到堆外内存之后再通过C语言的系统调用进行用户进程和操作系统内核缓存数据的交互。
    3. 也因此解释了,C语言没有垃圾回收,也没有内存整理,需要用户手动free或者delete内存的占用;而Java用了gc,方便了用户,牺牲了IO性能。因为按照上面的理解,Java程序在发生IO时,比C语言的进程更多一次数据拷贝。

在这里插入图片描述
FileChannelImpl#write

public int write(ByteBuffer var1) throws IOException {
        this.ensureOpen();
        if (!this.writable) {
            throw new NonWritableChannelException();
        } else {
            synchronized(this.positionLock) {
                int var3 = 0;
                int var4 = -1;

                byte var5;
                try {
                    this.begin();
                    var4 = this.threads.add();
                    if (this.isOpen()) {
                        do {
                        	// 可以看到此处的调用, fd是文件描述符,操作系统层面对应某个文件
                            var3 = IOUtil.write(this.fd, var1, -1L, this.nd);
                        } while(var3 == -3 && this.isOpen());

                        int var12 = IOStatus.normalize(var3);
                        return var12;
                    }

                    var5 = 0;
                } finally {
                    this.threads.remove(var4);
                    this.end(var3 > 0);

                    assert IOStatus.check(var3);

                }

                return var5;
            }
        }
    }

IOUtil.write

static int write(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException {
		// 如果是直接内存(堆外内存)缓冲,直接把数据写入操作系统的page cache
        if (var1 instanceof DirectBuffer) {
            return writeFromNativeBuffer(var0, var1, var2, var4);
        } else {
            int var5 = var1.position();
            int var6 = var1.limit();

            assert var5 <= var6;

            int var7 = var5 <= var6 ? var6 - var5 : 0;
            // 从直接内存(堆外内存)中根据需要写入数据大小分配一块临时大小的空间
            ByteBuffer var8 = Util.getTemporaryDirectBuffer(var7);

            int var10;
            try {
            	// 把堆内缓冲数据拷贝到堆外内存中
                var8.put(var1);
                var8.flip();
                var1.position(var5);
                // 把堆外内存数据写入操作系统的 page cache
                int var9 = writeFromNativeBuffer(var0, var8, var2, var4);
                if (var9 > 0) {
                    var1.position(var5 + var9);
                }

                var10 = var9;
            } finally {
                Util.offerFirstTemporaryDirectBuffer(var8);
            }

            return var10;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值