资讯详情

JVM 与 Linux 的内存关系详解,java五年经验面试题

内核内存是Linux内存空间主要用于程序调度、内存分配、连接硬件资源等程序逻辑使用。

用户内存是每个过程的主要空间,Linux为每个过程提供相同的虚拟内存空间;这使得过程相互独立,不相互干扰。实现的方法是使用虚拟内存技术:给每个过程一定的虚拟内存空间,只有当虚拟内存实现时 物理内存只有在使用时才能分配。

如下图所示,32Linux就系统而言,一般为0~3G虚拟内存空间分配为用户空间,3~4G虚拟内存空间分配 为核心空间;64位系统的划分类似。

JVM 与 Linux 的内存关系详解

从过程的角度来看,可以直接访问过程的用户内存(虚拟内存空间)分为代码区、数据区、堆叠区、堆叠区和未使用区五部分。

将应用程序存储在代码区域的机器代码不能在运行过程中修改,具有只读和固定尺寸的特点。

应用程序中的全局数据、静态数据和一些常量字符串存储在数据区,其大小也是固定的。

堆是程序运行时动态应用的空间,是程序运行时直接应用和释放的内存资源。

栈区用于存储传入参数、临时变量、返回地址等数据。

未使用区域分配新内部 储存空间的准备区域。

二、进程与JVM内存空间

============


JVM本质上是一个过程,所以它的内存空间(也称为运行时的数据区,注意和JMM差异)也有过程的一般特征。深入简单 Java 中 JVM 本文参考内存管理。

但是,JVM这不是一个普通的过程,它在内存空间中有许多新的特征,主要有两个原因 个:

  • JVM将许多原本属于操作系统管理范畴的东西移植到JVM内部,目的在于减少系统调用的次数;

  • Java NI

O,目的是减少用于读写的用途IO系统调用的费用。JVM与普通过程内存模型相比,过程如下:

需要注意的是,这个模型不是JVM内存使用的精确模型更注重从操作系统的角度省略一些JVM内部细节(虽然也很重要)。下面从用户内存和内核内存两个方面的解释JVM过程的内存特征。

1.用户内存


上图特别强调JVM过程模型的代码区和数据区是指JVM而不是自己的Java程序的。普通进程栈区,在JVM一般只用作线程栈。JVM堆区与普通工艺的区别最大,具体说明如下:

首先是永久代。永久代本质上是Java代码区码区和数据区。Java程序中类(class),它将被加载到整个区域的不同数据结构中,包括常量池、域、方法数据、方法体、结构函数、类别中的特殊方法、实例初始化、接口初始化等。这个区域对于操作系统来说,是堆的一个部分;而对于Java程序来 这就是容纳程序本身和静态资源的空间,使之JVM能够解释执行Java程序。

二是新生代和老年代。新生代和老年代是。Java堆叠空间主要用于存储内存对象;但其管理模式与普通过程有本质区别。

例如,当普通过程在运行过程中为内存对象分配空间时,C 执行new操作时,会触发分配内存空间的系统调用,操作系统的线程会根据对象的大小分配空间,然后返回 同时,当程序释放对象时,如C 执行delete操作时,还会触发系统调用,通知操作系统对象占用的空间已经可以回收。

JVM内存的使用不同于一般过程。JVM向操作系统申请一个完整的内存区域(具体尺寸可以JVM参数调节)作为Java程序堆(分为新生代和老年代);当Java程序申请内存空间,如执行new操作,JVM根据这个空间所需的大小分配Java程序,并且Java程序不负责通知JVM何时可以释放这个 对象的空间,回收垃圾对象内存空间的原因JVM进行。

