java方法体入栈_深入Java虚拟机笔记--体系结构及其堆,栈,方法区

平时我们用的大多是Sun(现已被Oracle收购)JDK提供的JVM,但是JVM本身是一个规范,所以可以有多种实现,除了Hotspot外,还有诸如Oracle的JRockit、IBM的J9也都是非常有名的JVM。

一,JVM 结构

0818b9ca8b590ca3270a3433284dd417.png

从上图可以看出,JVM 主要由类加载子系统, 运行时数据区(内存空间),执行引擎,以及本地方法接口等组成。其中运行数据区又包括 方法区,堆,Java栈,本地方法栈,PC寄存器。

在途中还可以看到,堆,方法区是所有线程共享,而Java栈,本地方法栈,PC寄存器是线程私有的。

1,类加载子系统 Class Loader

负责加载编译好的 .class 文件,并装入内存,使JVM 可以实例化或者以其他方法使用加载后的类。JVM的类加载子系统支持在运行时的动态加载。

类加载子系统 工作原理

类加载分为装载、链接、初始化三步。

装载

通过类的全限定名和ClassLoader加载类,主要是将指定的.class文件加载至JVM。当类被加载以后,在JVM内部就以“类的全限定名+ClassLoader实例ID”来标明类。

在内存中,ClassLoader实例和类的实例都位于堆中,类的其他信息位于方法区中。

装载过程采用了一种“双亲委派模型”的方式。

链接

链接的任务是把二进制的类型信息合并到JVM 运行状态中去。

初始化

初始化类中的静态变量,并执行类中的static 代码,构造函数。

JVM规范严格定义了何时需要对类进行初始化:

a、通过new关键字、反射、clone、反序列化机制实例化对象时

b、调用类的静态方法时。

c、使用类的静态字段或对其赋值时。

d、通过反射调用类的方法时。

e、初始化该类的子类时(初始化子类前其父类必须已经被初始化)。

f、JVM启动时被标记为启动类的类(简单理解为具有main方法的类)。

2,运行数据区(内存)

2.1Java 栈

Java栈是由许多栈帧组成的,

一个栈帧对应一个方法调用。当线程调用一个Java 方法时,虚拟机压入一个新的栈帧到该线程的Java栈中(Java栈是线程私有的),当该方法返回时,这个栈帧被弹出并抛弃。

Java栈的主要任务是存储方法参数,局部变量,中间运算结果,并提供部分其他模块工作需要的数据。

2.2本地方法栈

在Sun JDK中,本地方法栈和Java栈是同一个。

2.3方法区

线程共享。

方法区存储类元数据、常量、静态变量。方法区中对于每个类存储了以下数据:

a.类及其父类的全限定名(java.lang.Object没有父类)

b.类的类型(Class or Interface)

c.访问修饰符(public, abstract, final)

d.实现的接口的全限定名的列表

e.常量池

f.字段信息

g.方法信息

h.静态变量

i.ClassLoader引用

j.Class引用

Hotspot 将其称为永久代(Permanent Generation),但是这部分区域也需要GC。运行时常量池(Runtime Constant Pool)是方法区的一部分,用来存放符号引用,常量,直接引用等。JDK 1.7开始,字符串常量池从方法区中移出。

2.4堆 Heap

堆用于存储几乎所有的对象实例以及数组值。是线程共享的。他也被称为”GC堆”,因为堆是垃圾收集器的主要管理区域。

在堆内存的管理上,Sun JDK从1.2版本开始引入了分代管理的方式。

分为年轻代(Young Generation),老年代(Old Generation)。这种分代方式大大改善了垃圾收集的效率。

新生代:每次GC 时,这块区域的对象都有大批死去,因此称为新生代。新生代可以继续分为伊甸园(Eden)和两个存活区(survivor space) 。

老年代:每次GC 时,这块区域的对象存活率较高。

在这里看到一个形象的图片

0818b9ca8b590ca3270a3433284dd417.png

最后堆中的需要等GC 来清理,栈中的直接销毁了。

3,执行引擎

执行引擎是 JVM 执行Java字节码的核心。

二,堆,栈,方法区

