直接内存

常见于 NIO 操作时,用于数据缓冲区

分配回收成本较高,但读写性能高

不受 JVM 内存回收管理

注意手动的System.gc是不能释放直接内存的

分配和回收原理

使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法
ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦
ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调
用 freeMemory 来释放直接内存

特别注意,

-XX:+DisableExplicitGC 显式的,这个是在jvm调优的过程中将System.gc失效,所以导致ByteBuffer 不能被回收,导致直接内存不能被释放。

演示直接内存溢出

public class Demo {
    static int _100Mb = 1024 * 1024 * 100;

    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
                list.add(byteBuffer);
                i++;
            }
        } finally {
            System.out.println(i);
        }
        // 方法区是jvm规范, jdk6 中对方法区的实现称为永久代
        //                  jdk8 对方法区的实现称为元空间
    }
}

显式gc回收对直接内存的影响

public class Demo {
   static int _1Gb = 1024 * 1024 * 1024;

   /*
    * -XX:+DisableExplicitGC 显式的
    */
   public static void main(String[] args) throws IOException {
       ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
       System.out.println("分配完毕...");
       System.in.read();
       System.out.println("开始释放...");
       byteBuffer = null;
       System.gc(); // 显式的垃圾回收,Full GC
       System.in.read();
   }
}

直接内存分配的底层原理:Unsafe

public class Demo {
   static int _1Gb = 1024 * 1024 * 1024;

   public static void main(String[] args) throws IOException {
       Unsafe unsafe = getUnsafe();
       // 分配内存
       long base = unsafe.allocateMemory(_1Gb);
       unsafe.setMemory(base, _1Gb, (byte) 0);
       System.in.read();

       // 释放内存
       unsafe.freeMemory(base);
       System.in.read();
   }

   public static Unsafe getUnsafe() {
       try {
           Field f = Unsafe.class.getDeclaredField("theUnsafe");
           f.setAccessible(true);
           Unsafe unsafe = (Unsafe) f.get(null);
           return unsafe;
       } catch (NoSuchFieldException | IllegalAccessException e) {
           throw new RuntimeException(e);
       }
   }
}

演示 ByteBuffer 作用

public class Demo {
    static final String FROM = "E:\\编程\\视频\\0.mp4";
    static final String TO = "E:\\a.mp4";
    static final int _1Mb = 1024 * 1024;

    public static void main(String[] args) {
        io(); // io 用时:1535.586957 1766.963399 1359.240226
        directBuffer(); // directBuffer 用时:479.295165 702.291454 562.56592
    }

    private static void directBuffer() {
        long start = System.nanoTime();
        try (FileChannel from = new FileInputStream(FROM).getChannel();
             FileChannel to = new FileOutputStream(TO).getChannel();
        ) {
            ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb);
            while (true) {
                int len = from.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                to.write(bb);
                bb.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("directBuffer 用时:" + (end - start) / 1000_000.0);
    }

    private static void io() {
        long start = System.nanoTime();
        try (FileInputStream from = new FileInputStream(FROM);
             FileOutputStream to = new FileOutputStream(TO);
        ) {
            byte[] buf = new byte[_1Mb];
            while (true) {
                int len = from.read(buf);
                if (len == -1) {
                    break;
                }
                to.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("io 用时:" + (end - start) / 1000_000.0);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值