内存那点事

背景

前几天,我的知识星球(有兴趣的欢迎加入https://t.zsxq.com/EUn6IIE)的一个圈友咨询我一个问题:他已经将java启动参数设置为-Xms1g -Xmx1g,启动后,他动过top命令观察,发现其占用的内存远远不到1g。

如下这么简单的一个代码:


public class Main {

    public static void main(String[] args)throws Exception {

        System.in.read();//防止程序退出

    }

}

其占用的内存却只有这些(使用top -pid命令查看)

image

这个问题呢,当时也是让我脑袋一愣,难道这是JVM做了什么特殊操作吗?读到这里的你也不放思考一下为什么。

虚拟地址空间

圈友这个问题引发了我更远的思考,于是不得已将大学毕业还给老师的知识重新拿出来分析。

如果你大学里学的东西还没还给老师,你应该还知道,咱们的程序进程,是运行在一个虚拟地址空间里。

它的寻址过程如下图:

image

cpu在读取某个地址时,其地址只是一个虚拟地址,由MMU设备将虚拟地址转换成实际的物理内存地址后,在进行读取操作。

你或许会好奇为啥使用虚拟地址,但当你看到如下好处后,你肯定会赞叹其牛逼的设计。

1、进程间相互隔离

如果没有虚拟地址,每个进程直接对物理内存进行操作,势必会存在各个进程相互影响而无法正常进行。

有了虚拟地址,不同进程的虚拟地址,可以映射到不同得物理地址,相互之间无干扰。

2、方便内存共享

上一个点我们说到了不同进程的虚拟地址,可以映射到不同的物理地址。其实不同进程的虚拟地址也可以映射到相同的物理地址以实现内存共享。

比如每个操作系统的进程,都会需要跟内核程序打交道。有了内存共享,多个进程间就可以共用内核程序,而不需要为每一个进程在物理内存里加载一份内核程序。

再比如动态链接库,也是通过共享内存实现物理内存中只加载一份的。

3、简化编译时的链接

由于进程使用的是虚拟地址,以32位机器为例,每个进程的访问范围都是0~4g的地址空间。当我们在编译源代码时,就可以为程序里的变量、方法分配这个虚拟地址,链接的时候就可以直接用这个虚拟地址实现链接。(如果你不理解什么是链接,你可以简单地理解为:将源代码里的方法调用的地方替换为该方法的内存地址)

如果没有虚拟地址,程序里的变量、方法的地址,只能是在程序被加载到内存时才能分配,链接也就无法在编译期进行。

如下这是一份Linux下进程所在虚拟地址空间里,不同区域的用途分配图:

image

所有linux下的进程都是这种固定的格式,每个区域都有固定的起始地址。JVM进程,本质上就是一个用c++写的普通进程,其地址空间布局也是这样,只不过它会对比如上边的运行时堆,进行更细的划分。

至于操作系统和硬件是如何管理虚拟地址空间到物理内存的映射,本篇就不做设计了,感兴趣的朋友可以自行阅读操作系统或者计算机系统的书籍。

进程使用内存

我们的进程,通过虚拟地址来操作内存。那当我们的进程在申请内存空间时,返回的内存地址自然也是虚拟内存地址。但我们申请的这块基于虚拟内存地址的内存,是否有对应的物理内存的分配呢?

在这里我们不妨做个简单地实验。我们写一段C程序,调用malloc申请一个1G的内存,然后使用top命令查看此进程所占用的内存空间:


#include <stdio.h>

#include <sys/malloc.h>

#include "unistd.h"

int main(int argc, const char * argv[]) {

    printf("pid is %d \n", getpid());

    long size = 1024*1024*1024;

    char *p = (char *)malloc(sizeof(char) * size);

    getchar();//不让程序退出

    return 0;

}

编译运行,然后根据打印出的进程id,使用top -pid XXX命令查看内存占用情况。你会发现其内存使用远没有达到1G。换句话说,操作系统并没有马上为我们申请的这个虚拟地址空间分配对应大小的物理内存。

何时系统才会给我们的虚拟地址空间分配对应的物理内存呢?

我们不妨换个角度理解我们计算机中的物理内存:物理内存是虚拟地址空间内存的高速缓存。

在我们使用虚拟地址空间时,如果没有对应的物理内存,就会出现我们常见的缓存不命中的情况。专业术语叫缺页异常。这时内核的缺页异常处理程序,将会帮助我们分配物理内存,如果物理内存不足,它将会选择一个物理内存页作为牺牲,写回磁盘上,这也就是我们所说的交换分区。

到这里我们可以看出,我们进程中所使用的内存大小,与真正占用物理内存大小,没有绝对的相等关系。进程申请的内存还没有被使用时,会出现物理内存小于进程内存的情况;进程内存对应的物理内存被写回到交换分区时,也会出现进程内存大于实际物理内存的情况。

总结

到这里,Java进程启动时,其占用的内存小于Xms指定的内存大小,就可以说清楚了。它不是JVM的原因,而是操作系统管理进程内存空间的方式上的原因。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269