一、java的堆外内存

堆外内存的限额默认与堆内内存(由-XMX 设定)相同,可用 -XX:MaxDirectMemorySize 重新设定

1、优缺点

优点:

(1)可以扩展至更大的内存空间。比如超过1TB甚至比主存还大的空间;

(2)理论上能减少GC暂停时间;

(3)可以在进程间(系统调用,aio)共享,减少JVM间的对象复制,使得JVM的分割部署更容易实现;

(4)它的持久化存储可以支持快速重启,同时还能够在测试环境中重现生产数据

(5)堆外内存能减少IO时的内存复制,不需要堆内存Buffer拷贝一份到直接内存,然后才写入Socket中

缺点:

(1)需要手动释放(或者在引用直接内存的对象回收的时候释放,相当于依赖gc)

(2)当依靠gc时,需要考虑gc时机如何确定,频繁显示调用System.gc()显然不是个好主意,因为不可控,而依靠堆内存控制,需要计算堆外内存的增长速率与堆内内存增长速率的关系,合理配置jvm参数,在堆外内存OOM之前触发gc

(3)Unsafe对象通常是不可见的,因为其实现并非开源,并且不同jre发行商都对此有不同实现和政策

2、申请

(1)通过Unsafe对象,调用native方法,与c++类似,
ptr =Unsafe.allocateMemory(size),
(2)Java.nio.ByteBuffer也可以申请直接内存,底层也是使用Unsafe,但是有较安全的管理机制。

3、释放

(1)Unsafe.freeMemory(ptr)。
(2)ByteBuffer在Oracle JVM中是通过Cleaner的机制去释放。
ByteBuffer通过将自身注册到Cleaner中,作为一个虚引用,在gc时的回调方法里释放直接内存。

二、Netty的堆外内存使用

Cleaner这个类可被重复执行,释放过了就不再释放。所以GC时再被动执行一次clean()也没所谓。

在Netty里,因为不确定跑在Sun的JDK里(比如安卓),所以多废了些功夫来确定Cleaner的存在。

Netty的内存buffer对象都继承了ReferenceCounted接口,并且在netty handler的设计规范中,所有输入的数据在处理结束时都会调用ReferenceCountUtil.release()释放,只是具体释放方法根据池化、非池化、直接内存、堆内存的不同实现不同。

引用数工具类:ReferenceCountUtil

池化对象接口:ReferenceCounted

1、池化

专家们说,OpenJDK没有接受 jemalloc (redis们在用)的补丁,直接用malloc在OS里申请一段内存,比在已申请好的JVM堆内内存里划一块出来要慢,所以我们在Netty一般用池化的 PooledDirectByteBuf 对DirectByteBuffer进行重用 ,《Netty权威指南》说性能提升了23倍,所以基本不需要头痛堆外内存的释放,顺便还告别了大数据流量下的频繁GC。

在netty4.0以后,池化内存都在一个PoolArena基类型的线程变量中进行申请释放,对于不熟悉netty新版的开发者来说,经常会掉入这个大坑中。

2、非池化

一般在业务线程中发送数据时使用,因为一般netty的handler只处理ByteBuf类型的数据,我们可以用Unpooled.wrapper方法提供一个对byte[]的封装即可。

如果使用非池化堆外内存,其实就是DirectByteBuffer的一个封装,使用也与其类似。

三、Netty开发中的注意事项

1、线程模型

总的来说是IO线程池+业务线程池,IO线程在服务器可以把acceptor和读写分离,而对于轻业务的应用,可以省略业务线程,直接在io线程中处理。

在netty4.0以后的版本要特别注意每个连接的IO处理都是在“同一个线程”,打引号是因为在局部他确实坚持了这一原则,但在宏观配置下,这种特性也可以被打破,netty的线程被封装叫做EventExecutor, 通常我们获取EventExecutor的方式是通过ChannelHandlerContext或者Channel,ChannelHandlerContext引用了Channel.要非常注意的点就是,Channel的EventExecutor是WorkerGroup也就是常说的IO线程, 在连接建立的时候就由WorkerGroup线程组分配了一个线程,并在断开连接前都由这一个线程处理该Channel的IO工作,ChannelHandlerContext的EventExecutor是依赖配置,如果为空就使用Channel的EventExecutor。在netty底层代码中,经常可以看到这样的段落:

