1 JVM内存模型的个人理解

1 java虚拟机内存模型

1.1 程序计数器

如果当前执行的是Java方法,则指示当前字节码指令的地址,如果执行的是本地本地方法,则值为Undefined。每个线程都有自己的程序计数器,也就保证了线程切换的过程中, 该线程执行的程序能恢复到正确的执行位置。

1.2 本地方法栈

一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。本地方栈存放的方法是本地方法,其他的和虚拟机栈基本相同。接下来我们介绍虚拟机栈。

1.3 虚拟机栈

虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

我们分析一个方法执行需要哪些信息?

我们需要保存局部变量,对象引用,方法调用,方法返回地址

局部变量表:局部变量和对象引用。局部变量包括各种基本的数据类型。

操作数栈:局部变量表是无法操作变量的,变量要进入操作数栈进行各种操作。就像编译原理中的对指令进行操作的栈。

动态链接:函数调用,我们都知道动态链接过程,就是在方法在运行的时候如果调用了其他的方法,通过动态链接把这两个方法链接到一块。

方法出口:方法的返回地址

1.4 堆

虚拟机栈中局部变量表存放了指向对象的引用,那么这个对象实例具体保存在哪呢?答案是保存在堆中。

1.4.1对象的实例

(1)对象头

(2)对象的实例数据

程序代码中各种所定义的各种类型字段内容。

(3)对齐数据的补充

HotSpot VM自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,如果不是8字节的整数倍就要填充成8字节的整数倍。

1.5 方法区

栈和堆都是运行时的,那么一些静态信息肯定也要保存,保存这些静态信息的区域就是方法区。

方法区保存虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。运行时常量池也是方法区的一部分。

1.5.1 运行时常量池

每一个Class文件中,都维护着一个常量池(这个保存在类文件里面,不要与方法区的运行时常量池搞混),里面存放着编译时期生成的各种字面值和符号引用;这个常量池的内容,在类加载的时候,被复制到方法区的运行时常量池 ;

字面值:就是像string, 基本数据类型,以及它们的包装类的值,以及final修饰的变量,简单说就是在编译期间,就可以确定下来的值;

符号引用:不同于我们常说的引用,它们是对类型,域和方法的引用,类似于面向过程语言使用的前期绑定,对方法调用产生的引用。例如,在编译时, Java类并不知道所引用的类的实际地址, 因此只能使用符号引用来代替. 比如org.simple.People类引用了org.simple.Language类, 在编译时People类并不知道Language类的实际内存地址, 因此只能使用符号org.simple.Language来表示Language类的地址。

 1.5.2 字段信息:字段信息存放类中声明的每一个字段的信息,包括字段的名、类型、修饰符。

 1.5.3 方法信息:类中声明的每一个方法的信息,包括方法名、返回值类型、参数类型、修饰符、异常、方法的字节码。

(在编译的时候,就已经将方法的局部变量、操作数栈大小等确定并存放在字节码中,在装载的时候,随着类一起装入方法区。)

 1.5.4 静态变量:类变量,类的所有实例都共享,我们只需知道,在方法区有个静态区,静态区专门存放静态变量和静态块。

 1.5.5 到类classloader的引用:到该类的类装载器的引用。

 1.5.6 到类class 的引用:虚拟机为每一个被装载的类型创建一个class实例,用来代表这个被装载的类。 

2 实例说明

package Chapter2;

public class ExampleImpl3

// 运行时, jvm 把appmain的信息都放入方法区

{

       public static void main(String[] args) // main 方法本身放入方法区。

       {

              Sample test1 = new Sample(" 测试1 "); // test1是引用,所以放到栈区里,

                                                                           // Sample是自定义对象应该放到堆里面

              Sample test2 = new Sample(" 测试2 ");

              test1.printName(); //运行时,放到虚拟栈

              test2.printName();

       }

}

class Sample // 运行时, jvm 把appmain的信息都放入方法区

{

       private String name; // new Sample实例后, name 引用放入栈区里, name 对象放入堆里

       /** 构造方法 */

       public Sample(String name)

       {

              this.name = name;

       }

       /** 输出 */

       public void printName() // print方法本身放入 方法区里。

       {

              System.out.println(name);

       }

}


3 总结:

2.1 静态信息(非运行)

(1)静态的信息放到方法区中。例如类的版本、字段信息、方法信息、到类实例的引用(由此判断实例对象的类型)

2.2 动态信息(运行)

(2)对象的实例放到堆中。例如,对象头,对象实例数据(对象中各种类型的字段的的值)。

(3)方法的运行内存是虚拟机栈。例如,方法中的变量以及对象的引用,操作数栈,方法出口等信息。

发布了146 篇原创文章 · 获赞 91 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/A1342772/article/details/90520991