JVM深入学习笔记七-JVM执行

运行时栈帧

栈帧( 局部变量表(基本数据类型,对象引用,returnAddress类型 ), 操作数栈 , 动态链接 , 方法返回地址 )

局部变量表

以Slot为最小的单位。用来存放32位以内的数据类型。像long,double需要连续的两个Slot

局部变量从0位开始会每一个index对应一个变量的值0一般是this

Slot可以改变,当程序计数器指针超出Slot的作用域(用{}指定)的时候则这个Slot就能够复制给其他人了。

垃圾回收的时候Slot会影响GC的行为,比如:

public class TestSlot {
	public static void main(String[] args) {
		{
			byte[] placeholder = new byte[64*1024];
		}
		System.gc();
	}
}

输出:

[Full GC 345K->212K(15872K), 0.0051430 secs]

改为:

public static void main(String[] args) {
		{
			byte[] placeholder = new byte[64*1024];
		}
		int a = 1;
		System.gc();
	}

输出:

[Full GC 345K->148K(15872K), 0.0067385 secs]

或者改为:

public static void main(String[] args) {
		{
			byte[] placeholder = new byte[64*1024];
			placeholder = null;
		}
		System.gc();
	}

输出跟上面一样。

这个是因为,加入了int a = 1;之后 placeholder占用的Slot被a用了,就能被回收了。

null也是因为Slot为空了。但是null这种编程办法并不提倡,因JIT的时候这行代码会被去掉。通常我们提倡使用作用于({})来控制Slot以促进回收。

操作数栈

后入先出栈,方法执行开始的时候为空,当需要的时候再入栈出栈,比如做数学运算,或者方法调用的传参都是用操作数栈进行的。

动态链接

栈帧持有一个指向运行时常量池中该帧所属方法的引用,Class文件常量池中有大量的符号引用,一部分在类加载的时候转化为直接引用。另外一部分就是在运行的时候通过该帧的这个引用来转化为直接引用。也就是动态链接了。

方法返回地址

方法退出可能因为return 或者是异常,退出实际上是把当前的栈帧出栈,然后根据这个返回地址找到被调用的位置,还会把返回值压入栈帧等。

方法调用

解析

编译器可知,运行期不可变的方法的调用成为解析。 有static的方法和private的方法。 在编译器就会转为直接引用

分派

1. 静态分派

Human man = new Man(). 其中 Man继承于Human,  Human就成为静态变量,  Man就是实际变量, 静态变量编译器确定,实际变量运行期确定。

重载就是使用了静态分派的方式,根据静态变量来判断的。

另外看下面这段代码:

public class OverLoad {

	public static void main(String[] args) {
		OverLoad.sayHello('a');
	}

	private static void sayHello(char c) {
		System.out.println("Char");
	}

	private static void sayHello(int c) {
		System.out.println("Int");
	}

	private static void sayHello(long c) {
		System.out.println("long");
	}

	private static void sayHello(Character c){
		System.out.println("Character");
	}

	private static void sayHello(char c, String... a) {
		System.out.println("Char...");
	}
}

打印什么呢。从sayHello char往下开始一直注释,分别会打印 Char, Int, long,Character, Char...

为什么呢,肯定不是因为代码顺序。 是因为重载的机制,是用的静态分派,分派规则是先找自身,然后JVM强转的,然后是自动封包的,最后是动态参数的

2. 动态分派

如下代码:

abstract class Human{
	abstract void sayHello();
}

class Man extends Human{
	@Override
	void sayHello() {
		System.out.println("Man");
	}
}

class Woman extends Human{
	@Override
	void sayHello() {
		System.out.println("Woman");
	}

	public static void main(String[] args) {
		Human man = new Man();
		Human woman = new Woman();
		man.sayHello();
		woman.sayHello();
	}
}

man.sayHello()和woman.sayHello()的调用部分的字节码是一样的,都是invoke Human.sayHello() 大体的格式,实际上不是这样的。

主要在于new 的时候会把实际变量的引用压栈,然后调用的时候会从栈顶找到根当前符号变量一致的直接引用进行与符号变量的一致性验证,如果通过就是用这个直接引用,没通过会继续对这个符号变量的父类进行验证,如果都没通过则抛出异常

基于栈的执行

基于栈的可以避免硬件差异,能够更紧凑,还能够根据语言特点进行优化,灵活性也更高,但是会比寄存器的执行速度慢一些,因为会有入栈和出栈的过程。

代码:

public int add(){
		int a = 100;
		int b = 200;
		return a + b;
	}

使用javap -c来查看字节码:

public int add();
  Code:
   0:   bipush  100
   2:   istore_1
   3:   sipush  200
   6:   istore_2
   7:   iload_1
   8:   iload_2
   9:   iadd
   10:  ireturn

}

那么这个方法的执行过程就应该是:

计数器 从0开始执行到10

0的时候100入操作数栈,this入局部变量表

1把100出栈放入局部变量表

7,8两部把100,200放入操作数栈顶

9把100,200从操作数站定出栈,进行计算放入操作数栈顶。

10. 返回调用点,把栈顶的300压入调用栈。

字节码生成技术及动态代理

javac, JDK动态代理,  CGLIB等等

interface IHello{
	void sayHello();
}

class Hello implements IHello{

	@Override
	public void sayHello() {
		System.out.println("hello world");
	}

}

public class DynamicProxyTest {
	static class DynamicProxy implements InvocationHandler{
		private IHello hello;
		public IHello bind(IHello hello){
			this.hello = hello;
			return (IHello) Proxy.newProxyInstance(hello.getClass().getClassLoader(),
					new Class[]{IHello.class}, this);
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			System.out.println("welcome");
			return method.invoke(hello, args);
		}
	}
	public static void main(String[] args) {
		IHello hello = new Hello();
		IHello helloP = new DynamicProxy().bind(hello);
		helloP.sayHello();

	}
}

