JVM运行时内存划分

一、运行时内存数据划分

1、总览

JDK1.8之前的运行时内存划分(蓝色线程私有,红色线程共享):
JVM运行时内存划分
JDK1.8的运行时内存划分:
JVM运行时内存划分
可以看出JDK1.8的时候,变化还是有的;其中:
1、方法区从JVM中取出。
2、方法区移入到本地内存,更名为元数据区。(方法区也称永久代,主要是因为永久代和元数据区都是方法区的一种实现)。
3、在JDK1.7之前,字符串常量池是存在于方法区内的,JDK1.7之后字符串常量池就从方法区内取出,存放在堆空间里面了。

2、本地方法栈和程序计数器

①、本地方法栈(线程私有)
在《深入理解JAVA虚拟机》中有这么一句话:“Java虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。
对于这句话的,我的理解是:Java虚拟机不仅使用Java方法,也使用其他语言的方法(C、C++…),比如CAS操作中的UNSAFE类:提供了硬件级别的原子操作,而这个类里面的具体方法使用native关键字修饰,表明这是一个其他语言编写的方法,这些方法是要放到本地方法栈里面运行的。
②、程序计数器(私有)
记录当前线程运行到的字节码的行号,如果执行的是native方法,那么这个计数器值则为空(Undefined)。

3、Java虚拟机栈(私有)

JVM运行时内存划分
栈的单位是栈帧,线程执行到某个方法的时候会创建一个栈帧来存放这个方法的信息,具体有局部变量表、操作数栈,动态链接、方法出口信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
①、局部变量表:用于存放方法参数和方法内部定义的局部变量(有八大基础数据类型和引用类型returnAddress[已被异常表替代])
②、操作数栈:主要用做算法运算和调用其他方法(当做加法运算时,会将操作数栈中最接近栈顶的两个元素出栈相加后,对结果进行入栈,完成了一次加法运算)
③、动态连接:包含一个指向运行时常量池中该栈帧所属方法的引用。
④、方法返回地址:方法执行后,有正常退出和异常退出两种方式,前者可以用PC计数器的值作为返回地址,后者只能通过异常处理器表来确定下一条应该执行的地址。
⑤、附加信息


  • 说到动态连接,就必须得说一说方法的调用。在JVM中,存在两种调用方法:解析和分派;其中分派包含静态分派和动态分派。

  • 什么是解析?

    • 书中是这么解释的:“在类加载的解析阶段,会将其中一部分符号引用转换为直接引用,这种解析能够成立的前提是:方法在程序真正运行之前就存在一个可确定的调用版本,并且这个调用版本在运行期是不可以改变的。换句话说,调用目标在程序代码写好、编译器进行编译的时候就必须确定下来。这类方法调用称为解析。…符合解析的方法包括静态方法(static)和私有方法(private),因为前者在类加载进内存的时候就会确定,后者则在外部不可被访问”。
  • 什么是符号引用转换为直接引用?

    • 比如说com.test.Person类中引用了com.test.Animal类,在编译阶段,Person类并不知道Animal的实际内存地址,因此只能用com.test.Animal这个符号来代表Animal真实的内存地址。在解析阶段,JVM可以通过解析该符号引用,来确定com.test.Animal类的真实内存地址(如果该类未被加载过,则先加载)。 一句话概括就是:符号引用:引用变量指向某个特定字符;直接引用:引用变量直接指向内存地址。
  • 什么是静态分派?

    • 所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。比如Human man = new Man();,Human称为变量的静态类型,Man称为变量的实际类型。静态分派的典型应用是方法重载。并且和解析一样都发生在编译阶段。
  • 什么是动态分派

    • 在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
  • 总结:类的方法调用分为解析和静态分派、动态分派,前两者发生在类的编译阶段,后者发生在类的运行阶段。

4、堆(共享)

这里我直接引用Bruce128的图:
JVM运行时内存划分

堆是JVM内存占用最大,管理最复杂的一个区域。其唯一的用途就是存放对象实例:所有的对象实例及数组都在对上进行分配。堆有自己进一步的内存分块划分,按照GC分代收集角度的划分请参见上图。
老年代 : 三分之二的堆空间
年轻代 : 三分之一的堆空间
eden区: 8/10 的年轻代空间
survivor0 : 1/10 的年轻代空间
survivor1 : 1/10 的年轻代空间
命令行上执行如下命令,查看所有默认的jvm参数
java -XX:+PrintFlagsFinal -version

原文出处:https://blog.****.net/bruce128/article/details/79357870

  • 新生代和老生代的转换规则:
    新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。 当一个对象被判定为 “死亡” 的时候,GC 就有责任来回收掉这部分对象的内存空间。新生代是 GC 收集垃圾的频繁区域。 当对象在 Eden 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳,则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域中,然后清理所使用过的 Eden 以及 Survivor 区域,并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。 但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。

原文出处:https://blog.****.net/sted_zxz/article/details/72476673

5、元数据区(共享)

①、作用:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。其中存在class常量池和运行时常量池两块区域
②、字符串常量池、class常量池和运行时常量池的理解:

i、字符串常量池:
i-i、在JDK6.0及之前版本中,String Pool里放的都是字符串常量;
i-ii、在JDK7.0中,由于String#intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用。
ii、class常量池:
ii-i、我们写的每一个Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References);
ii-ii、每个class文件都有一个class常量池。
iii、运行时常量池:
iii-i、运行时常量池存在于内存中,也就是class常量池被加载到内存之后的版本,不同之处是:它的字面量可以动态的添加(
String#intern()),符号引用可以被解析为直接引用
iii-ii、JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。

原文出处:https://blog.****.net/zm13007310400/article/details/77534349

最后的一个问题:为什么要费时费力的把方法区从虚拟机内存里移出来放到主机内存里面?
借助https://blog.****.net/bruce128/article/details/79357870讨论区网友qq_36146308的回答:
原本在jdk1.7中的永久代,也可以理解为方法区,存在于JVM分配内存中。在jdk1.8该区域搬到了jvm分配内存外的机器内存中了,并 改名为元数据区。举个实际例子来讲,在jdk1.7中,你的电脑是2g内存,分配给一个java程序128m内存,其中32m是永久代内存,这部分内存存放了你的类信息和HelloWorld,静态变量和常量。 而在jdk1.8中,同样的2g内存,java程序还是128m内存,但是元数据区的内存空间是从(2g-128m)剩余的空间中分配。这个设计的好处在于,避免了类信息或常量过多使永久代增大,与jvm中其他对象保存的堆空间竞争内存

6、JVM的运行时内存划分图

JVM运行时内存划分
原文参考:https://blog.****.net/bruce128/article/details/79357870
https://www.cnblogs.com/dolphin0520/p/3613043.html
https://blog.****.net/dd864140130/article/details/49817357
http://wiki.jikexueyuan.com/project/java-vm/polymorphism.html
https://blog.****.net/sted_zxz/article/details/72476673
https://blog.****.net/wangbiao007/article/details/78545189
https://blog.****.net/zm13007310400/article/details/77534349