本文转载自美团技术团队发表的同名文章html
https://tech.meituan.com/linux-jvm-memory.htmljava
一, linux与进程内存模型linux
要理解jvm最重要的一点是要知道jvm只是linux的一个进程,把jvm的视野放大,就能很好的理解JVM细分的一些概念c++
下图给出了硬件系统进程三个层面内存之间的关系.程序员
从硬件上看,Linux系统的内存空间由两个部分构成:物理内存和SWAP(位于磁盘)。物理内存是Linux活动时使用的主要内存区域;当物理内存不够使用时,Linux会把一部分暂时不用的内存数据放到磁盘上的SWAP中去,以便腾出更多的可用内存空间;而当须要使用位于SWAP的数据时,必须先将其换回到内存中。数据结构
从Linux系统上看,除了引导系统的BIN区,整个内存空间主要被分红两个部分:内核内存(Kernel space)、用户内存(User space)。app
内核内存是linux自身使用的内存空间,主要提供程序调度,内存分配,连接硬件资源等程序逻辑使用.用户内存是提供给各个进程主要空间,linux给每一个进程提供4G相同的虚拟内存空间;这种虚拟内存空间,是一种寻址的空间,进程之间都有相互独立的4G空间地址,实际用到多少,最终会经过页表写入真正的磁盘.所以进程之间也是相互独立的,互不干扰异步
虚拟内存空间分配以下,jvm
从进程角度看进程能直接访问的用户内存被划分为五个部分(与jvm的内存划分相似,由于它也是linux的一个进程):代码区,数据区,堆区,栈区,未使用区,函数
代码区中存放应用程序的机器代码,运行过程当中代码不能被修改,具备只读和固定大小的特色
数据区中存放应用程序的机器代码静态数据和一些常量字符串等,其大小也是固定的,
堆是运行时程序动态申请的空间,属于程序运行时直接申请释放的内存资源(jvm的GC也主要回收该部分空间)
栈区用来存放函数的传入参数,临时变量(jvm中的方法使用的栈帧),以及返回地址等数据,
二,进程与jvm内存模型
jvm本质是一个进程,所以其内部也有进程的通常特色,可是jvm不是一个普通的进程,其内存模型有一些新特色:1,jvm将许多原本属于操做系统管理范畴的东西,移植到了jvm内部,目的在于减小系统调用的次数;2,JavaNIO,目的在于减小用于读写IO的系统调用的销,jvm进程与普通进程内存模型比较图:
须要说明的是,这个模型的并非JVM内存使用的精确模型,更侧重于从操做系统的角度而省略了一些JVM的内部细节(尽管也很重要)。下面从用户内存和内核内存两个方面讲解JVM进程的内存特色。
1,用户内存
上图特别强调了JVM进程模型的代码区和数据区指的是JVM自身的,而非Java程序的。普通进程栈区,在JVM通常仅仅用作线程栈。JVM的堆区和普通进程的差异是最大的,下面具体详细说明
首先是永久代,永久代本质上是Java程序的代码区和数据区,Java程序中类(class),会被加载整个区域的不一样数据结构中去,包括常量池,域,方法数据,方法体,构造函数,以及类中的专用方法,实例初始化,接口初始化.永久代对于操做系统来讲是堆的一部分;而对于Java程序来讲,这是容纳程序自己及静态资源的空间
其次是新生代和老年代,新生代和老年代才是Java程序真正使用的堆空间,主要用于内存对象的存储;可是其管理方式和普通进程有本质的区别。
普通进程在运行时给内存对象分配空间时,好比C++执行new操做时,会触发一次分配内存空间的调用,由操做系统的线程根据对象的大小分配好空间后返回;同时程序释放对象时,好比c++执行delete操做时也会触发一次系统调用,通知操做系统对象所占用的空间能够回收了
jvm对于内存的使用和通常进程不一样,jvm像操做系统申请一整段内存区域(具体大小能够在jvm参数调节)做为Java内存的堆(分为新生代和老年代);当Java程序申请内存空间,好比new操做时,jvm将在这段空间中按所需大小分配给Java程序,而且Java不负责通知回收该内存空间.
JVM的内存管理方式的优势是显而易见的,包括:第一,减小系统调用的次数,JVM在给Java程序分配内存空间时不须要操做系统干预,仅仅在Java堆大小变化时须要向操做系统申请内存或通知回收,而普通程序每次内存空间的分配回收都须要系统调用参与;第二,减小内存泄漏,普通程序没有(或者没有及时)通知操做系统内存空间的释放是内存泄漏的重要缘由之一,而由JVM统一管理,能够避免程序员带来的内存泄漏问题。
最后是未使用区,未使用区是分配新内存空间的预备区域。对于普通进程来讲,这个区域被可用于堆和栈空间的申请及释放,每次堆内存分配都会使用这个区域,所以大小变更频繁;对于JVM进程来讲,调整堆大小及线程栈时会使用该区域,而堆大小通常较少调整,所以大小相对稳定。操做系统会动态调整这个区域的大小,而且这个区域一般并无被分配实际的物理内存,只是容许进程在这个区域申请堆或栈空间。
2.内核内存
应用程序一般不直接和内核内存打交道,内核内存由操做系统进行管理和使用;不过随着linux对性能的关注及改进,一些新的特性使得应用程序可使内核内存或者映射到内核空间.javaNIO正是在这种背景下诞生的,其充分利用了linux系统的新特性,提高了Java程序的IO性能
上图给出了NIO使用的内核内存在linux系统中的分布状况,NIObuffer主要包括:NIO使用各类channel时所使用的ByteBuffer,Java程序主动使用ByteBuffer,allocationDirector申请分配的Buffer.而在PageCache里面,NIO使用的内存主要包括FileChannel.map方式打开文件占用mapped,FileChanneltransferTo和FileChannel.transferFRrom
Linux和JavaNIO在内核内存上开辟空间给程序使用,主要是减小没必要要的复制,减小IO操做系统调用的开销.例如将磁盘文件数据发送到网卡,使用普通方法和NIO时,数据流动比较
将数据在内核内存和用户内存之间拷贝是比较消耗资源和时间的事情,而从上图咱们能够看到,经过NIO的方式减小了2次内核内存和用户内存之间的数据拷贝。这是Java NIO高性能的重要机制之一(另外一个是异步非阻塞)。
从上面能够看出,内核内存对于Java程序性能也很是重要,所以,在划分系统内存使用时候,必定要给内核留出必定可用空间。
https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554698&idx=1&sn=7f08e278c1f6a7c0db0ba5418c442fa4&chksm=f3f833dcc48fbacaede8af14925b983f871e834e58ba079c3cb41557bd5fa5596817fcab1543#rd