可能很多人都知道Java程序上生产后,运维人员都会设定好JVM的堆大小,而且还是把最大最小设置成一样的值。那究竟是为什么呢?一般而言,Java程序如果你不显示设定该值得话,会自动进行初始化设定。
  -Xmx 的默认值为你当前机器最大内存的 1/4
  -Xms 的默认值为你当前机器最大内存的 1/64 
  显然这样配置的意义是希望JVM可以根据当前运行的环境,动态伸缩堆内存大小。之所以生产上设置成固定大小,网上也是说法不一,很多时候都是使用“防止内存抖动”这样的模糊词语给出解释。但是我相信各位读者也很懵,不知道这个词具体表达什么含义。
  所以接下来我打算用这篇文章来着重解释一下这其中的门道。带大家彻底弄懂设置固定大小堆的底层原理和好处。为了能顺利看懂本文,我假设你们已经具备了一定的操作系统基础知识。
最大堆或最小堆,从字面上理解就是JVM在运行Java程序时,为其分配堆内存空间的上限和下限值。我们把最大和最小堆设置成相同值那意思就是分配了固定大小的内存呗。这样不就省去了动态调整内存(申请和释放)以及频繁的用户态和内核态的切换带来的开销吗?。如下图所示。
  看上去就是这么回事,简单明了。然而当我们尝试去做个模拟实验,事实却并非如此。比如,随便写个Java程序,使用如下命令启动之。并设置好固定大小堆为1G。
  java -Xmx1024m -Xms1024m -jar demo.jar
  然后我们通过查看进程的内存占用时,发现程序并没有占用1G的空间,而是很小的占用。这个实验结果和我们预期的完全不一致。究竟是什么原因呢?
  问题其实出在我们对内存模型的理解上有问题。很多人可能都是像上面图中那样理解程序分配内存的。实际上是不对的,且也更复杂。首先我们要理解一个重要概念,那就是“进程的虚拟地址空间”,我们用户程序通过malloc这个系统调用申请内存,实际上就是申请了一个虚拟的内存,并不是真正的物理内存。大家要注意,这个虚拟的内存就是指“进程的虚拟地址空间”,而不是我们通常理解的Windows下的虚拟内存或Linux下的swap(分区交换)。如下图所示。
  用户程序申请的虚拟内存(虚拟地址空间),也就是通过malloc系统调用,本质就是在进程的虚拟地址空间里分配了一块地址范围而已。32位系统理论上最大4G,每个进程都有自己的虚拟地址空间,都能申请到最大4G内存。但是申请了的内存,如果没有实际使用(写入数据),则操作系统不会给这块虚拟空间分配实际的物理内存。其实原因很简单,物理内存一直属于紧缺资源,所以现代操作系统都设计为由内核程序统一管理,用户程序无权直接干涉。不是说你申请多少就真的给你多少,而是你实际使用多少才会给你多少。
  回到上面那个小实验,你发现启动后程序内存占用很小就是这个原因。尽管JVM已经在你启动时向系统申请了1G的固定堆大小空间。但是由于你这个程序只是一个简单的测试,里面并没有实际的代码操作业务。所以你实际上只用到了很小的物理内存空间。但是如果你的程序真有业务逻辑,随着系统的运行,实际占用物理内存就会越来越多,直到达到申请的上限值1G。运行期间,你的程序同时也会释放一些对象(通过GC),并在适当的时机归还一些物理内存给操作系统。所以占用的物理内存大小,也会动态有所调整。这样操作系统就可以给其他程序使用,提高了内存利用效率。这样的设计也没什么不好的。
  如上图所示,操作系统对内存管理是以页为基本单位的,一个页代表了一个固定大小的地址范围。用户程序给某个变量比如byte[]赋值时,此时该变量对应的进程虚拟地址空间所在的页在物理内存上找不到对应的页映射时,就会触发了一个缺页中断异常,操作系统就会重新将虚拟地址的页映射到物理内存中的页,此时才是真正实现了内存分配,会占用实际的物理内存空间。假如Java程序的GC把这个byte[]变量收回了,也就是不需要占用内存空间了,用户进程的堆管理器会适当的归还一些物理内存给操作系统,以便下次可以给其他任何程序使用。需要注意的是用户程序调用的malloc和free两个系统调用,都是针对用户进程的虚拟地址空间而言的,并不是实际操作物理内存。只有操作系统才拥有对实际物理内存的管理权限。操作系统可以使用有效的各种算法,来独立高效的管理物理内存。这里面的细节,我这里不详细说了,有兴趣的可以去看些操作系统的资料深入了解下。
  然而我们实际的Java程序,配置成固定堆大小后,你会发现,内存占用一旦上去了就下不来了。即使当前程序处于比较空闲的状态下。这又是为什么呢?难道Java的GC没有回收内存?
  其实并不是GC没有回收内存,而是我们这里存在理解问题。GC回收内存并不是指物理内存,而是指当前进程的虚拟内存(虚拟地址空间)。一般而言,回收的虚拟内存并不会立即归还给操作系统,从而操作系统也就无法回收它了。至于何时归还物理内存,这取决于一个叫glibc的堆管理器。它根据一定的策略和算法适当的释放真实的物理内存。否则即便Java程序GC了对象,该对象占用的物理内存也不会立即释放出来。由于这里我们是设置了固定大小的堆空间,实际上GC回收的虚拟内存,也不会被释放归还给操作系统。故Java进程内存占用一旦增长,内存占用几乎都不会再下降了,这样也是出于对象再分配的效率考虑的。这样显然可以避免操作系统反复把进程的虚拟地址页复映射物理内存页(缺页中断异常)操作,导致频繁的用户态和内核态切换造成的性能问题。
 

