说jvm,可能指的是:
一个运行时的虚拟机实例( a runtime instance)就是负责运行一个java程序,这个实例随着java程序的开始(main方法运行)而产生,结束而消失。
java虚拟机中有两种线程,守护线程与非守护线程,像gc这种后台线程就是守护线程,而main方法产生的是非守护线程。当所有的非守护线程结束,则jvm结束。
JVM(此处指:the abstract specification)定义了抽象的组成部分,以及它们之间的交互方式,来定义JVM的实现(a concrete implementation):
每个JVM都有一个 class loader subsystem,当类或接口(必须完全限定名)装载到JVM中,会被装载到class loader subsystem中.同样,每个JVM会有一个execution engine,负责执行载入的class中方法指令。
Class文件包含有代码以及支持代码用的元数据(metadata)。代码部分就是字节码,而元数据部分则包括诸如泛型等许多信息。所以说Class文件包含字节码,但不只有字节码。
takes care of finding and loading types
jvm有两种类装载器:a primordial class loader and class loader objects(原始类装载器:jvm的一部分,用户自定义的类装载器对象:java程序的一部分 )。对于类装载器对象(java程序)都必须继承自
java.lang.ClassLoader.ClassLoader中定义的接口方法,提供了对JVM访问的机制。对于JVM中每个被装载的类型,都会产生一个java.lang.class对象代表它,和其他Object对象一样,对象的产生都在Heap中,而类型信息都放在Method Area.
类装载器除了要定位、导入二进制Class文件外,还必须验证导入类的正确性,为类变量分配并初始化内存。
大致顺序:
当JVM运行时,它需要内存去存储,比如字节码等其他从class文件中获取的信息,对象实例化,方法参数,返回值,局部变量,计算结果的媒介等等。于是每个JVM定义了多个 runtime data areas 去组织这些执行程序需要的内存。(各种JVM会根据这些抽象的定义,产生各种不同的具体实现)。
每个JVM实例有一个Method Area 和一个Heap,这个MethodArea 、Heap被这个JVM中的所有线程所共享。当一个JVM使用classLoader正确定位并装载了class 文件,它会从这些二进制信息中解析出类型信息,并把这些类型信息放入MethodArea中,并把其中的对象实例放入Heap中。
我说了method area主要放类型信息,所谓类型信息指:
For each type it loads, a Java Virtual Machine must store the following kinds of information in the
method area:
The fully qualified name of the type
The fully qualified name of the typeís direct superclass (unless the type is an interface or class java.lang.Object, neither of which have a superclass)
Whether or not the type is a class or an interface
The typeís modifiers ( some subset of` public, abstract, final)
An ordered list of the fully qualified names of any direct superinterfaces
其实通过java.lang.Class的方法,我们可以动态的获取存储在MethodArea里的一些类型信息:
public String getName();
public Class getSuperClass();
public boolean isInterface();
public Class[] getInterfaces();
public ClassLoader getClassLoader()
另外,MethodArea还包括这些:
The constant pool for the type
每个Type(类或接口)都有一个constant pool。它是一个有序集合,其中存放基本类型的引用,还有类型(类或接口)的引用、类变量引用、方法引用。
Field information
存储 类变量的信息:类变量的类型、名称、修饰符(如private、public 、final、static等等)
Method information
方法名、方法返回值、方法修饰符号、方法参数列表、还有方法的字节码、方法抛出的异常列表(非abstract 、native 方法)。
All class ( Static Fields) variables declared in the type, except constants
A reference to class ClassLoader
JVM需要追踪类型(type,指类或接口)是否被载入原始classLoader或classLoader的对象,因此method Area需要存储一个classloader的引用。
A reference to class Class
JVM对每个载入的类型(interface\class)都会创建一个java.lang.class的实例。并且,JVM还要用某种方式,将这个java.lang.class实例与它的Type Information(存在与MethodArea) 关联起来。
通过这个静态方法,你可以获取任何载入到classLoader里的类型对应的java.lang.class实例。
public static Class forName(String className)
另外一种方法获取class,是每个类型实例的getClass()方法。
System.out.println(Class.forName("java.lang.String"));
System.out.println(new String().getClass());
When the Java Virtual Machine loads a type, it uses a class loader to
locate the appropriate class file. The class loader reads in the class
file–a linear stream of binary data-- and passes it to the virtual
machine. The virtual machine extracts information about the type from
the binary data and stores the information in the method area. Memory
for class (static) variables declared in the class is also taken from
the method area
用一个实例来描述一下JVM的动作:
class Lava {
private int speed = 5; // 5 kilometers per hour
void flow() {
}
}
public class Volcano {
public static void main(String[] args) {
Lava lava = new Lava();
lava.flow();
}
}
运行程序,JVM找到并读取Volcano.class文件,然后在引入的Volcano.class文件中读取Volcano类的信息,将这些信息放入Method Area中。然后JVM通过解释Method Area中的codeByte调用main方法。
注意,当JVM执行main()时,它并没有加载Lava.class,它会按需加载。
执行main()时, JVM调用足够的内存空间给CostantPool的第一条Volcano类,然后在Volcano的ConstantPool中找类Lava的引用符号(就是字符“Lava”),它会检查Lava是否被载入。当JVM发现没有,就会寻找并读取Lava.class文件,然后从二进制的Lava.class中读取信息,并放入MethodArea.紧接着, JVM会把刚才ConstantPool的首条的指针(指向Volcano的指针)替换为指向方法区Lava类数据的指针,以后用这个指针快速访问Lava。
接下来,JVM通过ConstantPool的首条指针(指向MethodArea的Lava信息),来快速获取Lava信息,并按需分配足够内存。
(JVM总是通过MethodArea类型信息去判断需要调用多少内存)当JVM确定了内存大小,就去堆上分配,并把这个对象的speed变量初始化为0.然后,把生成的Lava对象的引用压入栈中。
Lava lava = new Lava();这个代码完成了。
然后,调用flow()。
JVM通过在多种类型的数据上进行操作来完成计算。
基本类型保存值、引用类型指向对象。(基本类型与引用类型的区别,很简单)
这里要提boolean,虽然它也被定义为基本类型,但是java编译器把源码编译为字节码,装载在JVM中,boolean被用int或byte来表示,0表示false;非0整数被表示true.
Heap用来存放实例化对象或数组,会调用分配来自heap的内存空间。也就是说,对象本身(memory)在heap上创建。
两个Java application 不可能共享Heap,当一个java application 的不同线程可以共享,因此,我们在多线程中,同步的使用一个对象。
另外,代码中可以去决定什么时候创建对象,但是不能决定什么时候销毁对象(释放内存)。这个完全有JVM自己决定(GC管理heap,除了释放空间,它还会移动对象来减少内存碎片)。
我们已经知道,栈中存放执行堆中对象的引用,除此,堆中对象内部还会保留一个执行方法区的引用(为了快速访问类型信息)。