java虚拟机方面

来源:互联网 发布:网络环境的含义 编辑:程序博客网 时间:2024/05/17 01:09

 

一、类装载器:通过分别使用不同的类装载器装载可靠的包和不可靠的包来实现安 全性。

 

1.2版本的Java虚拟机开始运行时,在应用程序启动以前,它至少创建一个用户自定义类装载器,也可能是多个。所有这些类装载器被连接在一个双亲--孩子的关系链中,在这条链的顶端是启动类装载器,末端是一个被称为“系统类装载器”的类装载器。

在有双亲委派模式下,启动类装载器可以抢在标准扩展类装载器之前去装载类,而标准扩展类装载器可以抢在类路径类装载器之前去装载那个类,类路径类装载器又可以抢在网络类装载器之前去装载它。这样,启动类装载器会在最可信的类库—JAVA 核心API—中首先检查每个被装载的类型,然后,才依次到标准扩展、类路径上的本地类文件中检查。

Java虚拟机只把彼此访问的特殊权限授予由同一个类装载器装载到同一个包中的类型。

 

所有的java虚拟机实现必须在每一个类或接口首次主动使用时初始化。下面这六种情形符合主动使用的要求:

w          当创建某个类的新实例时(或者通过在字节码中执行new指令;或者通过不明确的创建、反射、克隆或者反序列化)。

w          当调用某个类的静态方法时(即在字节码中执行invokestatic指令时)。

w          当使用某个类或接口的静态字段,或者对该字段赋值时(即在字节码中,执行getstaticputstatic指令),final修饰的静态字段除外,它被初始化为一个编译时的常量表达式

w          当调用java API中的某些反射方法时,比如类Class中的方法或者java.lang.reflect包中的类的方法。

w          当初始化某个类的子类时(某个类初始化时,要求它的超类已经被初始化了)。

w          当虚拟机启动时某个被标明为启动类的类(即含有main())方法的那个类)。

其他的都是被动使用。

 

初始化一个类包含两个步骤:

1)如果类存在直接超类的话,且直接超类还没有被初始化,就先初始化直接超类。

2)如果类存在一个类初始化方法,就执行此方法。

注:1,当初始化一个类的直接超类的时候,也是需要包含这两个步骤。因此第一个被初始化的类永远是Object,然后是被主动使用的类的继承树上的所有的类。超类总是在子类之前被初始化。

    2,初始化接口并不需要初始化它的父接口,因此初始化一个接口只需要一步:即第二步。如果类存在一个类初始化方法,就执行此方法。

 

    二、类型转换:java虚拟机包含许多进行基本类型转换工作的操作码,后面没有操作数,转换的值从栈顶端获得。

w          Int,long,floatdouble之间的转换操作码:i2l, i2f, i2d,  l2i, l2f, l2d,  f2i, f2l, f2d,  d2i, d2l, d2f

w          Int转换比int类型占据更小空间的数据类型。i2b,  i2c,  i2s分别表示将int类型值转换成byte, shortchar类型。

w          Long, float, double类型值不能直接转换成比int占据更小空间的数据类型。因次,把float类型转换成byte类型需要两步:首先,float类型值必须通过f2i指令转换成int类型。然后,通过i2b指令转换成byte类型值。

w          不存在从byte, char, short类型转换成int的操作码,因为,任何byte, char, short类型的值在压入栈的时候,就已经有效地被转换成int类型的值了。

w          涉及byte, char, short类型的运算,首先会把它们转换成int类型,然后对int类型值进行运算,最后得到int类型的结果。因此,如果把两个byte类型值相加,最后会得到一个int类型的结果。可以显式地转换为byte类型。

 

三、方法调用:

java程序设计语言提供了两种基本的方法:实例方法和类方法(或者静态方法)。这两种方法的区别:

1)实例方法在被调用之前,需要一个实例,而类方法不需要。

2)实例方法使用动态(迟)绑定,而类方法使用静态(早)绑定。

注:当java虚拟机调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当虚拟机调用一个实例方法时,它会基于对象实际的类

(只能在运行时得到)来选择所调用的方法。

 

四、线程同步

Java语言中的同步有两种方式:互斥和协作。互斥帮助线程在访问共享数据时不被其他线程干扰,而协作帮助线程与其他线程共同工作。

 

Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如何?――还得对synchronized关键字的作用进行深入了解才可定论。

总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。

在进一步阐述之前,我们需要明确几点:

A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。

B.每个对象只有一个锁(lock)与之相关联。

C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

接着来讨论synchronized用到不同地方对代码产生的影响:

假设P1P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1P2就都可以调用它们。

1  synchronized当作函数修饰符时,示例代码如下:

Public synchronized void methodAAA()

{

//….

}

这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。

上边的示例代码等同于如下代码:

public void methodAAA()

{

synchronized (this)      //  (1)

{

       //…..

}

}

 (1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(

2.同步块,示例代码如下:

            public void method3(SomeObject so)

              {

                     synchronized(so)

{

       //…..

}

}

这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:

class Foo implements Runnable

{

       private byte[] lock = new byte[0];  // 特殊的instance变量

    Public void methodA()

{

       synchronized(lock) { //… }

}

//…..

}

注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

3.将synchronized作用于static 函数,示例代码如下:

      Class Foo

{

public synchronized static void methodAAA()   // 同步的static 函数

{

//….

}

public void methodBBB()

{

       synchronized(Foo.class)   //  class literal(类名称字面常量)

}

       }

   代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。

记得在《Effective Java》一书中看到过将 Foo.class P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。

可以推断:如果一个类中定义了一个synchronizedstatic函数A,也定义了一个synchronized instance函数B,那么这个类的同一对象Obj在多线程中分别访问AB两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class

小结如下:

搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。