文档章节

堆外内存初探

boomya
 boomya
发布于 2016/07/01 15:10
字数 1634
阅读 398
收藏 6

使用Java语言的同学们都知道, Java的虚拟机对内存的管理大部分情况下就是指堆内存的管理, GC的也是对堆内存的清理和回收. 下面就看一下堆外内存的对JVM的意义.

第一次了解到堆外内存的使用场景是在使用netty, netty中提到的一个概念, "零拷贝", 也是netty高性能的原因之一. 零拷贝, 主要体现在三个方面:

  • Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外(直接)内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
  • Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
  • Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

在使用堆外内存的同时也带来了新的问题, 相比较堆内存, 堆外内存的分配和回收要更耗时, 所以netty提供了基于内存池的缓冲区重用机制.

将本地缓存和堆外内存联系到一起, 是有一次调试线上频繁FULL GC然后OOM的问题, 当时的情况是, 线上频繁的报警有FULLGC, 怀疑有内存泄露,, 通过dump堆内存快照, 分析后发现有一个特别大的HashMap, 原因是打点日志引起的, 应用默认集成了日志系统会自动记录用户的所有行为, 应用这边合并日志后发送到日志接收端, 日志接收端挂掉了, 导致一直在重试, 重试的过程中不停的有新的日志加进来, 最后导致FULLGC. 当时我就在想, 如果将这种本地缓存移到堆外是不是就可以不用参与GC, 也可以使用更大的内存.

堆外内存有以下特点:

  • 对于大内存有良好的伸缩性, 堆外内存突破JVM的内存限制
  • 对垃圾回收停顿的改善可以明显感觉到
  • 在进程间可以共享,减少虚拟机间的复制

堆外内存更适合生命周期中等或长期的对象

关于堆外内存的回收
堆外内存的回收其实依赖于我们的GC机制(堆外内存不会对GC造成什么影响)
首先我们要知道在java层面和我们在堆外分配的这块内存关联的只有与之关联的DirectByteBuffer对象了,它记录了这块内存的基地址以及大小,那么既然和GC也有关,那就是GC能通过操作DirectByteBuffer对象来间接操作对应的堆外内存了。
DirectByteBuffer对象在创建的时候关联了一个PhantomReference,说到PhantomReference它其实主要是用来跟踪对象何时被回收的,它不能影响GC决策.
GC过程中如果发现某个对象除了只有PhantomReference引用它之外,并没有其他的地方引用它了,那将会把这个引用放到java.lang.ref.Reference.pending队列里,在GC完毕的时候通知ReferenceHandler这个守护线程去执行一些后置处理, 而DirectByteBuffer关联的PhantomReference是PhantomReference的一个子类,在最终的处理里会通过Unsafe的free接口来释放DirectByteBuffer对应的堆外内存块

什么是基地址 这里就提出了段的概念, 将1G的数据划分为n个段, 每一个段是64K, 每一个段也就是每一个64K就是一个基地址 段内的数据的地址就是当前基地址的偏移地址, 此时 段地址+偏移地址就能够找到真正的内存数据了.

为什么要主动调用System.gc
System.gc()会对新生代的老生代都会进行内存回收,这样会比较彻底地回收DirectByteBuffer对象以及他们关联的堆外内存.
DirectByteBuffer对象本身其实是很小的,但是它后面可能关联了一个非常大的堆外内存,因此我们通常称之为**冰山对象*.
我们做ygc的时候会将新生代里的不可达的DirectByteBuffer对象及其堆外内存回收了,但是无法对old里的DirectByteBuffer对象及其堆外内存进行回收,这也是我们通常碰到的最大的问题.
如果有大量的DirectByteBuffer对象移到了old,但是又一直没有做cms gc或者full gc,而只进行ygc,那么我们的物理内存可能被慢慢耗光,但是我们还不知道发生了什么,因为heap明明剩余的内存还很多(前提是我们禁用了System.gc -- JVM参数DisableExplicitGC)。

