1、Java进程内存不回落
异常现象:针对 Java 应用进程进行压力测试,在压测过程中进程内存逐渐升高,但在压测结束后,进程占用内存仍然一直很高不回落,奇怪的是此时堆内存占用其实很低。即 Java应用一直占用高内存并且在空闲时也并未将内存归还给操作系统,这与通常的认知不同。
“JVM 的垃圾回收,只是一个逻辑上的回收,回收的只是 JVM 申请的那一块逻辑堆区域,将数据标记为空闲之类的操作,不是调用 free 将内存归还给操作系统”
1.1、操作系统与JVM的内存分配
JVM 的自动内存管理,其实只是先向操作系统申请了一大块内存,然后自己在这块已申请的内存区域中进行“自动内存管理”。JAVA 中的对象在创建前,会先从申请的这一大块内存中划分出一部分来给这个对象使用,在 GC 时也只是这个对象所处的内存区域数据清空,标记为空闲而已。JVM 归还内存给操作系统的代价比较大,所以不会轻易进行,JVM 不会在每次 GC 后都进行内存的归还。
虽然代价高,但 JVM 还是提供了这个归还内存的功能。JVM 提供了-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio 两个参数,用于配置这个归还策略。
- MinHeapFreeRatio:表示空闲内存最少保留的比例值,当使用内存越来越大,空闲区域小于比值时,会进行扩容,扩容的上限为 Xmx。如果是在收缩过程,剩余内存达到这个阈值后,就会停止收缩。
- MaxHeapFreeRatio:代表当空闲区域达到该值时,会进行“缩容”,缩容的下限为Xms
不过虽然有这个归还的功能,不过因为这个代价比较昂贵,所以 JVM 在归还的时候,是线性递增归还的,并不是一次全部归还。而且经过实测,这个归还内存的机制,在不同的垃圾回收器,甚至不同的 JDK 版本中还不一样!
一般为了避免 JVM 频繁的扩容缩容,我们会将 Xms 和 Xmx 配置为相等的大小,避免这个扩容的操作。
那是不是只要把 Xms 和 Xmx 配置成一样的大小,这个 JAVA 进程一启动就会占用这个大小的内存呢?并不是的,不会的,哪怕你 Xms6G,启动也只会占用实际写入的内存。这是因为进程在申请内存时,操作系统并不是直接分配物理内存给进程的,而是分配一块虚拟空间,到真正往这块虚拟空间写入数据时才会通过缺页异常(Page Fault)处理机制分配物理内存。可以简单地认为操作系统的内存分配是“惰性”的,分配并不会发生实际的占用,有数据写入时才会发生内存占用。所以哪怕配置了Xms6G,启动后也不会直接占用 6G 内存,实际占用的内存取决于你有没有往这 6G 内存区域中写数据的。
标签:解析,操作系统,占用,归还,内存,JVM,异常现象,空闲 From: https://www.cnblogs.com/wenxuehai/p/18461462