上面看了JVM的体系结构,现在对比了解JVM 内存中的3个区:堆heap,栈 stack,方法区 method。

堆区:

1. 全部是对象,每个对象都包含与之对应的 class 信息。(class的目的是得到操作指令)。不存放基本类型和数据引用,只存放对象本身.

栈区:

2. Java栈是由许多栈帧组成的。每个线程包含一个栈区。

3. 栈中只保存基础数据类型的对象和自定义对象的引用(对象都放在堆区)

4. 每个栈中的数据都是私有的,其他栈不能访问。

5. 栈分为3 个部分:基本类型变量区,执行环境上下文,操作指令区(存放操作指令)

方法区:

1. 线程共享,方法区包含所有的 class 和 static 变量

2. 方法区中包含的都是在整个程序中永远唯一的元素,如 class ,static 变量。

为了更清楚地搞明白发生在运行时数据区里的黑幕,我们来准备2个非常简单的小程序。

public class Demo { //运行时,JVM 把demo 的信息都放入方法区

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

//test1 是引用,所以放到栈区,Sample 是自定义的对象,放到堆中

Sample s1=new Sample("测试1");

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

s1.printName();

s2.printName();

}

}

class Sample{ //运行时,JVM 把sample 的信息都放入方法区

/* 范例名称 */

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

private String name;

/* 构造方法 */

public Sample(String name){

this.name=name;

}

/* 输出 */

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

System.out.println(name);

}

}

发出指令:java Demo ,系统收到指令,启动一个Java 虚拟机进程,这个进程首先从classpath中找到Demo.class 文件,读取文件中的二进制数据,然后把Demo 类的类信息存放到运行时数据区。这一过程就是Demo 类的加载过程。

接着,Java 虚拟机定位到方法区中 Demo类的Main() 方法,开始执行它的指令:

第一条:Sample test1=new Sample("测试1");

就是让虚拟机创建一个Sample 实例,并且使用 test1 引用这个实例。貌似小case一桩哦,就让我们来跟踪一下Java虚拟机,看看它究竟是怎么来执行这个任务的:

1、

Java虚拟机一看,不就是建立一个Sample实例吗,简单,于是就直奔方法区而去,先找到Sample类的类型信息再说。结果呢,嘿嘿,没找到@@,这会儿的方法区里还没有Sample类呢。可Java虚拟机也不是一根筋的笨蛋,于是,它发扬“自己动手,丰衣足食”的作风,立马加载了Sample类,把Sample类的类型信息存放在方法区里。

2、 好啦,资料找到了,下面就开始干活啦。Java虚拟机做的第一件事情就是在堆区中为一个新的Sample实例分配内存,

这个Sample实例持有着指向方法区的Sample类的类型信息的引用。这里所说的引用,实际上指的是Sample类的类型信息在方法区中的内存地址,其实,就是有点类似于C语言里的指针啦~~,而这个地址呢,就存放了在Sample实例的数据区里。

3、

在JAVA虚拟机进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。OK,原理讲完了,就让我们来继续我们的跟踪行动!位于“=”前的Test1是一个在main()方法中定义的变量,可见,它是一个局部变量,因此,它被会添加到了执行* main() 方法的主线程*的 JAVA 方法调用栈中。而“=”将把这个test1变量指向堆区中的Sample实例,也就是说,它持有指向Sample实例的引用。

OK,到这里为止呢,JAVA虚拟机就完成了这个简单语句的执行任务。参考我们的行动向导图,我们终于初步摸清了JAVA虚拟机的一点点底细了

接下来,JAVA虚拟机将继续执行后续指令,在堆区里继续创建另一个Sample实例,然后依次执行它们的printName()方法。当JAVA虚拟机执行test1.printName()方法时,JAVA虚拟机根据局部变量test1持有的引用,定位到堆区中的Sample实例,再根据Sample实例持有的引用,定位到方法去中Sample类的类型信息,从而获得printName()方法的字节码,接着执行printName()方法包含的指令.——-printName 方法在方法区???不太理解(类的所有信息都存储在方法区中。)

0818b9ca8b590ca3270a3433284dd417.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值