V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhao1014
V2EX  ›  Java

为什么 Java 堆可以处于物理上不连续的内存空间?

  •  
  •   zhao1014 · 2020-11-07 11:47:11 +08:00 · 5622 次点击
    这是一个创建于 1481 天前的主题,其中的信息可能已经有所发展或是发生改变。

    JVM 为大对象分配内存时需要连续的内存空间,这个内存空间指的是逻辑上连续的内存空间吧?

    在物理内存中对象有可能被拆分开分配到物理不连续的空间中吗?

    如果 Java 堆逻辑连续的空间物理上并不连续,那么垃圾回收的过程中,由复制算法和标记-整理算法得到的空闲内存是不是也是逻辑上连续,物理上不一定连续?

    JVM 运行时数据区是不是对物理内存的一部分的封装?如果是封装,那么逻辑内存与物理内存之间是不是存在一个映射关系?维护这个映射关系的开销不会很大么?为什么不要求物理连续呢?

    第 1 条附言  ·  2020-11-07 12:39:19 +08:00
    感谢各位的回答!去研究操作系统了!
    38 条回复    2020-11-09 11:03:15 +08:00
    hello2060
        1
    hello2060  
       2020-11-07 11:51:23 +08:00 via iPhone
    没研究过这么高深的问题,既然 java 里都是指针,为啥必须连续空间?你一个对象创建出来只是把成员初始化了,这时候成员还不一定指向任何有意义的东西呢,连续空间有啥用
    vk42
        2
    vk42  
       2020-11-07 11:53:42 +08:00   ❤️ 3
    物理地址一般对用户态程序是不可见的,至于虚拟地址到物理地址映射是 OS 维护的事,JVM 没必要多管闲事
    zhao1014
        3
    zhao1014  
    OP
       2020-11-07 11:58:35 +08:00 via Android
    @hello2060 可是 jvm 分配大对象内存要求连续空间,指针指向的是对象所占内存空间的起始地址,也就是第一个字节的地址,再通过对象的类型得知该对象所需空间具体大小然后顺序读取内存
    noe132
        4
    noe132  
       2020-11-07 12:04:18 +08:00   ❤️ 1
    应用程序没有权限直接操作物理内存的,操作的都只是虚拟内存,内存分配是 OS 的工作。至于怎么分配,怎么映射,又多大开销,《操作系统》欢迎你
    zhao1014
        5
    zhao1014  
    OP
       2020-11-07 12:04:36 +08:00 via Android
    @vk42 好的,谢谢你
    zhao1014
        6
    zhao1014  
    OP
       2020-11-07 12:06:12 +08:00 via Android
    @hello2060 好像没能理解你说的意思,说的话跟你没对上,抱歉了
    zhao1014
        7
    zhao1014  
    OP
       2020-11-07 12:07:31 +08:00 via Android
    @noe132 你说的虚拟内存是不是虚拟存储器,本质上是存储在磁盘上,只在需要的时候才会被读取到内存里?
    djFFFFF
        8
    djFFFFF  
       2020-11-07 12:11:11 +08:00
    @zhao1014 虚拟内存是操作系统的知识,对物理内存的抽象和隔离。看一下操作系统的知识就知道了。
    zhao1014
        9
    zhao1014  
    OP
       2020-11-07 12:12:56 +08:00 via Android
    @djFFFFF 好的,谢谢!
    secondwtq
        10
    secondwtq  
       2020-11-07 12:20:39 +08:00 via iPhone   ❤️ 1
    “维护这个映射关系的开销不会很大么”
    这个开销由硬件承担。可能有其他的方案,但是现在主流硬件都针对这个优化,已经形成了路径依赖
    julyclyde
        11
    julyclyde  
       2020-11-07 12:22:41 +08:00   ❤️ 1
    应用程序看不到物理内存的地址空间
    所以讨论在物理地址空间是否连续根本就是个 NULL 问题
    BrettD
        12
    BrettD  
       2020-11-07 12:31:18 +08:00 via iPhone   ❤️ 1
    物理内存映射是操作系统内核管的事情,JVM 只管到逻辑地址
    hello2060
        13
    hello2060  
       2020-11-07 12:31:58 +08:00   ❤️ 1
    @zhao1014 虚拟内存应该只是一个虚拟的东西,他映射到实际存储数据的地方,可以在内存上也可以在磁盘里。

    我前面的回答其实和你问的不是一回事。但是比如说一个飞机对象,包含 100 个乘客对象,也就是飞机对象有 100 个乘客对象的 reference, 这些 reference 可以是在连续空间里的(不管实际存储位置是不是连续的),但是具体每一个乘客对象,完全可以在不连续的位置上(这个更不可能要求实际存储位置是连续的,因为每个乘客对象都是独立的,完全可以在不同的时间地点被创建)再到飞机对象被销毁的时候,乘客对象完全可能还继续存在着,要求物理连续没有意义啊。--- 如果我说的不是你问的,你可以忽略嘿嘿
    zhao1014
        14
    zhao1014  
    OP
       2020-11-07 12:37:24 +08:00 via Android
    @hello2060 哈哈,感谢回答
    mejee
        15
    mejee  
       2020-11-07 12:39:45 +08:00   ❤️ 1
    这里的连续地址,指的是虚拟内存的连续,而不是物理内存的连续。可以理解为虚拟内存统一受 JVM 管理,当分配大对象时候,需要在虚拟内存地址中,找到连续的内存(因为 JVM 管理虚拟内存,所以虚拟内存也是有碎片化使用的)
    misaka19000
        16
    misaka19000  
       2020-11-07 12:43:13 +08:00   ❤️ 2
    学学操作系统吧。。。CPU 有一个部件叫做 MMU,用来管理物理内存页。

    或者看我之前写的文章
    https://www.nosuchfield.com/2018/11/23/Program-compilation-linking-loading-and-running/
    中第四部“装载”中介绍的过程
    Jooooooooo
        17
    Jooooooooo  
       2020-11-07 12:53:51 +08:00   ❤️ 1
    你可以研究下操作系统虚拟内存和物理内存的映射管理.
    zhao1014
        18
    zhao1014  
    OP
       2020-11-07 13:03:08 +08:00 via Android
    @misaka19000 感谢,发现了一个错别字,
    “但其实每个旅行图手中的地图都是不一样的,这个地图保证客人绝对不会找到一个已经被别人使用的房间”旅行图→旅行团
    misaka19000
        19
    misaka19000  
       2020-11-07 13:42:05 +08:00
    @zhao1014 #18 感谢,已经修改
    nightwitch
        20
    nightwitch  
       2020-11-07 14:40:56 +08:00   ❤️ 1
    推荐去看 csapp 的第九章:虚拟内存,关键词:页表,换页和 page fault 。

    对应用程序来说内部见到的地址都是虚拟的,由虚拟地址往物理地址的映射这部分是由操作系统和硬件 mmu+tlb 单元来控制的。
    winglight2016
        21
    winglight2016  
       2020-11-07 17:57:52 +08:00   ❤️ 1
    JVM 里的对象不需要“连续内存空间”,lz 恐怕是以为所有和某个对象相关的数据都放在连续的内存地址中(假设不区分是物理地址还是 JVM 的虚拟地址),但是并不是这样,一个 java Object 只需要声明就好了(我记得只需要 52 个字节),具体的数据是存在堆栈内存中的。

    如果 lz 是专注 java 开发,那么是没必要了解 OS,先研究一下 JVM 吧。
    systemcall
        22
    systemcall  
       2020-11-07 19:02:14 +08:00   ❤️ 1
    应用程序能够访问到的都是“虚拟内存”
    硬盘上的是“页面文件”而不是“虚拟内存”,这 2 个不是一回事
    一般的应用程序是拿不到“物理内存”的地址的。虚拟内存是硬件实现的(现在是 CPU 里面的 MMU 之类的部分来做),一般的应用是看不到这些东西的
    Intel 这几年牙膏倒吸的漏洞,就和 Intel 的 MMU 的缺陷有关系,感兴趣的话可以看看
    另外,现在的 SSD 也是映射过的。NAND 闪存非常容易有坏块,而且还要做均衡磨损、垃圾回收等一系列的事情。操作系统里可以看到的 LBA 和 SSD 内部 NAND   Flash 上的位置不是一一对应的,而是由 SSD 主控负责映射。如果 SSD 有 DRAM 缓存的话,DRAM 用来存放映射表
    不用担心效率问题,因为是专门的硬件来完成。针对内存的一些性能测试,也在一定程度上反映了这套系统的性能
    irytu
        23
    irytu  
       2020-11-07 19:38:33 +08:00 via iPhone
    虚拟内存 物理地址都存在每个进程的页表 page table 里面 安全、好管理、也有利于简化一些内存相关的操作
    zhao1014
        24
    zhao1014  
    OP
       2020-11-07 20:10:56 +08:00 via Android
    @winglight2016 我是看了深入理解 java 虚拟机中垃圾回收方面的东西才有了这种想法,比如说:
    使用复制算法留下的空间是规整的,标记清除算法会产生大量碎片空间,此时 jvm 会维护一个空闲列表来记录空闲内存,在为对象分配内存时选取足够大的连续空间。

    所以才有了对“连续空间”的一些疑问
    zhao1014
        25
    zhao1014  
    OP
       2020-11-07 20:23:54 +08:00
    @systemcall 今天了解了一下关于存储器的知识:“内存与磁盘的动态交换。操作系统将正在使用的部分保存在内存中,其他部分留在磁盘,在需要时再从磁盘中读取,不用的数据再返回给磁盘。”

    我感觉虚拟存储器好像跟大家说的虚拟内存不是一回事,虚拟存储器就是我们使用 Windows 系统时可以设置的虚拟内存,大家提到的“虚拟内存”似乎是对物理内存的一种封装?本质上是一层逻辑?我这样想是对的吗?
    zclHIT
        26
    zclHIT  
       2020-11-07 20:33:05 +08:00   ❤️ 1
    赞一下楼主,之前自己也没有仔细思考过这个问题,看了大家的讨论才恍然大悟,不说了我也去翻《操作系统》去了。。。
    Huelse
        27
    Huelse  
       2020-11-07 21:25:03 +08:00
    段页式内存管理,这个是操作系统维护的,而且有专门的寄存器来映射地址

    这个是操作系统中内存管理的基础
    DOLLOR
        28
    DOLLOR  
       2020-11-07 21:55:20 +08:00   ❤️ 1
    外行人眼中:
    虚拟内存=页面文件

    计算机专业里:
    虚拟内存=物理内存+页面文件(linux 下叫交换区)

    由此可见,所谓的虚拟内存,是对多种存储介质的一层封装。
    对应用程序来说,它们都是在虚拟内存里工作的,并不需要关心自己到底在物理内存里还是页面文件里。至于决定它们要运行在物理内存里还是页面文件里,那时操作系统的工作。

    如果你使用的是 win10 、win8 操作系统,可以打开任务管理器->性能标签->内存,仪表盘下面有个“已提交”,表现为类似 数字 A / 数字 B 形式。这里体现的就是虚拟内存(计算机专业里)的使用状态,数字 A 是已分配给程序的虚拟内存,数字 B 虚拟内存总大小——大致就是你的内存+设置的页面文件大小。

    生活中常常看到有人抱怨,“明明可用内存还有那么多,系统却提示内存不足了”,那是因为他们只留意物理内存还有可用空间,但实际上 windows 是根据虚拟内存(上述的“已提交”)来判断内存不足的,这时候就要通过增大页面文件大小,才能缓解“内存不足”。
    mejee
        29
    mejee  
       2020-11-07 22:09:43 +08:00
    @winglight2016 需要连续空间的。比如数组。或者成员变量比较多的对象,需要的空间也大一些
    zhao1014
        30
    zhao1014  
    OP
       2020-11-07 22:35:21 +08:00 via Android
    @DOLLOR 感谢!
    quanjw
        31
    quanjw  
       2020-11-07 22:52:16 +08:00
    《程序员的自我修养—链接、装载与库》 emmm 其实我也没看完,但是你这个问题 我有看到过解释
    winglight2016
        32
    winglight2016  
       2020-11-08 11:08:57 +08:00
    @mejee 你说的是那种原生类型的数组吧,那种 element 都不是对象,连续存储就比较方便,但是也依然可以分开存储。

    @zhao1014 对象不需要连续存储不代表一定要分开存储,也不代表 JVM 全部不需要连续存储。而且 JVM 维护的空闲内存列表和运行时产生的内存分配略有不同,JVM 是负责 java 程序和 OS 之间的交互层,所以 java 内部的内存管理和 OS 的内存没什么直接的关系。所以,连续内存最大的好处在于提高存储效率,减少读写时间,然而因为 GC 机制,内存碎片化难以避免。
    systemcall
        33
    systemcall  
       2020-11-08 12:15:09 +08:00 via Android   ❤️ 1
    @zhao1014 虚拟内存不仅仅是含有物理内存,还有页面文件,甚至有些地址根本没分配内存
    而且,如果是 32 位的 Windows,进程空间只有 2G,这 2G 也没有都分页,也只有一部分页在物理内存。一些文章介绍的很详细。
    m30102
        34
    m30102  
       2020-11-08 14:00:53 +08:00
    听过 4k 对齐吗? mmu 管理的最小单位就是 4k
    Junzhou
        35
    Junzhou  
       2020-11-08 20:52:21 +08:00
    看来你跟我一样,操作系统的知识都还给老师了。
    xx6412223
        36
    xx6412223  
       2020-11-09 09:46:39 +08:00
    任何进程的内存都可以是物理不连续的,或者说不连续才是正常情况。不是面试想了解 jvm 还是建议先看 Linux 内存管理
    zhao1014
        37
    zhao1014  
    OP
       2020-11-09 10:58:18 +08:00 via Android
    @xx6412223 虽然是为了应对面试,但还是想理解清楚 XD,会继续深入学习的
    xingguang
        38
    xingguang  
       2020-11-09 11:03:15 +08:00
    本来物理内存是否连续和逻辑内存是否连续就没关系的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1056 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 21:41 · PVG 05:41 · LAX 13:41 · JFK 16:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.