Java堆外内存探索

Java堆外内存探索

引言

“墙内的人想尽办法要出去,墙外的人拼了命想进去”

作为一位Java的使用者,从一开始,JVM就帮我们处理好内存的分配回收问题,很多时候,我们也不用考虑这些问题。但是,随着越来越深入地使用Java,我们愈发觉得如果能够帮JVM减少一些重担,能够手动对内存进行控制,那么该多好!

YGC上的探索

记录的是掘金上的一篇文章https://juejin.im/post/5a9cb49df265da239706561a?utm_source=gold_browser_extension

  • 主要讲解了YGC的过程包括old-gen-scanning(用于在YGC判断除了GC ROOTS外的OLD GEN对YOUNG的引用关系)
  • 减低GC压力
    • 把内存放到堆外(压力只存在于对堆外引用的扫描)
    • 通信连接可以用堆外内存来分配,可以提高连接数同时降低JVM压力
  • 避免破坏GC的分代假设,特别避免老年代引用新生代对象

讲讲如何使用堆外内存

Java中使用堆外内存有两种途径,即UnSafe和NIO的ByteBuffer,两者皆可,其实ByteBuffer的底层也是基于UnSafe去实现对堆外内存的使用。-XX:MaxDirectMemorySize=40M

  • 区别
    • UnSafe用完需要调用freeMemory(address)释放堆外内存
    • ByteBuffer是基于JVM的堆内内存的DirecrByteBuffer引用,该引用被回收,则堆外内存就会回收

UnSafe

既然Unsafe类是堆外内存的使用始祖,那么,讲使用我们就直接翻过高墙,进去看看!!!

先说说UnSafe类的描述与功能:

  • UnSafe类官方建议只在JDK内部使用,这样更满足JAVA的设计初衷,即那堵内存使用的高墙。
  • 功能
    • CAS(DouLea大师就是利用UnSafe实现CAS乐观锁)
    • 使用堆外内存存储数据
    • 阻塞唤醒线程
    • 修改某个对象的属性的值
    • 实例化对象

Sample

//以下代码用反射获取UnSafe实例
private static Unsafe unsafe = null;
    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            try {
                unsafe = (Unsafe) field.get(null);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
//申请1KB内存
//保留address用于释放内存
long address = unsafe.allocateMemory(1024 * 1024);
//重新申请2KB内存
address=unsafe.reallocateMemory(address,1024*1024*2);
//存储
unsafe.putObject(String.class, 1024, "String");
//读取
String obj = (String) unsafe.getObject(String.class, 1024);
//CAS
boolean res = unsafe.compareAndSwapObject(String.class, 1024, "123", "124");
//..还有更多的JNI方法,有兴趣自己查看即可,例如可以获取某个对象某个属性的地址,然后进行操作等。

应用场景

  • GC对性能的影响较大的时候,适合使用堆外内存降低GC压力从而提高性能
  • 减少COPY——Netty的零拷贝技术
    • 如果数据的发送需要从堆内存到堆外内存再到网络或者其它地方,则适合使用堆外内存减少数据的拷贝消耗
  • 应用层缓存功能
发布了58 篇原创文章 · 获赞 32 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/rekingman/article/details/104217591
今日推荐