Linux运维进阶-Python,Docker,Shell

理解linux平台上java程序的内存模型

2020-04-30  本文已影响0人  My熊猫眼

java 程序是运行在jvm 虚拟机里面的,离开jvm虚拟机,那么java程序无法直接在linux平台的运行。 所以java应用程序和os 平台之间是隔着jvm虚拟机的。
所谓的jvm虚拟机,本质上就是一个进程,此时它的内存模型和普通的进程有相同之处,但它又是java程序的管理者,所以它又有自己独特的内存模型.
从os层面来看jvm的进程,其内存模型包含如下几个部分: 内核内存 + jvm的code + jvm的data + jvm的 heap + jvm的stack + unused memory. 其中的heap, stack 就是我们常说的“堆栈” 空间. 我们更多需要从jvm作为java程序管理者的角度来看其内存模型:
此时jvm的内存空间可以分为两大类,分别是 “堆内存” 以及“非堆内存”,其中前者是可以分配给java程序使用的,而后者则是jvm进程自己使用的。 所以“堆内存”是我们要讨论的重点:

A. “堆内存”的大小是通过如下两个参数控制的:
-Xms , 这个是jvm启动时候的初始堆大小.
-Xmx, 这个是jvm最大允许分配的堆内存大小.
jvm进程会根据 可用堆空间的大小,动态调整jvm的堆大小,但是最大不超过Xmx设置的值,当然也会减小堆空间的大小,最小为Xms设置的值;如果设置的Xms 大于 Xmx, 那么可能会导致java程序无法正常启动,通常情况下,设置Xms 和Xmx 一样大小,以避免jvm频繁调整堆的大小.
jvm进程不仅仅提供了java程序的运行环境,同时还进行 java 程序的内存回收工作(也就是GC操作),程序员从而可以不用考虑内存回收,这个是jvm进程(也就是java虚拟机)来完成的.

B. GC 过程的理解:
在jvm进行GC的时候,会遍历所有已经分配的堆空间里的对象,从而判断是否可以进行内存回收, 如果这时候有一部分数据已经在SWAP空间上,那么就需要把这部分数据交换回内存,而如果内存本就不够,就需要把堆空间中的另一部分给交换到swap中,这时候可能发生的最坏的情况是:堆中的对象被全部交换到swap中,然后再swap到堆中. 而Linux的swap回收是具有滞后性的,所以可能看到swap的空间被大量使用. 同时会经历系统响应缓慢的情况.

C. java程序的NIO:
通常情况下,应用进程不直接访问内核内存,而是通过操作系统作为一个中介层实现和内核的交互,但是随着对性能的追求,特别是为了解决高并发的性能问题,在java里面有了一种叫做NIO(Non-blocking IO)的的访问方式,比较NIO 和原始IO的区别:
原始IO是面向流的,每次从流中读取一个或多个字节直至读取所有字节,没有缓存这些数据,这就意味着当一个线程调用read或者write方法的时候,线程处于阻塞状态直到io 操作完成. 当并发非常高的时候,就会出现明显的问题.
NIO是面向缓冲的,有一个通道channel 和这个缓冲区buffer 相对应;读取的时候,访问通道channel来获得缓冲区buffer的数据,通道channel支持异步读写数据,这样线程无需等待IO操作的完成,从而解决了线程读操作的阻塞问题. 在数据写入的时候,向channel 写入数据就可以了,因为支持异步IO读写,所以也无需等待写入的完成,所以在数据写入的时候也解决了阻塞的问题. 另外,通道channel 支持同时进行读写操作,而流仅仅支持单向的读或者写. 另外,磁盘数据和缓冲区之间通常是采用“块操作”,这个效率也比流式操作高的多, 当IO操作完成后,缓冲区Buffer会被释放,以便再次使用。
和流式比起来,缺点是: 需要使用缓冲区,也就是需要消耗一定的内存资源, 而流操作则不需要缓冲区, java 的NIO使用的内存区域是内核内存的system 区和PageCache区

D. java占用空间大小计算:
java 程序是在jvm里面运行的,所以java 程序占用的内存大小理论上不会超过 JVM的 堆大小,主要包含以下部分:

java 永久代(java程序的代码区和数据区) + java 堆(新生代 和 老年代) + java 线程栈空间大小+ NIO

其中jvm配置的"堆"大小的最大值,就是: "java 永久代+java 新生代+java 老年代" 的最大值

默认情况下,每个java线程 stack的大小为1M, 所以java线程栈大小和线程数量多少有关,这部分内存不属于jvm管理的内存

NIO的大小,如果java 大量使用NIO, 这个值就会比较大,要通过监控工具查看其大小, 这部分内存属于内核内存的System 区和PageCache区域。

如果系统内存明显足够,但是依然大量使用 swap,其中的一种可能是: NIO的buffer 回收不及时,导致pagecache 空间不足,而pagecache不足的情况下,如果依然有很多的NIO 访问,那么必然导致需要导致大量使用swap.
上一篇下一篇

猜你喜欢

热点阅读