打印的是:

welcome
hello world

主要在于Proxy.newProxyInstance这个方法,其进行了验证,生成字节码等操作。

另外是能够通过设置参数来把这个生成的动态类放到硬盘上的可以反编译查看。

时间: 2024-12-09 20:35:38

JVM深入学习笔记七-JVM执行的相关文章

JVM深入学习笔记六-JVM类加载

类加载过程 主要分为了 加载->链接(验证->准备->解析)->初始化->使用->卸载这几个阶段. 加载 三件事 1. 通过类的权限定名称来获取定义此类的二进制字节流(可以是文件,网络,数据库,动态等等等等) 2. 把类的结构放在方法区中 3. 创建Class对象作为访问入口 验证 主要包括了字节码验证,元数据验证(这部分在编译期间基本上避免了),类文件格式验证. 准备 包括了内存分配和类变量(static)初始值的设定,以及常量池的写入. 解析 主要是解析符号引用和直

JVM深入学习笔记一:Java 编译器初探

闲来无事想大概看下Java编译器的执行过程 一. 编译一个文件 首先去搞到源代码.从JDK6开始Java代码开源,放到OPENJDK组织中去,所以从这个网站进行下载: http://hg.openjdk.java.net/jdk6/jdk6/langtools/  下载之后是一个zip包,解压,可以直接导入到eclipse中进行使用,导入之后可能会报错,需要设置一下. 好,下面写一个用作测试的类: import java.util.Arrays; import java.util.List; i

JVM深入学习笔记五:JVM 监控工具

命令行工具 JDK提供了很多的工具来监控JVM. 这些工具都是支持RMI远程监控的,暂且不记录远程调试的办法 1. JPS Process Status.  列出正在执行的虚拟机进程,可以查看到具体的类和进程ID. public class JPSTest { public static void main(String[] args) { while(true){} } } 执行之. 使用windows命令查看进程: C:\Users\huanggang>tasklist | findstr

JVM深入学习笔记三:JVM 内存模型

1. JVM运行时内存 图 程序计数器 当前线程执行的字节码的行号指示器,线程通过这部分来选择下一条指令,通过这个部分来实现分支,循环等操作.没有OOM,没有参数可以控制 Java虚拟机栈 描述方法执行的内存区,一个方法的执行就是形成一个栈帧入栈和出栈的过程 栈帧(局部变量表,操作数栈, 动态链接),  局部变量表就是参数和一些方法内的局部变量,操作数栈是方法中的一些操作的运算用的字节码, 动态链接是区别于静态链接的部分,主要包含一些执行信息和资源 包含了两种异常: 线程请求的栈深度大于虚拟机所

JVM深入学习笔记二:Java JIT编译

JIT是java虚拟机把热点字节码编译成机器码的技术. 解释执行,在当运行次数比较少的时候能够省去编译的操作直接运行字节码.  另外解释更加的节约内存. 而编译为机器码则可以获得更高的效率. 因为各有好处,HotSpot使用了共存的机制,可以使用-Xint强制使用解释模式或者是-Xcomp 编译模式. 此外HotSpot提供了两种编译器Client Compile和Server Compiler,分别针对于更快的编译速度和更好的编译效果.使用-client或者-server参数指定 在这种共存模

JVM深入学习笔记四:JVM垃圾收集和内存分配

1. 垃圾判断算法 引用计数法,    给对象添加引用计数器,每有一个引用则+1, 没有则-1,为0为已死.python就是使用这种算法.  但是不能解决循环引用的问题. 根搜索算法.     从根开始向下搜索,如果有对象到根不可达则为死对象. HopSpot使用的是这种算法.   这里的根可以是栈中的引用对象, 方法区常量的引用对象,方法区静态属性的引用对象,JNI的引用对象. 两次标记.   虚拟机在回收对象时要做两次标记才会真的回收.无根的时候是一次,执行了finallise方法的时候会再

JSP学习笔记(七)-----猜数字游戏

js|笔记 1. 这是一个猜数字的游戏,通过使用JSP调用JavaBean2. 需要两个文件,number.jsp和NumberGuessBean.java3. 先看number.jsp代码:<html><jsp:useBean id="number" class="NumberGuessBean" scope="session" /><jsp:setProperty name="number"

【Alljoyn】 Alljoyn学习笔记七 Alljoyn瘦客户端库介绍

Alljoyn瘦客户端库介绍(上)   1.简介 本文档对AllJoynTM瘦客户端的核心库文件(AJTCL)进行了详尽的介绍.本文档介绍了系统整体架构,AllJoyn框架结构,并着重于介绍如何将嵌入式设备加入AllJoyn系统整体架构中.1.1目的 本文档介绍了如何使一个受限于功耗.计算能力和内存的设备(嵌入式设备)加入AllJoyn分布式系统.具体而言,本文档包括了对AllJoyn面向嵌入式系统的方面的介绍,并着重描述了基于AllJoyn的系统的各个组件是如何与嵌入式设备协作以构建一个基于接

D3D9学习笔记(七) TEXTURE 纹理

texture永远是图形学最精华细节最多的地方,所以我也花了最多的时间阅读和写这个章节.   d3dDevice->SetTexture(n,tex)设置当前在n通道的texture,n的数量硬件在0-7.多个通道的texture可以进行blending   一个pixel在光照计算后得到一个颜色值c1,它纹理采样有一个颜色值c2,最终这个pixel的颜色值为 Color = c1 x DestBlend+c2 x SourceBlend 可以通过 IDirect3DDevice9::SetRe