第二课:jvm运行机制

编程入门 行业动态 更新时间:2024-10-26 17:30:56

第二课:jvm<a href=https://www.elefans.com/category/jswz/34/1746719.html style=运行机制"/>

第二课:jvm运行机制

1.启动流程

 java + 类-->装载配置(config文件)-->根据配置找到java.dll(为java虚拟机的主要实现)-->初始化java虚拟机(比如JNIEnv接口,findclass等操作可以通过这个接口来实现)-->找到main方法

2.如图1所示:jvm的内部结构

几个重要的组成部分:
pc寄存器:1.每个线层又有一个pc寄存器2.在线程创建时 创建3.指向下一条指令的地址4.执行本地方法时,pc的值为undefined
方法区:保存类的原信息 (类型的常量池/字段、方法信息/方法字节码)在jdk6和7中,String等常量信息置于方法,jdk7时,已经移动到了堆通常和永久区(perm)关联在一起
java堆:所有通多new操作出来的对象,都是通过堆来操作的--和程序开发密切相关--应用系统对象都保存在java堆中--所有线程共享java堆--一般是对分代GC来说,堆也是分代的(不同的gc方法需要不同的堆)--GC的主要工作区间eden--s0--s1--tenured(复制算法)
java栈:-线程私有-栈由一系列的帧组成(因此Java栈也叫做帧栈)-帧保存一个方法的局部变量、操作数栈、常量池指针-每一次方法调用创建一个帧,并压栈


3.JVM栈很重要的一个局部变量表 基本结构 (包含参数和局部变量)

  public class StackDemo{public static int runStatic(int i,long l,float f,Object O,byte b){return 0;}public int runInstance(char c,short s,boolean b){return 0;}} 

想想成每一个都是32位的一个小格子一个槽位 long是64位占俩

静态方法如下:

0 int int i
1 long long l
2 *1
3 float float f
4 reference Object o
5 int byte b

实例方法如下:

0 reference this
1 int char c
2 int short s
3 int boolean b

4. java栈 操作数栈
-java没有寄存器 所有参数传递使用操作数栈

 public static int add(int a,int b){int c = 0;c = a+b;return c;}

当执行上面程序帧栈的的操作工程

0 iconst_0 // 0压栈
1 istore_2 // 弹出int,存放于局部变量2
2 iload_0 // 把局部变量0压栈
3 iload_1 // 局部变量1压栈
4 iadd // 弹出两个变量,求和,结果压栈
5 istore_2 // 弹出结果,存放局部变量2
6 iload_2 // 局部变量2压栈
7 ireturn //返回 

5. Java栈的分配(优化手段)

-小对象(几十个bytes),在没有逃逸的情况下,可以直接分配在栈上
-直接分配在栈上,可以自动回收,减少GC压力(是指,这个栈没有了,那我就没有必要再去进行垃圾回收了)
-大对象或者逃逸对象无法栈上分配  

解释:一般来说一个栈最大几百kb,1M顶天了,所以大对象,是没有办法在栈上进行分配的。当有小对象时,当一个方法分配在栈上,执行完成之后,这些小对象随着方法的消亡而消亡,所以可以直接分派到栈上。
这里还有一个逃逸对象的概念,是指,我并不止一个线程中要用到这个对象,我在其他的线程中,也用到这个对象,所以这个对象是在栈尚无法进行分配的。
6.图2:堆、栈、方法去之间的交互

public class AddMain{// AddMain的所有信息都在方法区,包括main方法public static void main(String ){//new出来的sample对象在堆里面,test1和test2 都是sample的对象的引用,在栈里面Sample test1 = new Sample("测试1");Sample test2 = new Sample("测试2");test1.printName();test2.printName();}public class Sample{//Sample的所有信息都是在i方法区内private name;//name 的对象在堆里面,name的引用在栈里面public Sample(name){this.name = name;}//println方法也在方法区内部public class printName{System.Out.println(name);}
}


