java (jvm)程序内存组成(结构) 堆内存 非堆内存
1. java 程序内存组成
参考:https://deepu.tech/memory-management-in-jvm/
(基于 jdk 11)
Heap Memory:堆内存
存放 object 和 动态数据
参数: Xms(初始) Xmx(最大)
分为 Yong 和 Old
Meta Space: 元数据
class loaders 加载的 class 存放这里
之前叫做 永久代 Permanent Generation(PermGen) Space
使用 主机内存,默认情况没上限(可使用参数设置),即使超过内存,会移到虚拟内存
参数: XX:MetaspaceSize(初始) 和 -XX:MaxMetaspaceSize(最大,达到后回收内存)
Thread Stacks: 堆栈内存区域
进程中每个线程有一个堆栈内存。
参数 Xss 设置堆栈内存限制
存储特定于线程的静态数据的地方,包括方法/函数框架和指向对象的指针。
Code Cache:代码缓存
这是 Just In Time (JIT) 编译器存储经常访问的已编译代码块的地方
通常,JVM 必须将字节码解释为本机机器码,而 JIT 编译的代码不需要解释,因为它已经是本机格式并缓存在此处。
Shared Libraries: 共享库
系统只加载一次
2. java 堆
Java 堆是虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一作用就是存放对象实例,几乎所有的对象实例都是在这里分配内存的(不绝对,在虚拟机的优化策略下,也会存在栈上分配、标量替换的情况)。
当类加载器读取了类文件后,需要把类、方法、常量、变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。
Java 堆是 GC 回收的主要区域,因此很多时候也被称为 GC 堆。
从内存回收的角度看,Java 堆还可以被细分为:新生代和老年代;新生代还可以被划分为: Eden Space、From Survivor Space、To Survivor Space。
堆在逻辑上分为三个区域:
java7
java8
在Java7时代
堆分为 新生区(新生区包含伊甸园区和幸存区,幸存区又包含幸存者0区和幸存者1区。此外,幸存者0区又称为From区,幸存者1区又称为To区,From区和To区并不是固定的,复制之后交互,谁空谁是To),养老区和永久代;
在Java8中
永久代已经被移除,被一个称为元空间的区域所取代。元空间的本质和永久代类似。
元空间与永久代之间最大的区别在于:
永久代使用的JVM的堆内存(但是逻辑上是非堆的),但是java8以后的元空间并不在虚拟机中而是使用本机物理内存(所以在上图中,我用虚线表示)。
永久代:
是一个常驻内存的区域,用于存放JDK自身所携带的Class,Interface的元数据,即存储的是运行环境必须的类信息,被转载进此区域的数据是不会被垃圾回收的,只有关闭JVM才会释放此区域所占用的内存空间。
元空间:
取代永久代,不在Java虚拟机的堆中实现,而是使用本机物理内存实现。默认情况下元空间大小仅受本地内存限制。类的元数据放入native memory,字符串常量在Java堆中(运行时常量和基本类型常量在元空间——方法区)
PS:jdk1.8,jvm把字符串常量池移到了堆内存里。此时方法区=元空间
堆之所以要分区
是因为 Java程序中不同对象的生命周期不同,70%~99%对象都是临时对象,这类对象在新生区“朝生夕死”。如果没有分区,GC时搜集垃圾需要对整个堆内存进行扫描;分区后,回收这些“朝生夕死”的对象,只需要在小范围的区域中(新生区)搜集垃圾。所以,分区的唯一理由就是为了优化GC性能。
共 0 条评论