JVM GC-Invisible Heap
淘宝基于OpenJDK HotSpot VM,定制且开源的服务器版Java虚拟机. GC-Invisible Heap,简称GCIH,是一种将Java对象从Java堆内移动到堆外,并且可以在JVM间共享这些对象的技术。

  • 没有序列化和反序列化
  • GC性能更高
  • 和直接操作JVM对象完全一样

如何从JVM移入堆外内存
遍历所有对象, 从根对象开始, 根据对象的引用关系递归调用moveIn(将该对象在GCIH的地址encode到刻对象的header上, 并设置header的最低2位为01).

如何阻止GC扫描
正常的GC过程是, 从root对象出发, 标记活的对象的同时, 将这个对象push到queue, 以便后续的GC线程处理 GCIH更改了GC过程, 当某个对象的地址在GCIH地址空间内时, 直接返回, 不将对象放到queue中

© 著作权归作者所有

boomya
粉丝 20
博文 19
码字总数 25712
作品 0
杭州
程序员
私信 提问
JVM初探- 使用堆外内存减少Full GC

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hanqing280441589/article/details/54406665 JVM初探-使用堆外内存减少Full GC 标签 : JVM 问题: 大部分主流...

菜鸟-翡青
2017/01/13
0
0
(二)Block之存储域 NSConcreteStackBlock,NSConcreteGlobalBlock,NSConcreteMallocBlock

相关文章 (一)Block的实质初探 (二)Block之存储域 NSConcreteStackBlock,NSConcreteGlobalBlock,NSConcreteMallocBlock (三)Block之截获变量和对象 (四)Block之 _block修饰符及其存...

madaoCN
2017/10/15
0
0
(三)Block之截获变量和对象

相关文章 (一)Block的实质初探 (二)Block之存储域 NSConcreteStackBlock,NSConcreteGlobalBlock,NSConcreteMallocBlock (三)Block之截获变量和对象 (四)Block之 _block修饰符及其存...

madaoCN
2017/10/21
0
0
ArrayList 底层数组扩容原理

原文出处:清浅池塘 ArrayList部分一共五篇文章了,并且引入了时间复杂度来分析,强烈建议大家一定要按顺序阅读,本文是第2篇,相关文章分别是: 1、ArrayList初始化 – Java那些事儿专栏 再...

清浅池塘
2017/12/02
0
0
JVM相关博文,jvm堆内存,堆外内存

使用GC日志命令行选项为: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename> JVM 【-server】 glassfish应用服务器 -server 启动 垃圾收集器默认组合方式为 新生代:Paralle...

weiliu007
2016/08/25
46
0

没有更多内容

加载失败,请刷新页面

加载更多

友元函数强制转换

友元函数强制转换 p522

天王盖地虎626
昨天
3
0
js中实现页面跳转(返回前一页、后一页)

本文转载于:专业的前端网站➸js中实现页面跳转(返回前一页、后一页) 一:JS 重载页面,本地刷新,返回上一页 复制代码代码如下: <a href="javascript:history.go(-1)">返回上一页</a> <a h...

前端老手
昨天
2
0
JAVA 利用时间戳来判断TOKEN是否过期

import java.time.Instant;import java.time.LocalDateTime;import java.time.ZoneId;import java.time.ZoneOffset;import java.time.format.DateTimeFormatter;/** * @descri......

huangkejie
昨天
2
0
分页查询

一、配置 /*** @author beth* @data 2019-10-14 20:01*/@Configurationpublic class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor(){ ......

一个yuanbeth
昨天
6
0
在LINQPad中使用Ignite.NET

LINQPad是进行.NET开发的一款优秀工具,非常有利于Ignite.NET API的快速入门。 入门 下载LINQPad:linqpad.net/Download.aspx,注意要选择64位操作系统的AnyCPU版本; 安装Ignite.NET的NuGet...

李玉珏
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部