Jvm 内存模型之二撩裙边/方法执行
2017-02-27 14:08
423 查看
在 Jvm 内存模型之初探花裙中我们已经大致了解了jvm内存的逻辑结构以及各个存储区域的作用。然而在迈向资深老司机的道路上,浅尝辄止可不行,撩起花裙加深印象才符合老司机的风格。接下来咱们可以一个java类文件来研究类执行过程中其各个部分如何与jvm内存逻辑模型的各个部分映射的。
这个类实现了Runnable 接口,共包含三个属性、三个方法、一个静态代码块。那么就从作为入口的main方法开始逐步分析。
第一步:在执行main方法之前,需要先载入HelloWorld的class文件
jvm会先去方法区查找是否存在HelloWorld类信息。
当jvm未查找到HelloWorld类的相关信息时会去classpath下找到HelloWorld的class文件并装载,将class文件的字节流结构化到方法区的存储空间中。
装载完成后,在链接时将class包含字面量”a”,”aa”,888,”hello world on load”,”hello world thread end”,”main thread end” 并将这些字面量存入常量池。可以注意到,程序中出现了多处”a”,”aa”,888等字面量,但是在常量池中每个字面量仅会存储一份。可简单理解为常量不重复。然后将代码中的字面量替换为引用。
链接完成后,类初始化过程开始,并执行class包含静态代码。调用系统函数输出”hello world on load”。至此class载入及初始化过程完成。
第二步:jvm调用main方法
jvm启动一个线程调用HelloWorld,即执行主线程。
根据main方法中的局部变量,例如int aInt 、Thread thread、返回值信息等生成栈帧并将栈帧压入虚拟机栈。
修改主线程的程序计数器数值,记录当前执行的字节码代码行号。(伴随着代码执行同步修改的,下文中不再说明)
调用Thread的构造方法,生成该方法的栈帧压栈。
调用HelloWorld的构造方法(由于HelloWorld没有构造方法,此处调用默认构造方法),生成此构造方法的栈帧并压入虚拟机栈(为了简化理解此处忽略了调用父类的构造方法过程,姑且任务此时处于栈顶)。
向堆内存申请一块内存区域存储HelloWorld的实例,实例中至少包含对于其他对象的引用(例如数组引用),类信息等。
执行完HelloWorld的构造方法后返回HelloWorld对象实例的引用, HelloWorld的栈构造方法的栈帧弹栈,并继续执行当前栈顶的Thread的构造方法。
Thread构造方法执行完毕,将此线程的对象存放在Thread thread的引用中。
将Thread的start方法入栈,执行start方法,启动hello world 线程。主线程程序计数器记录当前主线程代码执行位置。
若cpu切换至hello world 线程时,新增一个程序计数器,新增虚拟机栈,将run方法压栈,开始执行run方法。
若cpu切换回主线程,则主线程根据程序计数器记录的位置继续执行。
当主线程虚拟机栈的方法全部弹出时主线程执行完毕,hello world 线程同理。至此,方法执行完毕。
其实jvm执行类的细节远比上文描述中复杂,本文的目的仅仅是梳理类执行的过程与jvm内存模型中的各个部分关系。若需详细了解其执行细节,还需要进一步梳理。
小弟原创,欢迎拍砖
public class HelloWorld implements Runnable { private String[] aHeapStringArray = {"a"+"a"}; private static String aStaticString = "aa"; private final String aFinalString = "aa"; static { System.out.println("hello world on load"); } public void aVoidMethod(){ int aInt = 888; System.out.println("hello world thread end"); } @Override public void run() { aVoidMethod(); } public static void main(String[] agr) throws InterruptedException { int aInt = 888; Thread thread = new Thread(new HelloWorld()); thread.start(); Thread.sleep(aInt); System.out.println("main thread end"); } }
这个类实现了Runnable 接口,共包含三个属性、三个方法、一个静态代码块。那么就从作为入口的main方法开始逐步分析。
第一步:在执行main方法之前,需要先载入HelloWorld的class文件
jvm会先去方法区查找是否存在HelloWorld类信息。
当jvm未查找到HelloWorld类的相关信息时会去classpath下找到HelloWorld的class文件并装载,将class文件的字节流结构化到方法区的存储空间中。
装载完成后,在链接时将class包含字面量”a”,”aa”,888,”hello world on load”,”hello world thread end”,”main thread end” 并将这些字面量存入常量池。可以注意到,程序中出现了多处”a”,”aa”,888等字面量,但是在常量池中每个字面量仅会存储一份。可简单理解为常量不重复。然后将代码中的字面量替换为引用。
链接完成后,类初始化过程开始,并执行class包含静态代码。调用系统函数输出”hello world on load”。至此class载入及初始化过程完成。
第二步:jvm调用main方法
jvm启动一个线程调用HelloWorld,即执行主线程。
根据main方法中的局部变量,例如int aInt 、Thread thread、返回值信息等生成栈帧并将栈帧压入虚拟机栈。
修改主线程的程序计数器数值,记录当前执行的字节码代码行号。(伴随着代码执行同步修改的,下文中不再说明)
调用Thread的构造方法,生成该方法的栈帧压栈。
调用HelloWorld的构造方法(由于HelloWorld没有构造方法,此处调用默认构造方法),生成此构造方法的栈帧并压入虚拟机栈(为了简化理解此处忽略了调用父类的构造方法过程,姑且任务此时处于栈顶)。
向堆内存申请一块内存区域存储HelloWorld的实例,实例中至少包含对于其他对象的引用(例如数组引用),类信息等。
执行完HelloWorld的构造方法后返回HelloWorld对象实例的引用, HelloWorld的栈构造方法的栈帧弹栈,并继续执行当前栈顶的Thread的构造方法。
Thread构造方法执行完毕,将此线程的对象存放在Thread thread的引用中。
将Thread的start方法入栈,执行start方法,启动hello world 线程。主线程程序计数器记录当前主线程代码执行位置。
若cpu切换至hello world 线程时,新增一个程序计数器,新增虚拟机栈,将run方法压栈,开始执行run方法。
若cpu切换回主线程,则主线程根据程序计数器记录的位置继续执行。
当主线程虚拟机栈的方法全部弹出时主线程执行完毕,hello world 线程同理。至此,方法执行完毕。
其实jvm执行类的细节远比上文描述中复杂,本文的目的仅仅是梳理类执行的过程与jvm内存模型中的各个部分关系。若需详细了解其执行细节,还需要进一步梳理。
小弟原创,欢迎拍砖
相关文章推荐
- JVM内存模型之方法区
- JVM-由常量池 运行时常量池 String intern方法想到的(三)之String内存模型
- jvm内存模型中-栈,方法区,程序计数器是线程安全的
- tomcat占用jvm内存数修改方法及监控方法
- JVM内存模型以及垃圾回收
- JVM内存模型以及垃圾回收
- 【转】JVM内存模型以及垃圾回收
- JVM内存模型以及垃圾回收
- JVM内存模型基础知识
- JVM内存模型以及垃圾回收
- OO 中的继承分析:主要分析在编译和运行过程中 子类、父类 的字段和方法以及实例化时候在内存中分配 和 执行的先后,以及两个原则
- JVM学习笔记(1、 基本结构;2、Java代码编译和执行的整个过程3、内存管理和垃圾回收 4、 内存调优 )
- JVM内存模型以及垃圾收集策略解析
- JVM内存模型及垃圾收集策略解析
- 转:JVM内存模型以及垃圾收集策略解析
- WinFrom调用执行Clementine模型的方法
- JVM内存模型
- tomcat占用jvm内存数修改方法及监控方法
- JVM内存模型以及垃圾收集策略解析
- JVM内存模型以及垃圾回收