7.图三 :关于线程
-每一个线程都有一个工作内存和主存,主存是一个共享的内存区间(可以理解成堆空间,所有线程都共享,所有线程都可以在里面存储数据)
-工作内存存放主存中变量的值的拷贝

	 图三解释:当数据从主存复制到工作内存时,必须出现两个动作:第一由主存执行的read操作,第二,由工作内存执行的相应的load操作;当数据从工作内存拷贝到主存时,也出现两个操作,一个是由工作内存存储到主存的store操作,第二,由主存执行的相应的write操作每一个操作都是原子的,即执行期间不会被中断 但是read和load之间是可以被中断的对于普通变量,一个线程中更新的值,可能无法立即加载到主存中,也就无法被其他反应在其他变量中如果需要其他线程立即可见,需要使用volatile关键字


8.volatile关键字

public class VolatileStopThread extends Thread{private volatile boolean stop = false;public void stopMe(){stop = true ;}public void run(){int i = 0 ;while(!stop)i++}System.out..println("stop Thread");}public static void main(String args[]) throws InterruptException{VolatileStopThread t = new VolatileStopThread();t.start();Thread.sleep(1000);t.stopMe();Thread.sleep(1000);}} 

volatile不能代替锁(线程不安全的)
一般认为volatile比锁的性能要好(不绝对)

选择使用volatile的条件是:
语义是否满足应用

如果这个里面没有volatile这个关键字的话,这个VolatileStopThread只在自己的工作内存当中查找这个stop的值,因此永远也不会停止

9.内存模型可见的概念
1.可见性

  可见性是指在我修改了线程的值的时候,其他的线程能够立即知道(这里说的不是能够知道,是立即知道)能够实现可见性的几种方法1.volatile 2.synchronized(unlock之前,写变量值回主存)线程间的同步,这个词就是同步3.final(一旦初始化完成,其他线程就可见,常量)

2.有序性。(在一个线程当中,所有的指令,所有的操作都是有序执行的)
但是在另一个线程当中,去观察本线程,也许就是无序的(两种可能,第一种就是指令重排,第二种就是与主存存在同步延时)

 指令重排:在编译器中,会自动重排一些语句,比如a=1,b=2。在编译器的理解下,它不考虑语句的语义,如果他认为这是可以重排的,那么就会发生指令重排。但是如果a=1,b=a 这个就不可以发生指令重排

指令重排的一个例子:

public class OrderExample{int a = 0 ;boolean flag = false ;
public void writer(){a = 1;flag = true ; }public void reader(){if (flag){int i = a + 1;...}
}
}

解释上述代码:

   线程A首先执行write()方法线程B接着执行reader()方法线程B在 int i = a+1是不一定能看到a已经被赋值给1的,因为在write中,两句话的顺序可能会被打乱 (这个时候就体现了操作重排了)这个时候,两个方法都是同一个操作级别的,当你的flag = true时,这个时候reader就进来了,把flag取走了那么在执行的时候,最后的结果是多少呢 线程A:                   线程B:flag = true          flag = true(此时a = 0) a = 1 但是我想保证他的有序性,因为有时候是必要的,那我应该怎么做呢:我就可以在两个方法都加上synchronized关键字,加上suynchronized关键字之后,即使A线程出现了指令重排,但是线程B是进不来的,所以是要把一个线程运行完成之后,直到把线程锁释放,才能够运行下一个。不再是平行运行,而是属于一个串行了。

指令重排基本原则:

-程序顺序原则:一个程序内保证语义的串行性
-volatile规则:volatile变量的写,先发生于读
-锁规则:解锁(unlock)必然发生在所后的加锁(lock)前
-传递性:A先于B,B先于C,那么A必然先于C
-线程的 start方法先于它的每一个动作
-线程的所有操作电鱼线程的中介(Thread.join())
-线程的中断(interrupt())先于被中断线程的代码
-对象的构造函数执行结束先于finalize()方法

10.解释执行和编译执行的区别
解释执行:

  -解释执行以解释的方式运行字节码-解释执行的意思是:读一句,执行一句	

编译执行:

-将字节码编译成机器码
-直接执行机器码
-运行时编译
-编译后性能有数量级的提升(10倍以上)

更多推荐

第二课:jvm运行机制

本文发布于:2024-02-17 10:10:47,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1693652.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:运行机制   jvm

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!