Java生产环境JVM设置成固定堆大小深层原理的更多相关文章

  1. Java生产环境下性能监控与调优详解视频教程 百度云 网盘

    集数合计:9章Java视频教程详情描述:A0193<Java生产环境下性能监控与调优详解视频教程>软件开发只是第一步,上线后的性能监控与调优才是更为重要的一步本课程将为你讲解如何在生产环境 ...

  2. 设置JVM参数,查看堆大小

    1.在eclipse设置JVM参数     打开eclipse-窗口-首选项-Java-已安装的JRE(对在当前开发环境中运行的java程序皆生效,也就是在eclipse中运行的java程序)编辑当前 ...

  3. jvm详情——6、堆大小设置简单说明

    年轻代的设置很关键JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理内存限制.32位系统下,一般限制在1.5G~2G:64 ...

  4. IT兄弟连 Java语法教程 Java开发环境 JVM、JRE、JDK

    要想开发Java程序,就需要知道什么是JVM.JRE以及JDK.JVM是运行Java程序的核心,JRE是支持Java程序运行的环境,而JDK是Java开发的核心,下面我们分别具体介绍它们以及它们之间的 ...

  5. java生产环境增量发版陷阱【原】

    前言 在生产环境,我们为了降低发版风险,一般都只做增量发布,不做全量发布. 除非项目只有一到两人开发,对时间线和代码脉络结构一清二楚,才可全量发布. 然而增量发布也是有一定隐藏陷阱在里面的,以下就是笔 ...

  6. JAVA虚拟机环境变量设置

    转自: 网络    下载java环境变量设置所需的jdk并安装,下载地址:http://www.oracle.com/technetwork/cn/java/javase/downloads/jdk7 ...

  7. 【Odoo 8开发教程】第二章:Odoo生产环境部署设置

    转载请注明原文地址:https://www.cnblogs.com/cnodoo/p/10792977.html 一:dbfilter 数据库访问规则设置 一个odoo实例可以连接到不同的数据库实例中 ...

  8. java jdk 环境变量设置

    我的电脑点右键,选择“属性”,选择“高级”标签,进入环境变量设置,分别设置如下三个环境变量: 设置JAVA_HOME: 一是为了方便引用,比如,JDK安装在C:\jdk1.6.0目录里,则设置JAVA ...

  9. [Java] Tomcat环境变量设置

    @echo off title Tomcat环境变量设置 color 0a set /p inputTH=D:\Work\024_Tomcat if /i "%inputTH%"= ...

随机推荐

  1. Vulnhub实战-Dockhole_2靶机👻

    Vulnhub实战-Dockhole_2靶机 靶机地址:https://www.vulnhub.com/entry/darkhole-2,740/ 1.描述 hint:让我们不要浪费时间在蛮力上面! ...

  2. 深入理解java中main方法

    理解main方法语法 深入理解main方法: 解释main方法的形式:public static void main(String[] args){} main方法调用者:虚拟机 java虚拟机需要调 ...

  3. JVM:Java中的引用

    JVM:Java中的引用 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 在原来的时候,我们谈到一个类的实例化 Person p = new Person() 在 ...

  4. UltraSoft - Beta - Scrum Meeting 1

    Date: May 17th, 2020. Scrum 情况汇报 进度情况 组员 负责 今日进度 q2l PM.后端 维护Beta阶段文档 Liuzh 前端 增加删除操作按钮 Kkkk 前端 查询增加 ...

  5. FastAPI 学习之路(二十八)使用密码和 Bearer 的简单 OAuth2

    OAuth2 规定在使用(我们打算用的)「password 流程」时,客户端/用户必须将 username 和 password 字段作为表单数据发送.我们看下在我们应该去如何实现呢. 我们写一个登录 ...

  6. 搬运2:早期写的探究printf

    目录: 1. 关于printf格式化输出 2. printf的一般形式 3. 转换说明 4. 格式化输出的意义 5. 转换说明修饰符 6. 修饰符中的标记 7. printf的返回值 ps:共3250 ...

  7. IRCUT作用

    IRCUT组成原理 IRCUT由两层滤光片组成,一片红外截止或吸收滤光片和一片全透光谱滤光片 白天是红外截止滤光片工作,晚上是全透滤光片工作,白天摄像头可以接收到人眼无法识别的红外线,会导致图像与肉眼 ...

  8. 因为一个小小的Integer问题导致阿里一面没过,遗憾!

    面试题:new Integer(112)和Integer.valueOf(112)的区别 面试官考察点猜想 这道题,考察的是对Integer这个对象原理的理解,关于这道题的变体有很多,我们会一一进行分 ...

  9. 双栈排序 牛客网 程序员面试金典 C++ Python

    双栈排序 牛客网 程序员面试金典 C++ Python 题目描述 请编写一个程序,按升序对栈进行排序(即最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中. ...

  10. cf12E Start of the season(构造,,,)

    题意: 给一个偶数N. 构造出一个矩阵. 满足:主对角线上全为0.每一行是0~N-1的一个全排列.矩阵关于主对角线对称. 思路: 觉得是智商题,,,,看完题解后觉得不难,但是我就是没想出来.只想到了前 ...