if (eventLoop.inEventLoop()) { register0(promise); } else { try { eventLoop.execute(new Runnable() { @Override              public void run() { register0(promise);             }         });

也就是说不管在任何线程执行代码,有的操作(底层一般是io)只会在同一个线程执行,这个原则主要是为了减少锁的存在,提高性能而且逻辑也简单很多,虽然在各种枝端末节都会重复出现上面的代码,但好在都隐藏在底层。

对于每个handler都可以分配独立的线程池,当然在一般需求中很难有多层次的异步解耦,而且非常不建议在IO处理的过程中加入多个线程池,可能会导致内存泄露,原因后面会说。4.1之后有一个新特性,就是对于业务线程池可以设置是否在固定的线程中执行handler,这和IO线程的方式一样,是一开始就分配一个线程还是总是从线程池中取出一个空闲的线程使用,这就根据业务情况自行选择了,业务是否有顺序和同步的要求。

2、内存池

DirectByteBuffer的释放依赖于gc,所以在DirectByteBuffer的实践中,为了防止OOM,每次申请新的堆外内存都调用System.gc(),当然这样做的副作用就是增加了gc的次数,但由于该方法触发gc的延迟特性,也可能导致偶发性的OOM,更甚至大部分java程序的jvm启动参数是禁用显示的gc调用。所以netty为了缓解这个冲突性的问题,把直接内存池化,在稳定的业务环境中可以保证使用的堆外内存不增长,但为了更加安全,还需要对具体的程序进行调优。
netty的内存池实现类是PoolThreadCache,其中包含直接内存的池与堆内存的池(PoolArena),从名称可以看出,PoolThreadCache是一个线程变量,因为其中涉及内存块的拆分和合并,为了免除锁的使用,所有的申请和释放都只能在同一个线程内完成。在netty的默认配置中,所有IO线程都是使用池化的直接内存读写网络数据,所以如果要加入异步业务时,一定要在池化内存传递到另一个线程前拷贝到非池化对象,然后释放池化资源。

3、Handler

与大部分服务器管道模式相似,netty的Handler chain对应spring的filter chain,与filter都是单例不同的在于netty中的ChannelHandler有单例共用的类型,也有复用的类型,一般通过ChannelHandlerInitializer去配置新建的连接,如果不是新的实例那肯定是复用类型,如果你的单例handler没有使用@Sharable注解,还会收到一个警告。

大部分handler都是单例,而基本上帧解码器都是新实例,因为tcp粘包的需要,半包的字节累加器对于每个连接都是独立的。

对于ChannelHandler还有一点要注意,一般来说输入(InBound)处理会把已处理的对象在结尾进行回收处理,解码器还会对输出的对象也回收,但在此之前handler会把输出对象传递到下一个handler,一般传递的Handler处理都是同步的,但如果handler分配了不同的线程池就会变成异步,有可能在下个handler处理前就把你将要处理的对象进行回收操作。ByteBuf一般都是个封装,底层的直接内存、堆内存有可能就被释放掉了,为了避免回收,主要是针对ByteBuf类型,一般要做引用数+1的操作。

4、Jvm参数

一般情况下,netty的网络IO都要使用直接内存,所以部署netty项目时,一定要考虑堆外内存的大小,由于这部分直接内存是池化的而且仅用于IO,所以对于稳定的上传下载流量下,内存池会增长到一个平衡点,这个大小约等于瞬时峰值的接收字节+发送字节数量,减少直接内存的借还周期就可以有效地控制直接内存池的大小,这也是为什么一般不在IO线程中处理业务,而把业务放到另外的线程中进行的原因之一。基本上可以用(最大并发数*最大包体)来估计直接内存的大小。
如果有业务需要使用到直接内存,就需要考虑何时回收直接内存,手动释放是一个选择,但可能会使逻辑复杂化,并难免遗漏。对于大部分项目来说还是希望通过GC自动去回收,所以如何保证在直接内存达到一定大小时触发fullGC呢?
一种方式是调整一个较小的老年代,并且生存区的阶段也尽可能短,促使保守的GC,当然这样对TPS肯定有影响。
方法二,根据堆内存的增长速率与直接内存的增长速率之间的关系去制定合理的GC触发机制,但这个需要业务比较单一,堆内存和直接内存之间的相关性比较高才可行。
方法三,对于堆内存增长速率比较稳定的项目,可以选择业务较少的时段定时触发fullGC,
至于实现方法可以使用jmap -live。

作者:gofun成都技术中心
链接:https://www.jianshu.com/p/7504e2cbe8db
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

netty堆外内存的使用相关推荐

  1. Netty堆外内存泄露排查与总结

    导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...

  2. Netty堆外内存泄露排查盛宴

    导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...

  3. Netty堆外内存泄露排查与总结 1

    导读 Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程. Netty 底层基于 JDK ...

  4. 一次堆外内存泄露的排查过程

    转载自  一次堆外内存泄露的排查过程 最近在做一个基于 websocket 的长连中间件,服务端使用实现了 socket.io 协议(基于websocket协议,提供长轮询降级能力) 的 netty- ...

  5. haddler处理队列 netty_Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  6. java nio 李林峰_Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  7. netty数据流堆外内存排查

    背景 行情服务每次在开盘与收盘期间的堆外内存都会上涨,并且需要周期性手动重启,影响到服务的稳定性 排查过程 1.堆外内存的计算标准 此matrix(used_direct_memory)计算标准由ne ...

  8. java堆外内存6_Java堆外内存排查小结

    简介 JVM堆外内存难排查但经常会出现问题,这可能是目前最全的JVM堆外内存排查思路.之前的文章排版太乱,现在整理重发一下,内容是一样的. 通过本文,你应该了解: pmap 命令 gdb 命令 per ...

  9. java nio 堆外内存_Java堆外内存之突破JVM枷锁

    对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收:而使用的内存是由JVM控制的. 那么,什么时机会进行垃圾回收,如何避免过度频繁的垃圾回收?如果JVM ...

最新文章

  1. 关于设计模式的一次聊天
  2. Windows平台SSH登录Linux并使用图形化界面
  3. linux之awk命令解读
  4. PL/SQL Developer中如何格式化SQL
  5. Bing搜索核心技术BitFunnel原理
  6. c语言将程序写为动态库,VS下生成C程序静态库(LIB)及动态库(DLL)的方法
  7. jQuery禁止Ajax请求缓存
  8. RT-Thread中堆和栈内存的分配
  9. jacobi matlab程序,jacobi迭代法实验MATLAB程序数值分析
  10. Python《使用Selenium 和pyautogui 实现自动登录淘宝》
  11. 瑞典禁止在5G网络中用华为和中兴设备 同时也将移除老设备
  12. 如何使一个你没有源代码的DLL文件变为强命名的DLL
  13. 9.2 定义和浏览翻译
  14. PADS2007库转换为PADS 9.0以上库文件
  15. 用python实现自动回复QQ消息——不到60行
  16. HTML短信测压引流源码
  17. 用户体验优化事半功倍:如何绘制客户行为轨迹图
  18. pandas 行列转换
  19. 概率论与数理统计的基本概念
  20. XCTF攻防世界WEB新手区题目writeup

热门文章

  1. 抖音怎么赚钱?好消息KTV模式来啦
  2. Spring Cloud 微服务网关Gateway组件
  3. 什么是回调地狱以及怎样解决回调地狱
  4. vue-router的cdn使用,一篇解决百度少有的cdn引用方法
  5. vs2017中 如何使用npm命令
  6. repeatingalarm
  7. 激光切割机切割圆孔不圆怎么处理
  8. pg常用工具之pg_waldump
  9. 余额宝技术架构及演进------阅读
  10. Django模型数据库配置