JVM内存管理的优点很明显,包括:一是减少系统调用次数,JVM在给Java在程序分配内存空间时,不需要操作系统干预 Java当堆积尺寸变化时,需要向操作系统申请内存或通知回收,而普通程序需要系统调用参与每个内存空间的分配和回收;第二,减少内存泄漏,没有普通程序(或 通知操作系统内存空间的释放是内存泄漏的重要原因之一JVM统一管理可以避免程序员造成的内存泄漏。

最后是未使用区,是分配新内存空间的备区。对于普通过程,该区域可用于堆叠和堆叠空间的应用和释放,该区域将用于每次堆叠内存分配 因此,域的大小变化频繁;对于JVM在过程中,该区域将用于调整堆尺寸和线程堆栈,而堆尺寸一般较小,因此尺寸相对稳定。该区域的操作系统将动态调整 该区域的实际物理内存通常际的物理内存,只允许过程在这个区域申请堆叠或堆叠空间。

2.内核内存


应用程序通常不直接处理内核内存,内核内存由操作系统管理和使用;但随着Linux对性能的关注和改进,一些新的特性使应用程序能够 使用内核内存,或映射到内核空间。Java NIO正是在这种背景下诞生的,充分利用了它Linux系统的新特性得到了改进Java程序的IO性能。

上图给出了Java NIO使用的内核内存linux系统中的分布。nio buffer主要包括:nio使用各种channel时所使用的ByteBuffer、Java主动使用程序 ByteBuffer.allocateDirector申请分配的Buffer。

而在PageCache里面,nio使用的内存主要包 括:FileChannel.map打开文件占用的方式mapped、FileChannel.transferTo和 FileChannel.transferFrom所需要的Cache(图中标示 nio file)。

通过JMX可以监控到NIO Buffer和 mapped 如下图所示。FileChannel通过系统调用实现原生的实现PageCache,过程对于Java它是透明的,不能监控这部分内存的使用。

Linux和Java NIO为程序使用内核内存开辟空间,主要是为了减少不必要的复制IO操作系统调用的费用。例如,将磁盘文件的数据发送到网卡件的数据发送到网卡上NIO时,数据流动比较下图所示:

复制内核内存和用户内存之间的数据消耗资源和时间,从上图可以看出,通过NIO的方式减少了2次内核内存和用户内存之间的数据拷贝。这是Java NIO高性能的重要机制之一(另一个是异步非阻塞)。

从上面可以看出,内核内存是对的Java程序性能也很重要。因此,在划分系统内存时,必须为核心留出一定的可用空间。

三、案例分析

======


1.内存分配问题


通过以上分析,可以总结省略相对较小的区域JVM占用的内存:

JVM内存 ≈ Java永久代 + Java堆(新生代和老年) + 线程栈+ Java NIO

回到文章开头提出的问题,原来的内存分配是:6g(java堆) + 600m(监控) + 800m(系统)剩余约6000m未分配内存。

现在分析这600m内存分配:

  • Linux保留大约200m,这部分是Linux需要正常运行,

  • Java线程数量为160,JVM默认线程栈的大小为1m,因此使用160m内存,

  • Java NIO buffer,通过JMX最多占2000m,

  • Java服务使用NIO需要大量阅读和撰写文件PageCache,就像之前的分析一样,这暂时不容易定量估计大小。

前三项加起来已经560了m,因此可以断定Linux物理内存不足。

细心的人会发现引言中有两个服务器,一个SWAP最多占用了2.16g,另外一个SWAP最多占用了871m;然而,我们的内存差距似乎并不大。事实上,这是因为SWAP和GC同时,从下图可以看出,SWAP长期使用GC在同一时刻发生。

SWAP和GC同时发生会导致GC时间很长,JVM严重卡顿,极端的情况下会导致服务崩溃。原因如下:JVM进行GC当时,有必要使用相应堆分区的用 如果GC当堆的一部分内容被交换到SWAP当你遍历这部分时,你需要将其交换回内存。同时,由于内存空间不足,你需要将内存堆放在一起 另一部分部分SWAP所以在遍历堆分区的过程中,(极端情况下)整个堆分区将轮流进行SWAP写一遍。Linux对SWAP回收滞后,我 会看到很多SWAP占用。Linux对SWAP回收滞后,我 会看到很多SWAP占用。通过减少堆积大小或增加物理内存,可以解决上述问题。

原因如下:JVM进行GC当时,有必要使用相应堆分区的用 如果GC当堆的一部分内容被交换到SWAP当你遍历这部分时,你需要将其交换回内存。同时,由于内存空间不足,你需要将内存堆放在一起 另一部分部分SWAP所以在遍历堆分区的过程中,(极端情况下)整个堆分区将轮流进行SWAP写一遍。Linux对SWAP回收滞后,我 会看到很多SWAP占用。Linux对SWAP回收滞后,我 会看到很多SWAP占用。通过减少堆积大小或增加物理内存,可以解决上述问题。

标签: 500n扭距传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台