JVM 学习笔记

JVM学习笔记-方法区(Method Area)

  方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。
  对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。对于其他虚拟机(如BEA JRockit、IBM J9等)来说是不存在永久代的概念的。即使是HotSpot虚拟机本身,根据官方发布的路线图信息,现在也有放弃永久代并“搬家”至Native Memory来实现方法区的规划了。
   Java虚拟机规范对这个区域的限制非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。在Sun公司的BUG列表中,曾出现过的若干个严重的BUG就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。
  根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

以上描述截取自:
  《深入理解Java虚拟机:JVM高级特性与最佳实践》 作者: 周志明

Inside a Java Virtual Machine instance, information about loaded types is stored in a logical area of memory called the method area. 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.
  在Java虚拟机中,关于被装载类型的信息存储在一个逻辑上被称为方法区(Method Area)的内存中,当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件——一个线性二进制数据流——然后将它传输到虚拟机中。紧接着虚拟机提取其中的类型信息,并将这些信息存储到方法区(Method Area)。该类型中的类变量(static变量)同样也是存储在方法区(Method Area)中。

The manner in which a Java Virtual Machine implementation represents type information internally is a decision of the implementation designer. For example, multi-byte quantities in class files are stored in big-endian (most significant byte first) order. When the data is imported into the method area, however, a virtual machine can store the data in any manner. If an implementation sits on top of a little-endian processor, the designers may decide to store multi-byte values in the method area in little-endian order.
  Java虚拟机在内部如何存储类型信息,这是由具体实现的设计者来决定的。比如,在class文件中,多字节值总是以高位在前的顺序存储。但是当这些数据引入到方法区后,虚拟机可以以任何方式存储它。假设某个实现是运行在低位优先的处理器上,那么它很可能会把多字节值以低位优先的顺序存储到方法区。

The virtual machine will search through and use the type information stored in the method area as it executes the application it is hosting. Designers must attempt to devise data structures that will facilitate speedy execution of the Java application, but must also think of compactness. If designing an implementation that will operate under low memory constraints, designers may decide to trade off some execution speed in favor of compactness. If designing an implementation that will run on a virtual memory system, on the other hand, designers may decide to store redundant information in the method area to facilitate execution speed. (If the underlying host doesnít offer virtual memory, but does offer a hard disk, designers could create their own virtual memory system as part of their implementation.) Designers can choose whatever data structures and organization they feel optimize their implementations performance, in the context of its requirements.
  当虚拟机运行Java程序时,它会查找使用存储在方法区中的类型信息。设计者应当为类型信息的内部表示设计恰当的数据结构,以尽可能在保持虚拟机小巧紧凑的同时加快程序的运行效率。如果正在设计一个需要在少量内存的限制中操作的实现,设计者可能会决定以牺牲某些运行速度来换取紧凑性。另外一个方面,如果设计一个将在虚拟内存系统中运行的实现,设计者可能会决定在方法区中保存一些冗余信息,一次来加快执行速度。(如果底层主机没有提供虚拟内存,但是提供了一个硬盘,设计者可能会在实现中创建一个虚拟内存系统。)Java虚拟机的设计者可以根据目标平台的资源限制和需求,在空间和时间上做出权衡,选择实现什么样的数据结果和数据组织。

All threads share the same method area, so access to the method areaís data structures must be designed to be thread-safe. If two threads are attempting to find a class named Lava, for example, and Lava has not yet been loaded, only one thread should be allowed to load it while the other one waits.
  由于所有线程都共享方法区,因此它们对方法区数据的访问必须被设计为是线程安全的。比如,假设同时有两个线程都企图访问一个名为Lava的类,而这个类还没有被装入虚拟机,那么,这时只应该有一个线程去装载它,而另一个线程则只能等待。

The size of the method area need not be fixed. As the Java application runs, the virtual machine can expand and contract the method area to fit the applicationís needs. Also, the memory of the method area need not be contiguous. It could be allocated on a heap–even on the virtual machineís own heap. Implementations may allow users or programmers to specify an initial size for the method area, as well as a maximum or minimum size.
  方法区的大小不必是固定的,虚拟机可以根据应用的需要动态调整。同样,方法区也不必是连续的,方法区可以在一个堆中自由分配。另外,虚拟机也可以允许用户或者程序员指定方法区的初始大小以及最小和最大尺寸等。

The method area can also be garbage collected. Because Java programs can be dynamically extended via class loader objects, classes can become “unreferenced” by the application. If a class becomes unreferenced, a Java Virtual Machine can unload the class (garbage collect it) to keep the memory occupied by the method area at a minimum. The unloading of classes–including the conditions under which a class can become “unreferenced”–is described in Chapter 7, “The Lifetime of a Class.”
  方法区也可以被垃圾收集,因为虚拟机允许通过用户定义的类装载器来动态扩展Java程序,因此一些类也会成为程序“不再引用”的类。当某个类变为不再被引用的类时,Java虚拟机可以卸载这个类(垃圾收集),从而使方法区占据的内存保持最小。类的卸载以及一个类变为“不再被引用”的必需条件,都将在第7章中描述。

以上描述截取自:
  《Inside the Java Virtual Machine 2nd Edition》 作者:Bill Venners


JVM学习笔记-类型信息(Type Information)

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)
    这个类型的直接超类的全限定名(除非这个类是java.lang.Object,它没有超类)。
  • Whether or not the type is a class or an interface
    这个类型是类类型还是接口类型
  • The typeís modifiers ( some subset of` public, abstract, final)
    这个类型的访问修饰符(public, abstract或final的某个子集)。
  • An ordered list of the fully qualified names of any direct
    superinterfaces
    任何直接超接口的全限定名的有序列表

Inside the Java class file and Java Virtual Machine, type names are always stored as fully qualified names. In Java source code, a fully qualified name is the name of a typeís package, plus a dot, plus the typeís simple name. For example, the fully qualified name of class Object in package java.lang is java.lang.Object. In class files, the dots are replaced by slashes, as in java/lang/Object. In the method area, fully qualified names can be represented in whatever form and data structures a designer chooses.
  在Java class文件和虚拟机中,类型名总是以全限定名出现。在Java源代码中,全限定名由类所属包的名称加一个“.”,再加上类名组成。例如,类Object的所属包为java.lang,那么它的全限定名应该是java.lang.Object,但在class文件里,所有的“.” 都被斜杠“/”代替,这样就成为java/lang/Object。至于全限定名在方法区中的表示,则因不同的设计者有不同的选择而不同,可以用任何形式和数据结构来表示。
In addition to the basic type information listed above, the virtual machine must also store for each loaded type:
  除了上面列出的基本类型信息外,虚拟机还得为每个被装载的类型存储以下信息:

  • The constant pool for the type
    该类型的常量池
  • Field information
    字段信息
  • Method information
    方法信息
  • All class (static) variables declared in the type, except constants
    除了常量以外的所有类(静态)变量
  • A reference to class ClassLoader
    一个到类CLassLoader的引用
  • A reference to class Class
    一个到Class类的引用

JVM学习笔记-常量池(Constant Pool)

For each type it loads, a Java Virtual Machine must store a constant pool. A constant pool is an ordered set of constants used by the type, including literals (string, integer, and floating point constants) and symbolic references to types, fields, and methods. Entries in the constant pool are referenced by index, much like the elements of an array. Because it holds symbolic references to all types, fields, and methods used by a type, the constant pool plays a central role in the dynamic linking of Java programs.
  虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用常量的一个有序集合,包括直接常量(String ,integer和floating point常量)和对其他类型、字段和方法的符号引用。池中的数据项就像数组一样是通过索引访问的。因为常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以它在Java程序的动态连接中起着核心作用。

Following the magic and version numbers in the class file is the constant pool. As mentioned in Chapter 5, “The Java Virtual Machine,” the constant pool contains the constants associated with the class or interface defined by the file. Constants such as literal strings, final variable values, class names, and method names are stored in the constant pool. The constant pool is organized as a list of entries. A count of the number of entries in the list, constant_pool_count, precedes the actual list, constant_pool.

  在class文件中,魔数和版本号后面的是常量池。正如第5章中所述,常量池包含了与文件中类和接口相关的常量。常量池中存储了诸如文字字符串、final变量值、类名和方法名的常量。Java虚拟机把常量池组织为入口列表的形式。在实际列表constant_pool之前,是入口列表中的技术constant_pool_count。

Many entries in the constant pool refer to other entries in the constant pool, and many items that follow the constant pool in the class file refer back to entries in the constant pool. Throughout the class file, constant pool entries are referred to by the integer index that indicates their position in the constant_pool list. The first entry in the list has an index of one, the second has an index of two, and so on. Although there is no entry in the constant_pool list that has an index of zero, the missing zeroeth entry is included in the constant_pool_count. For example, if a constant_pool list includes fourteen entries (with indexes one through fourteen), the constant_pool_count would be fifteen.
  常量池中的许多入口都指向其他的常量池入口,而且class文件中紧随着常量池的许多条目也会指向常量池中的入口。在整个class文件中,指示常量池入口在常量池列表中位置的整数索引都指向这些常量池入口。列表中的第一项索引值为1,第二项索引值为2,以此类推。尽管constant_pool列表中没有索引值为0的入口,但缺失的这一入口也被constant_pool_count计数在内。例如,当constant_pool中有14项(索引值从1到14)时,constant_pool_count的值为15.

Each constant pool entry starts with a one-byte tag that indicates the type of constant making its home at that position in the list. Once a Java Virtual Machine grabs and interprets this tag, it knows what to expect after the tag. Table 6-3 shows the names and values of the constant pool tags.
  每个常量池入口都从一个长度为一个字节的标志开始,这个标志指出了列表中该位置的常量类型。一旦java虚拟机获取并解析这个标志,Java虚拟机就会知道在标志后的常量类型是什么,表6-3列出了所有常量池标志的名字和值。

Table 6-3. Constant pool tags

Entry TypeTag ValueDescription
CONSTANT_Utf81A UTF-8 encoded Unicode string
CONSTANT_Integer3An int literal value
CONSTANT_Float4A float literal value
CONSTANT_Long5A long literal value
CONSTANT_Double6A double literal value
CONSTANT_Class7A symbolic reference to a class or interface
CONSTANT_String8A String literal value
CONSTANT_Fieldref9A symbolic reference to a field
CONSTANT_Methodref10A symbolic reference to a method declared in a class
CONSTANT_InterfaceMethodref11A symbolic reference to a method declared in an interface
CONSTANT_NameAndType12Part of a symbolic reference to a field or method

For each tag shown in Table 6-3, there is a corresponding table. The name of the table is formed by appending “_info” to the tag name. For example, the table that corresponds to the CONSTANT_Class tag is called CONSTANT_Class_info. The CONSTANT_Utf8_info table stores a compressed form of Unicode strings. The tables for the various kinds of constant pool entries are described in detail later in this chapter.
  表6-3中的每一个标志都有一个相对应的表,表名通过在标志后加上“_info”后缀来产生。例如,对应于CONSTANT_Class标志的表名为CONSTANT_Class_info,表名为CONSTANT_Utf8_info的表中存储着Unicode字符串的压缩形式。对英语各种不同常量池入口的表将在本章后面详细描述。

The constant pool plays an important role in the dynamic linking of Java programs. In addition to literal constant values, the constant pool contains the following kinds of symbolic references:
  在动态链接的Java程序中,常量池充当了十分重要的角色。除了字面常量(或者说直接量)值以外,常量池还可以容纳下面几种符号引用。

  • fully qualified names of classes and interfaces 类和字段的全限定名。
  • field names and descriptors 字段的名称和描述符
  • method names and descriptors 方法的名称和描述符

A field is an instance or class variable of the class or interface. A field descriptor is a string that indicates the fieldís type. A method descriptor is a string that indicates the methodís return type and the number, order, and types of its parameters. The constant poolís fully qualified names and method and field descriptors are used at run time to link code in this class or interface with code and data in other classes and interfaces. The class file contains no information about the eventual memory layout of its components, so classes, fields, and methods cannot be referenced directly by the bytecodes in the class file. The Java Virtual Machine resolves the actual address of any referenced item at run time given a symbolic reference from the constant pool. For example, bytecode instructions that invoke a method give constant pool index of a symbolic reference to the method to invoke. This process of using the symbolic references in the constant pool is described in more detail in Chapter 8, “The Linking Model.”
  字段是类或接口的实例变量或者类变量。字段的描述符是一个指示字段的类型的字符串。方法的描述符也是一个字符串,该字符串指示方法的返回值和参数的数量,顺序和类型。在运行时,Java虚拟机使用常量池的全限定名,方法和字段的描述符,把当前类或接口中的代码与其他类或接口中的代码连接起来。由于class文件并不包含其内部组件最终内存布局的信息,因此类,字段和方法并不能被class文件中的字节码直接引用。Java虚拟机从常量池获得符号引用,然后在运行时解析引用项的实际地址。例如,用来调用方法的字节码指令把一个符号引用的常量池索引传给所调用的方法。在常量池中使用符号引用的过程在第8章有更详细的阐述。

The constant pool is an ordered list of cp_info tables, each of which follows the general form shown in Table 6-8. The tag item of a cp_info table, an unsigned byte, indicates the tableís variety and format. cp_info tables come in eleven varieties, each of which is described in detail in the following sections.
Table 6-8. General form of a cp_info table
  常量池是一个可变长度cp_info表的有序序列。这些cp_info表的通常形式如表6-8所示。Cp_info表中的tag(标志)项是一个无符号的byte类型值,它表明了表的类型和格式cp_info表一共有11中类型。

TypeNameCount
u1tag1
u1infodepends on tag value

JVM学习笔记-字段信息(Field Information)

For each field declared in the type, the following information must be stored in the method area. In addition to the information for each field, the order in which the fields are declared by the class or interface must also be recorded. Hereís the list for fields:
  对于类型中声明的每一个字段,方法区中必须保存下面的信息。除此之外,这些字段在类或者接口中的声明顺序也必须保存。下面是字段信息的清单:

  • The field’s name
    字段名
  • The field’s type
    字段的类型
  • The field’s modifiers (some subset of public, private, protected,static, final, volatile, transient)
    字段的修饰符(public,private,protected,static,final,volatile,transient的某个子集)。

JVM学习笔记-方法信息(Method Information)

For each method declared in the type, the following information must be stored in the method area. As with fields, the order in which the methods are declared by the class or interface must be recorded as well as the data. Hereís the list:
  对于类型中声明的每一个方法,方法区中必须保存下面的信息。和字段一样,这些方法在类或者接口中的生命顺序也必须保存。下面是方法信息的清单:

  • The method’s name
    方法名
  • The method’s return type (or void)
    方法的返回类型(或void)
  • The number and types (in order) of the method’s parameters
    方法参数的数量和类型(按声明顺序)
  • The method’s modifiers (some subset of public, private, protected,static, final, synchronized, native, abstract)
    方法的修饰(public,private,protected,static,final,volatile,transient的某个子集)
    In addition to the items listed above, the following information must also be stored with each method that is not abstract or native:
      除上面的清单中列出的条目之外,如果某个方法不是抽象的和本地的,它还必须保存下列信息:
  • The method’s bytecodes
    方法的字节码(bytecodes)
  • The sizes of the operand stack and local variables sections of the method’s stack frame (these are described in a later section of this chapter)
    操作数栈和该方法的栈帧中的局部变量区的大小
  • An exception table (this is described in Chapter 17, “Exceptions”)
    异常表

JVM学习笔记-类(静态)变量(Class Variables)

Class variables are shared among all instances of a class and can be accessed even in the absence of any instance. These variables are associated with the class–not with instances of the class–so they are logically part of the class data in the method area. Before a Java Virtual Machine uses a class, it must allocate memory from the method area for each non-final class variable declared in the class.
  类变量是由所有类实例共享的,但是即使没有任何类实例,它也可以被访问。这些变量只与类有关——而非类的实例,因此它们总是作为类型信息的一部分而存储在方法区。除了在类中生命的编译时常量外,虚拟机在使用某个类之前,必须在方法区中为这些类分配空间。

Constants (class variables declared final) are not treated in the same way as non-final class variables. Every type that uses a final class variable gets a copy of the constant value in its own constant pool. As part of the constant pool, final class variables are stored in the method area–just like non-final class variables. But whereas non-final class variables are stored as part of the data for the type that declares them, final class variables are stored as part of the data for any type that uses them. This special treatment of constants is explained in more detail in Chapter 6, “The Java Class File.”
  而编译时常量(就是那些用final声明以及用编译时已知的值初始化的类变量)则和一般的类变量的处理方式不同,每个使用编译时常量的类型都会复制它的所有常量到自己的常量池中,或嵌入到它的字节码流中。作为常量池或字节码流的一部分,编译时常量保存在方法区中——就和一般的类变量一样。但是当一般的类变量作为声明它们的类型的一部分数据而保存的时候,编译时常量作为使用它们的类型的一部分而保存。


JVM学习笔记-指向ClassLoader类的引用(A Reference to Class ClassLoader)

For each type it loads, a Java Virtual Machine must keep track of whether or not the type was loaded via the primordial class loader or a class loader object. For those types loaded via a class loader object, the virtual machine must store a reference to the class loader object that loaded the type. This information is stored as part of the type’s data in the method area.
  每个类型被装载的时候,虚拟机必须跟踪它是由启动类装载器还是由用户自定义类装载器装载的。如果是用户自定义类装载器装载的,那么虚拟机必须在类型信息中存储对该类装载器的引用。这是作为方法表中的类型数据的一部分保存的。

The virtual machine uses this information during dynamic linking. When one type refers to another type, the virtual machine requests the referenced type from the same class loader that loaded the referencing type. This process of dynamic linking is also central to the way the virtual machine forms separate name spaces. To be able to properly perform dynamic linking and maintain multiple name spaces, the virtual machine needs to know what class loader loaded each type in its method area. The details of dynamic linking and name spaces are given in Chapter 8, “The Linking Model.”
  虚拟机会在动态连接期间使用这个信息。当某个类型引用另一个类型的时候,虚拟机会请求装载发起引用类型的类装载器来装载被引用的类型。这个动态连接的过程,对于虚拟机分离命名空间的方式也是至关重要的。为了能够正确地执行动态连接以及维护多个命名空间,虚拟机需要在方法表中得知每个类都是由哪个类装载器装载的。


JVM学习笔记-指向Class类的引用(A Reference to Class Class)

An instance of class java.lang.Class is created by the Java Virtual Machine for every type it loads. The virtual machine must in some way associate a reference to the Class instance for a type with the typeís data in the method area.
  对于每一个被装载的类型(不管是类还是接口),虚拟机都会相应地为它创建一个java.lang.Class类的实例,而且虚拟机还必须以某种方式把这个实例和存储在方法区中的类型数据关联起来。

Your Java programs can obtain and use references to Class objects. One static method in class Class, allows you to get a reference to the Class instance for any loaded class:
  在你的Java程序中,你可以得到并使用指向Class对象的引用。Class类中的一个静态方法可以让用户得到任何已装载的类的Class实例的引用。

begin
// A method declared in class java.lang.Class:
public static Class forName(String className);

end

If you invoke forName(“java.lang.Object”), for example, you will get a reference to the Class object that represents java.lang.Object. If you invoke forName(“java.util.Enumeration”), you will get a reference to the Class object that represents the Enumeration interface from the java.util package. You can use forName() to get a Class reference for any loaded type from any package, so long as the type can be (or already has been) loaded into the current name space. If the virtual machine is unable to load the requested type into the current name space, forName() will throw ClassNotFoundException.
  比如,如果调用forName(“java.lang.Object”),那么将得到一个代表java.lang.Object的Class对象的引用。可以使用forName()来得到代表任何包中任何类型的Class对象的引用,只要这个类型可以被(或者已经被)装载到当前命名空间中。如果虚拟机无法把请求的类型装载到当前命名空间,那么forName()会抛出ClassNotFoundException异常。

An alternative way to get a Class reference is to invoke getClass() on any object reference. This method is inherited by every object from class Object itself:
  另一个得到Class对象引用的方法是,可以调用任何对象引用的getClass()方法。这个方法被来自Object类本身的所有对象继承:

begin
// A method declared in class java.lang.Object:
public final Class getClass();

end

If you have a reference to an object of class java.lang.Integer, for example, you could get the Class object for java.lang.Integer simply by invoking getClass() on your reference to the Integer object.
Given a reference to a Class object, you can find out information about the type by invoking methods declared in class Class. If you look at these methods, you will quickly realize that class Class gives the running application access to the information stored in the method area. Here are some of the methods declared in class Class:

  比如,如果你有一个到java.lang.integer类的对象的引用,那么你只需要简单地调用Integer对象引用的getClass()方法,就可以得到表示java.lang.Integer类的Class对象。

  给出一个指向Class对象的引用,就可以通过Class类中定义的方法来找出这个类型的相关信息。如果查看这些方法,会很快意识到,Class类使得运行程序可以访问方法区中保存的信息。下面是Class类中声明的方法:

begin
// Some of the methods declared in class java.lang.Class:
public String getName();
public Class getSuperClass();
public boolean isInterface();
public Class[] getInterfaces();
public ClassLoader getClassLoader()

end

These methods just return information about a loaded type. getName() returns the fully qualified name of the type. getSuperClass() returns the Class instance for the typeís direct superclass. If the type is class java.lang.Object or an interface, none of which have a superclass, getSuperClass() returns null. isInterface() returns true if the Class object describes an interface, false if it describes a class. getInterfaces() returns an array of Class objects, one for each direct superinterface. The superinterfaces appear in the array in the order they are declared as superinterfaces by the type. If the type has no direct superinterfaces, getInterfaces() returns an array of length zero. getClassLoader() returns a reference to the ClassLoader object that loaded this type, or null if the type was loaded by the primordial class loader. All this information comes straight out of the method area.
  这些方法仅能够返回已装载类型的信息。getName()返回类型的全限定名,getSupperClass()返回类型的直接超类的Class实例。如果类型是java.lang.Object类或者是一个接口,它们都没有超类,getSupperClass()返回null。isInterface()判断该类型是否是接口,如果Class对象描述一个接口就返回true;如果它描述一个类则返回false,getInterfaces()返回一个Class对象数组,其中每个Class对象对应一个直接超接口,超接口在数组中以类型声明超接口的顺序出现。如果该类型没有直接超接口,getInterfaces()则返回一个长度为零的数组。getClassLoader()返回装载该类型的ClassLoader对象的引用,如果类型是由启动类装载器装载的,则返回null。所有这些信息都直接从方法区中获得。


JVM学习笔记-方法区示例与常量池解析(Method Area Use And Constant Pool Resolution)

As an example of how the Java Virtual Machine uses the information it stores in the method area, consider these classes:
  为了展示虚拟机如何使用方法区中的信息,我们举个例子,看下面这个类:

begin

// On CD-ROM in file jvm/ex2/Lava.java
class Lava {

    private int speed = 5; // 5 kilometers per hour

    void flow() {

    }

}



// On CD-ROM in file jvm/ex2/Volcano.java
class Volcano {

    public static void main(String[] args) {

        Lava lava = new Lava();

        lava.flow();

    }

}


end

The following paragraphs describe how an implementation might execute the first instruction in the bytecodes for the main() method of the Volcano application. Different implementations of the Java Virtual Machine can operate in very different ways. The following description illustrates one way–but not the only way–a Java Virtual Machine could execute the first instruction of Volcanoís main() method.
  下面的段落描述了某个实现是如何执行Volcano程序中main()方法的字节码中第一条指令的。不同的虚拟机实现可能会用完全不同的方法来操作,下 面描述的只是其中一种可能,但是并不是仅有的一种,下面看一下Java虚拟机是如何执行Volcano程序中main()方法的第一条指令的。

To run the Volcano application, you give the name “Volcano” to a Java Virtual Machine in an implementation-dependent manner. Given the name Volcano, the virtual machine finds and reads in file Volcano.class. It extracts the definition of class Volcano from the binary data in the imported class file and places the information into the method area. The virtual machine then invokes the main() method, by interpreting the bytecodes stored in the method area. As the virtual machine executes main(), it maintains a pointer to the constant pool (a data structure in the method area) for the current class (class Volcano).
  要运行Volcano程序,首先得以某种“依赖于实现的”方式告诉虚拟机“Volcano”这个名字。之后虚拟机将找到并读入相应的class文件 “Volcano.class”,然后他会从导入的class文件里的二进制数据中提取类型信息并放到方法区中。通过执行保存在方法区中的字节码,虚拟机开始执行main()方法,在执行时,他会一直持有指向当前类(Volcano类)的常量池(方法区中的一个数据结构)的指针。

Note that this Java Virtual Machine has already begun to execute the bytecodes for main() in class Volcano even though it hasnít yet loaded class Lava. Like many (probably most) implementations of the Java Virtual Machine, this implementation doesnít wait until all classes used by the application are loaded before it begins executing main(). It loads classes only as it needs them.
  注意,虚拟机开始执行Volcano类中main()方法的字节码的时候,尽管Lava类还没被装载,但是和大多数(也许是所有)虚拟机实现一样,他不会等到把程序中用到的所有类都装载后才开始运行程序。恰好相反,他只需在需要时才装载相应的类 。

main()’s first instruction tells the Java Virtual Machine to allocate enough memory for the class listed in constant pool entry one. The virtual machine uses its pointer into Volcanoís constant pool to look up entry one and finds a symbolic reference to class Lava. It checks the method area to see if Lava has already been loaded.
  main()的第一条指令告知虚拟机为列在常量池第一项的类分配足够的内存。所以虚拟机使用指向Volcano常量池的指针找到第一项,发现他是一个对Lava类的符号引用,然后他就检查方法区,看Lava类是否已经被装载了。

The symbolic reference is just a string giving the classís fully qualified name: “Lava”. Here you can see that the method area must be organized so a class can be located–as quickly as possible–given only the classís fully qualified name. Implementation designers can choose whatever algorithm and data structures best fit their needs–a hash table, a search tree, anything. This same mechanism can be used by the static forName() method of class Class, which returns a Class reference given a fully qualified name.
  这个符号引用仅仅是一个给出了类Lava的全限定名“Lava”的字符串。为了能让虚拟机尽可能快地从一个名称找到类,设计者应当选择最佳的数据结构和算法。这里可以采用各种方法,如散列表、搜索树等等。同样的算法也可以用于实现Class类的forName()方法,这个方法根据给定的全限定名返回 Class引用。

When the virtual machine discovers that it hasnít yet loaded a class named “Lava,” it proceeds to find and read in file Lava.class. It extracts the definition of class Lava from the imported binary data and places the information into the method area.
  当虚拟机发现还没有装载过名为“Lava”的类时,他就开始查找并装载文件“Lava.class”,并把从读入的二进制数据中提取的类型信息放在方法区中。

The Java Virtual Machine then replaces the symbolic reference in Volcanoís constant pool entry one, which is just the string “Lava”, with a pointer to the class data for Lava. If the virtual machine ever has to use Volcanoís constant pool entry one again, it wonít have to go through the relatively slow process of searching through the method area for class Lava given only a symbolic reference, the string “Lava”. It can just use the pointer to more quickly access the class data for Lava. This process of replacing symbolic references with direct references (in this case, a native pointer) is called constant pool resolution. The symbolic reference is resolved into a direct reference by searching through the method area until the referenced entity is found, loading new classes if necessary.
  紧接着,虚拟机以一个直接指向方法区Lava类数据的指针类替换常量池第一项(就是那个字符串“Lava”)—-以后就可以用这个指针来快速访问Lava类了。这个替换过程称为常量池解析 ,即把常量池中的符号引用替换为直接引用。这是通过在方法区中搜索被引用的元素实现的,在这期间可能又需要装载其他类。在这里,我们替换掉符号引用的“直接引用”是一个本地指针。

Finally, the virtual machine is ready to actually allocate memory for a new Lava object. Once again, the virtual machine consults the information stored in the method area. It uses the pointer (which was just put into Volcanoís constant pool entry one) to the Lava data (which was just imported into the method area) to find out how much heap space is required by a Lava object.
  终于,虚拟机转变为一个新的Lava对象分配内存。此时,它又需要方法区中的信息。还记得刚刚放到Volcano类常量池第一项的指针吗?现在虚拟机用它 来访问Lava类型信息(此前刚放到方法区中的),找到其中记录的这样一个信息:一个Lava对象需要分配多少堆空间。

A Java Virtual Machine can always determine the amount of memory required to represent an object by looking into the class data stored in the method area. The actual amount of heap space required by a particular object, however, is implementation-dependent. The internal representation of objects inside a Java Virtual Machine is another decision of implementation designers. Object representation is discussed in more detail later in this chapter.
  Java虚拟机总能够通过存储于方法区的类型信息来实现一个对象需要的内存,但是,某一个特定对象事实上需要多少内存,是跟特定实现相关的。对象在虚拟机内部的表示由实现的设计者来决定的。

Once the Java Virtual Machine has determined the amount of heap space required by a Lava object, it allocates that space on the heap and initializes the instance variable speed to zero, its default initial value. If class Lavaís superclass, Object, has any instance variables, those are also initialized to default initial values. (The details of initialization of both classes and objects are given in Chapter 7, “The Lifetime of a Class.”)
  当java虚拟机确定了一个Lava对象的大小后,它就在堆上分配这么大的空间,并把这个对象实例的变量speed初始化为默认初始值0.假如Lava类的超类Object也有实例变量,这也会在此时被初始化为相应的默认值。

The first instruction of main() completes by pushing a reference to the new Lava object onto the stack. A later instruction will use the reference to invoke Java code that initializes the speed variable to its proper initial value, five. Another instruction will use the reference to invoke the flow() method on the referenced Lava object.
  当把新生成的Lava对象的引用压到栈中,main()方法的第一条指令也完成了。接下来的指令通过这个引用调用Java代码(该代码把speed变量初始化为正确初始值5)。另外一条指令将用这个引用调用Lava对 象引用的flow()方法。


JVM学习笔记-堆(Heap)

  对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配①,但是随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换②优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。

  Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”(Garbage Collected Heap,幸好国内没翻译成“垃圾堆”)。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。如果从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。不过,无论如何划分,都与存放内容无关,无论哪个区域,存储的都仍然是对象实例,进一步划分的目的是为了更好地回收内存,或者更快地分配内存。在本章中,我们仅仅针对内存区域的作用进行讨论,Java堆中的上述各个区域的分配和回收等细节将会是下一章的主题。
  根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

以上描述截取自:
  《深入理解Java虚拟机:JVM高级特性与最佳实践》 作者: 周志明

Whenever a class instance or array is created in a running Java application, the memory for the new object is allocated from a single heap. As there is only one heap inside a Java Virtual Machine instance, all threads share it. Because a Java application runs inside its “own” exclusive Java Virtual Machine instance, there is a separate heap for every individual running application. There is no way two different Java applications could trample on each otherís heap data. Two different threads of the same application, however, could trample on each otherís heap data. This is why you must be concerned about proper synchronization of multi-threaded access to objects (heap data) in your Java programs.
The Java Virtual Machine has an instruction that allocates memory on the heap for a new object, but has no instruction for freeing that memory. Just as you canít explicitly free an object in Java source code, you canít explicitly free an object in Java bytecodes. The virtual machine itself is responsible for deciding whether and when to free memory occupied by objects that are no longer referenced by the running application. Usually, a Java Virtual Machine implementation uses a garbage collector to manage the heap.

  java 程序在运行时创建的 所有类实例或数组都放在同一个堆中 。而 一个Java 虚拟机实例 只存在一个堆内存空间 ,因此所有的线程均 共享这个堆 。又由于一个java 程序独占一个Java 虚拟机实例,因而每个java 程序都有他自己的堆内存—- 他们之间不会相互影响。但是同 一个Java 程序的多线程却共享着同一个堆空间 ,在这种情况下,就得考虑多线程访问对象(堆数据)的同步问题了。

  Java虚拟机有一条在堆中分配新对象的指令,但是没有释放内存的指令。java虚拟机的垃圾回收机制负责回收没有被使用的内存。
只要有一个对象引用,虚拟机就必须能够快速定位对象类型的数据。另外它也必须通过该对象引用访问相应的类数据(存储于方法区中的类型信息)。因此在对象中通常会有一个指向方法区的指针。

以上描述截取自:
  《Inside the Java Virtual Machine 2nd Edition》 作者:Bill Venners


JVM学习笔记-程序计数器(The Program Counter)

  程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

  由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

  如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

以上描述截取自:
  《深入理解Java虚拟机:JVM高级特性与最佳实践》 作者: 周志明

Each thread of a running program has its own pc register, or program counter, which is created when the thread is started. The pc register is one word in size, so it can hold both a native pointer and a returnValue. As a thread executes a Java method, the pc register contains the address of the current instruction being executed by the thread. An “address” can be a native pointer or an offset from the beginning of a method’s bytecodes. If a thread is executing a native method, the value of the pc register is undefined.
  对于一个运行中的Java程序而言,其中的每一个线程都有它自己的PC(程序计数器),在线程启动时创建。大小是一个字长。因此它既能持有一个本地指针,也能够持有一个returnAddress。当线程执行某个Java方法时,PC的内容总是下一条将被指向指令的“地址”。这里的“地址”可以是一个本地指针,也可以是在方法字节码中相对于该方法起始指令的偏移量。如果该线程正在执行一个本地方法,那么此时PC寄存器的值为”undefined”。

以上描述截取自:
  《Inside the Java Virtual Machine 2nd Edition》 作者:Bill Venners


JVM学习笔记-栈(Stack)

  与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame①)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

  经常有人把Java内存区分为堆内存(Heap)和栈内存(Stack),这种分法比较粗糙,Java内存区域的划分实际上远比这复杂。这种划分方式的流行只能说明大多数程序员最关注的、与对象内存分配关系最密切的内存区域是这两块。其中所指的“堆”在后面会专门讲述,而所指的“栈”就是现在讲的虚拟机栈,或者说是虚拟机栈中的局部变量表部分。

  局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。

  其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

以上描述截取自:
  《深入理解Java虚拟机:JVM高级特性与最佳实践》 作者: 周志明

When a new thread is launched, the Java Virtual Machine creates a new Java stack for the thread. As mentioned earlier, a Java stack stores a threadís state in discrete frames. The Java Virtual Machine only performs two operations directly on Java Stacks: it pushes and pops frames.
  每当启动一个新线程时,Java虚拟机都会为它分配一个Java栈。前面我们曾经提到,Java栈以帧为单位保存线程的运行状态。虚拟机只会直接对Java栈执行两种操作:以帧为单位的压栈或出栈。

The method that is currently being executed by a thread is the thread’s current method. The stack frame for the current method is the current frame. The class in which the current method is defined is called the current class, and the current classís constant pool is the current constant pool. As it executes a method, the Java Virtual Machine keeps track of the current class and current constant pool. When the virtual machine encounters instructions that operate on data stored in the stack frame, it performs those operations on the current frame.
  某个线程正在执行的方法被称为该线程的当前方法,当前方法使用的栈帧成为当前帧,当前方法所属的类成为当前类,当前类的常量池成为当前常量池。在线程执行一个方法时,它会跟踪当前类和当前常量池。此外,当虚拟机遇到栈内操作指令时,它对当前帧内数据执行操作。

When a thread invokes a Java method, the virtual machine creates and pushes a new frame onto the threadís Java stack. This new frame then becomes the current frame. As the method executes, it uses the frame to store parameters, local variables, intermediate computations, and other data.
  每当线程调用一个Java方法时,虚拟机都会在该线程的Java栈中压入一个新帧。而这个新帧自然就成为了当前帧。在执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等等数据。

A method can complete in either of two ways. If a method completes by returning, it is said to have normal completion. If it completes by throwing an exception, it is said to have abrupt completion. When a method completes, whether normally or abruptly, the Java Virtual Machine pops and discards the method’s stack frame. The frame for the previous method then becomes the current frame.
  Java方法可以以两种方式完成。一种通过return返回的,称为正常返回;一种是通过抛出异常而异常中止的。不管以哪种方式返回,虚拟机都会将当前帧弹出Java栈然后释放掉,这样上一个方法的帧就成为当前帧了。

All the data on a threadís Java stack is private to that thread. There is no way for a thread to access or alter the Java stack of another thread. Because of this, you need never worry about synchronizing multi-threaded access to local variables in your Java programs. When a thread invokes a method, the methodís local variables are stored in a frame on the invoking threadís Java stack. Only one thread can ever access those local variables: the thread that invoked the method.
  Java栈上的所有数据都是此线程私有的。任何线程都不能访问另一个线程的栈数据,因此我们不需要考虑多线程情况下栈数据的访问同步问题。当一个线程调用一个方法时,方法的局部变量保存在调用线程Java栈的帧中。只有一个线程总是访问哪些局部变量,即调用方法的线程。

Like the method area and heap, the Java stack and stack frames need not be contiguous in memory. Frames could be allocated on a contiguous stack, or they could be allocated on a heap, or some combination of both. The actual data structures used to represent the Java stack and stack frames is a decision of implementation designers. Implementations may allow users or programmers to specify an initial size for Java stacks, as well as a maximum or minimum size.
  像方法区和堆一样,Java栈和帧在内存中也不必是连续的。帧可以分步在连续的栈里,也可以分步在堆里,或者二者兼而有之。表示Java栈和栈帧的实际数据结构由虚拟机的实现者决定。某些实现允许用户指定Java栈的初始大小和最大最小值。

以上描述截取自:
  《Inside the Java Virtual Machine 2nd Edition》 作者:Bill Venners


JVM学习笔记-栈帧(The Stack Frame)

The stack frame has three parts: local variables, operand stack, and frame data. The sizes of the local variables and operand stack, which are measured in words, depend upon the needs of each individual method. These sizes are determined at compile time and included in the class file data for each method. The size of the frame data is implementation dependent.
栈帧由两部分组成:局部变量区,操作数栈和帧数据区。局部变量区和操作数栈的大小要视对应的方法而定,它们是按字长计算的。编译器在编译时就确定了这些值并放在class文件中。而帧数据区的大小依赖于具体的实现。

When the Java Virtual Machine invokes a Java method, it checks the class data to determine the number of words required by the method in the local variables and operand stack. It creates a stack frame of the proper size for the method and pushes it onto the Java stack.
  当虚拟机调用一个Java方法时,它从对应类的类型信息中得到此方法的局部变量区和操作数栈的大小,并据此分配栈帧内存,然后压入Java栈中。


JVM学习笔记-局部变量区(Local Variables)

The local variables section of the Java stack frame is organized as a zero-based array of words. Instructions that use a value from the local variables section provide an index into the zero-based array. Values of type int, float, reference, and returnValue occupy one entry in the local variables array. Values of type byte, short, and char are converted to int before being stored into the local variables. Values of type long and double occupy two consecutive entries in the array.
  Java栈帧的局部变量区被组织为一个以字长为单位,从0开始计数的数组。字节码指令通过从0开始的索引来使用其中的数据。类型为int、float、reference和returnAddress的值在数组中只占据一项,而类型为byte、short和char的值在存入数组前都被转换为int值,因而同样占据一项。但是类型为long和double的值在数组中却占连续的两项。

To refer to a long or double in the local variables, instructions provide the index of the first of the two consecutive entries occupied by the value. For example, if a long occupies array entries three and four, instructions would refer to that long by index three. All values in the local variables are word-aligned. Dual-entry longs and doubles can start at any index.
  在访问局部变量中的long和double值的时候,指令只需指出连续两项中第一项的索引值。例如某个long值占据第三四项,那么指令会取索引为3的long值。局部变量区的所有值都是字对齐的,long和double这样占据两项数组元素的值同样可以起始于任何索引。

The local variables section contains a method’s parameters and local variables. Compilers place the parameters into the local variable array first, in the order in which they are declared. Figure 5-9 shows the local variables section for the following two methods:

  局部变量区包含对应方法的参数和局部变量。编译器首先按声明的顺序把这些参数放入局部变量数组。

图5-9描绘了下面两个方法的局部变量区。

begin

// On CD-ROM in file jvm/ex3/Example3a.java
class Example3a {

    public static int runClassMethod(int i, long l, float f,

        double d, Object o, byte b) {

        return 0;

    }

    public int runInstanceMethod(char c, double d, short s,

        boolean b) {

        return 0;

    }

}

end

Figure 5-9
Note that Figure 5-9 shows that the first parameter in the local variables for runInstanceMethod() is of type reference, even though no such parameter appears in the source code. This is the hidden this reference passed to every instance method. Instance methods use this reference to access the instance data of the object upon which they were invoked. As you can see by looking at the local variables for runClassMethod() in Figure 5-9, class methods do not receive a hidden this. Class methods are not invoked on objects. You can’t directly access a class’s instance variables from a class method, because there is no instance associated with the method invocation.

  注意,在图5-9中显示的方法runInstanceMethod()中,局部变量中第一个参数是一个reference(引用)类型,尽管在方法源代码中没有显式声明这个参数,但这个参数this对于任何一个实例方法都是隐含加入的,它用来表示调用该方法的对象本身,与此相反,方法runClassMethod()中就没有这个隐含的this变量,因为它是一个类方法。类方法只与类相关,而与具体的对象无关,不能直接通过类方法访问类实例的变量,因为在方法调用的时候没有关联到一个具体实例。

Note also that types byte, short, char, and boolean in the source code become ints in the local variables. This is also true of the operand stack. As mentioned earlier, the boolean type is not supported directly by the Java Virtual Machine. The Java compiler always uses ints to represent boolean values in the local variables or operand stack. Data types byte, short, and char, however, are supported directly by the Java Virtual Machine. These can be stored on the heap as instance variables or array elements, or in the method area as class variables. When placed into local variables or the operand stack, however, values of type byte, short, and char are converted into ints. They are manipulated as ints while on the stack frame, then converted back into byte, short, or char when stored back into heap or method area.
  我们注意到,在源代码中的byte,short,char,和boolean在局部变量区都被转换成了int,在操作数栈中也一样,前面我们曾经说过,虚拟机并不直接支持boolean类型,因此Java编译器总是用int来表示boolean,但Java虚拟机对byte,short和char是直接支持的,这些类型的值可以作为实例变量或者数组元素存储在局部变量区,也可以作为类变量存储在方法区中,但在局部变量区和操作数栈中都会被转换成int类型的值,它们在栈帧中的时候都是当作int来进行处理的,只有当它被存回堆或方法区时,才会转换回原来的类型。

Also note that Object o is passed as a reference to runClassMethod(). In Java, all objects are passed by reference. As all objects are stored on the heap, you will never find an image of an object in the local variables or operand stack, only object references.
  同样需要注意的是作为runClassMethod()的引用被传递的参数Objecto.在Java中,所有的变量都按引用传递,并且都存储在堆中,永远都不会在局部变量区或操作数栈中发现对象的拷贝,只会有对象引用。

Aside from a methodís parameters, which compilers must place into the local variables array first and in order of declaration, Java compilers can arrange the local variables array as they wish. Compilers can place the methodís local variables into the array in any order, and they can use the same array entry for more than one local variable. For example, if two local variables have limited scopes that donít overlap, such as the i and j local variables in Example3b below, compilers are free to use the same array entry for both variables. During the first half of the method, before j comes into scope, entry zero could be used for i. During the second half of the method, after i has gone out of scope, entry zero could be used for j.
  除了Java方法的参数(编译器首先严格按照它们的声明顺序放到局部变量数组中,而对于真正的局部变量,它可以任意决定放置顺序,甚至可以用一个索引指代两个局部变量)— 比如当两个局部变量的作用域不重叠时,像下面Example3b中的局部变量i和j就是这种情况:在方法的前半段,在j开始生效前,0号索引的入口可以被用来代表i在方法的后半段,i已经超过了有效作用域,0号入口就可以用来表示j了。

begin

// On CD-ROM in file jvm/ex3/Example3b.java
class Example3b {

    public static void runtwoLoops() {

        for (int i = 0; i < 10; ++i) {

            System.out.println(i);

        }

        for (int j = 9; j = 0; --j) {

            System.out.println(j);

        }

    }

}

end

As with all the other runtime memory areas, implementation designers can use whatever data structures they deem most appropriate to represent the local variables. The Java Virtual Machine specification does not indicate how longs and doubles should be split across the two array entries they occupy. Implementations that use a word size of 64 bits could, for example, store the entire long or double in the lower of the two consecutive entries, leaving the higher entry unused.
  和其他运行时内存区一样,虚拟机的实现者可以为局部变量区设计任意的数据结构。比如对于怎样把long和double类型的值存储到两个数组项中,Java虚拟机规范没有指定。假如某个虚拟机实现的字长为64位,这时就可以把整个long或double数据放在数组中相邻两数组项的低项内,而使高项保持为空。


JVM学习笔记-操作数栈(Operand Stack)

Like the local variables, the operand stack is organized as an array of words. But unlike the local variables, which are accessed via array indices, the operand stack is accessed by pushing and popping values. If an instruction pushes a value onto the operand stack, a later instruction can pop and use that value.
  和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。

The virtual machine stores the same data types in the operand stack that it stores in the local variables: int, long, float, double, reference, and returnType. It converts values of type byte, short, and char to int before pushing them onto the operand stack.
  虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double、reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。

Other than the program counter, which canít be directly accessed by instructions, the Java Virtual Machine has no registers. The Java Virtual Machine is stack-based rather than register-based because its instructions take their operands from the operand stack rather than from registers. Instructions can also take operands from other places, such as immediately following the opcode (the byte representing the instruction) in the bytecode stream, or from the constant pool. The Java Virtual Machine instruction set’s main focus of attention, however, is the operand stack.
  不同于程序计数器,Java虚拟机没有寄存器,程序计数器也无法被程序指令直接访问。Java虚拟机的指令是从操作数栈中而不是从寄存器中取得操作数的,因此它的运行方式是基于栈的而不是基于寄存器的。虽然指令也可以从其他地方取得操作数,比如从字节码流中跟随在操作码(代表指令的字节)之后的字节中或从常量池中,但是主要还是从操作数栈中获得操作数。

The Java Virtual Machine uses the operand stack as a work space. Many instructions pop values from the operand stack, operate on them, and push the result. For example, the iadd instruction adds two integers by popping two ints off the top of the operand stack, adding them, and pushing the int result. Here is how a Java Virtual Machine would add two local variables that contain ints and store the int result in a third local variable:
  虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中,看看下面的示例,它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的:

begin

iload_0    // push the int in local variable 0 onto the stack

iload_1    // push the int in local variable 1 onto the stack

iadd       // pop two ints, add them, push result

istore_2   // pop int, store into local variable 2

end

In this sequence of bytecodes, the first two instructions, iload_0 and iload_1, push the ints stored in local variable positions zero and one onto the operand stack. The iadd instruction pops those two int values, adds them, and pushes the int result back onto the operand stack. The fourth instruction, istore_2, pops the result of the add off the top of the operand stack and stores it into local variable position two. In Figure 5-10, you can see a graphical depiction of the state of the local variables and operand stack while executing the above instructions. In this figure, unused slots of the local variables and operand stack are left blank.
  在这个字节码序列里,前两个指令iload_0和iload_1将存储在局部变量中索引为0和1的整数压入操作数栈中,其后iadd指令从操作数栈中弹出那两个整数相加,再将结果压入操作数栈。第四条指令istore_2则从操作数栈中弹出结果,并把它存储到局部变量区索引为2的位置。图5-10详细表述了这个过程中局部变量和操作数栈的状态变化,图中没有使用的局部变量区和操作数栈区域以空白表示。


JVM学习笔记-帧数据区(Frame Data)

In addition to the local variables and operand stack, the Java stack frame includes data to support constant pool resolution, normal method return, and exception dispatch. This data is stored in the frame data portion of the Java stack frame.
  除了局部变量区和操作数栈外,Java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些信息都保存在Java栈帧的帧数据区中。

Many instructions in the Java Virtual Machine’s instruction set refer to entries in the constant pool. Some instructions merely push constant values of type int, long, float, double, or String from the constant pool onto the operand stack. Some instructions use constant pool entries to refer to classes or arrays to instantiate, fields to access, or methods to invoke. Other instructions determine whether a particular object is a descendant of a particular class or interface specified by a constant pool entry.

  Java虚拟机中的大多数指令都涉及到常量池入口。有些指令仅仅是从常量池中取出数据然后压入Java栈(这些数据的类型包括int,long、float、double和String);还有些指令使用常量池的数据来指示要实例化的类或数组,要访问的字段,或要调用的方法;还有些指令需要常量池中的数据才能确定某个对象是否属于某个类或实现了某个接口。

Whenever the Java Virtual Machine encounters any of the instructions that refer to an entry in the constant pool, it uses the frame dataís pointer to the constant pool to access that information. As mentioned earlier, references to types, fields, and methods in the constant pool are initially symbolic. When the virtual machine looks up a constant pool entry that refers to a class, interface, field, or method, that reference may still be symbolic. If so, the virtual machine must resolve the reference at that time.

  每当虚拟机要执行某个需要用到常量池数据的指令时,它都会通过帧数据区中指向常量池的指针来访问它。以前讲过,常量池中对类型、字段和方法的引用在开始时都是符号。当虚拟机在常量池中搜索的时候,如果遇到指向类、接口、字段或者方法的入口,假若它们仍然是符号,虚拟机那时才会(也必须)进行解析。

Aside from constant pool resolution, the frame data must assist the virtual machine in processing a normal or abrupt method completion. If a method completes normally (by returning), the virtual machine must restore the stack frame of the invoking method. It must set the pc register to point to the instruction in the invoking method that follows the instruction that invoked the completing method. If the completing method returns a value, the virtual machine must push that value onto the operand stack of the invoking method.

  除了用于常量池的解析外,帧数据区还要帮助虚拟机处理Java方法的正常结束或异常中止。如果是通过return正常结束,虚拟机必须恢复发起调用的方法的栈帧,包括设置PC寄存器指向发起调用的方法中的指令——即紧跟着调用了完成方法的指令的下一个指令。假如方法有返回值,虚拟机必须将它压入到发起调用的方法的操作数栈。

The frame data must also contain some kind of reference to the method’s exception table, which the virtual machine uses to process any exceptions thrown during the course of execution of the method. An exception table, which is described in detail in Chapter 17, “Exceptions,” defines ranges within the bytecodes of a method that are protected by catch clauses. Each entry in an exception table gives a starting and ending position of the range protected by a catch clause, an index into the constant pool that gives the exception class being caught, and a starting position of the catch clauseís code.
  为了处理Java方法执行期间的异常退出情况,帧数据区还必须保存一个对此方法异常表的引用。异常表定义了在这个方法的字节码中受catch子句保护的范围,异常表中的每一项都有一个被catch子句保护的代码的起始和结束位置(译者注:即try子句内部的代码),可能被catch的异常类在常量池中的索引值,以及catch子句内的代码开始的位置。

When a method throws an exception, the Java Virtual Machine uses the exception table referred to by the frame data to determine how to handle the exception. If the virtual machine finds a matching catch clause in the method’s exception table, it transfers control to the beginning of that catch clause. If the virtual machine doesnít find a matching catch clause, the method completes abnormally. The virtual machine uses the information in the frame data to restore the invoking methodís frame. It then rethrows the same exception in the context of the invoking method.
  当某个方法抛出异常时,虚拟机根据帧数据区对应的异常表来决定如何处理。如果在异常表中找到了匹配的catch子句,就会把控制权转交给catch子句内的代码。如果没有发现,方法会立即异常中止。然后虚拟机使用帧数据区的信息回复发起调用的方法的帧,然后在发起调用的方法的上下文中重新抛出同样的异常。

In addition to data to support constant pool resolution, normal method return, and exception dispatch, the stack frame may also include other information that is implementation dependent, such as data to support debugging.
  除了上述信息(支持常量池解析、正常方法返回和异常派发的数据)外,虚拟机的实现者也可以将其他信息放入帧数据区,如用于调试的数据等。


JVM学习笔记-本地方法栈(Native Method Stacks)

  本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

以上描述截取自:
  《深入理解Java虚拟机:JVM高级特性与最佳实践》 作者: 周志明

In addition to all the runtime data areas defined by the Java Virtual Machine specification and described above, a running Java application may use other data areas created by or for native methods. When a thread invokes a native method, it enters a new world in which the structures and security restrictions of the Java Virtual Machine no longer hamper its freedom. A native method can likely access the runtime data areas of the virtual machine (it depends upon the native method interface), but can also do anything else it wants. It may use registers inside the native processor, allocate memory on any number of native heaps, or use any kind of stack.
   前面提到的所有运行时数据区都是Java虚拟机规范中明确定义的,除此之外,对于一个运行中的Java程序而言,他还可能会用到一些本地方法相关的数据区。当某个线程调用一个本地方法时,他就进入了一个全新的并且不再受虚拟机限制的世界 ,本地方法可以通过本地方法接口 来访问虚拟机得运行时数据区,但不止于此,他还可以做任何他想做的事情。比如,他甚至可以直接使用本地处理器中的寄存器,或者直接从本地内存的堆中分配任意数量的内存等等。总之,他和虚拟机拥有同样的权限(或者说能力)。

Native methods are inherently implementation dependent. Implementation designers are free to decide what mechanisms they will use to enable a Java application running on their implementation to invoke native methods.
  本地方法本质上是依赖于实现的,虚拟机实现的设计者可以自由地决定使用怎样的机制来让Java程序调用本地方法。

Any native method interface will use some kind of native method stack. When a thread invokes a Java method, the virtual machine creates a new frame and pushes it onto the Java stack. When a thread invokes a native method, however, that thread leaves the Java stack behind. Instead of pushing a new frame onto the threadís Java stack, the Java Virtual Machine will simply dynamically link to and directly invoke the native method. One way to think of it is that the Java Virtual Machine is dynamically extending itself with native code. It is as if the Java Virtual Machine implementation is just calling another (dynamically linked) method within itself, at the behest of the running Java program.
  任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入java栈。然而当他调用的是本地方法时,虚拟机会保持Java栈不变 ,不再在线程的java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。可以把这看做是虚拟机利用本地方法来动态扩展自己 。就如同Java虚拟机的实现在按照其中运行的Java程序的吩咐,调用属于虚拟机内部的另一个(动态连接的)方法。

If an implementationís native method interface uses a C-linkage model, then the native method stacks are C stacks. When a C program invokes a C function, the stack operates in a certain way. The arguments to the function are pushed onto the stack in a certain order. The return value is passed back to the invoking function in a certain way. This would be the behavior of the of native method stacks in that implementation.
  如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那个他的本地方法栈就是C栈。我们知道,当C程序调用一个C函数时,其栈操作都是确定的。传递 给该函数的参数已某个确定的顺序压入栈,他的返回值也以确定的方式传回调用者。同样,这就是改虚拟机实现中本地方法栈的行为。

A native method interface will likely (once again, it is up to the designers to decide) be able to call back into the Java Virtual Machine and invoke a Java method. In this case, the thread leaves the native method stack and enters another Java stack.
  很可能本地方法接口需要回调Java虚拟机中的Java方法(这也是由设计者决定的),在这种情形下,该线程会保存本地方法栈的状态并进入到另一个Java栈。

Figure 5-13 shows a graphical depiction of a thread that invokes a native method that calls back into the virtual machine to invoke another Java method. This figure shows the full picture of what a thread can expect inside the Java Virtual Machine. A thread may spend its entire lifetime executing Java methods, working with frames on its Java stack. Or, it may jump back and forth between the Java stack and native method stacks.
  图5-13描绘了这种情况,就是当一个线程调用一个本地方法时,本地方法又回调虚拟机中的另一个Java方法。这幅图展示了java虚拟机内部线程运行的全景 图。一个线程可能在整个生命周期中都执行Java方法,操作他的Java栈;或者他可能毫无障碍地在Java栈和本地方法栈之间跳转。

As depicted in Figure 5-13, a thread first invoked two Java methods, the second of which invoked a native method. This act caused the virtual machine to use a native method stack. In this figure, the native method stack is shown as a finite amount of contiguous memory space. Assume it is a C stack. The stack area used by each C-linkage function is shown in gray and bounded by a dashed line. The first C-linkage function, which was invoked as a native method, invoked another C-linkage function. The second C-linkage function invoked a Java method through the native method interface. This Java method invoked another Java method, which is the current method shown in the figure.
  上图所示,该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。图中的本地方法栈显示为 一个连续的内存空间。假设这是一个C语言栈,期间有两个C函数,他们都以包围在虚线中的灰色块表示。第一个C函数被第二个Java方法当做本地方法调用, 而这个C函数又调用了第二个C函数。之后第二个C函数被第二个Java方法当做本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过 本地方法接口回调了一个Java方法(第三个Java方法)。最终这个Java方法又调用了一个Java方法(他成为图中的当前方法)。

As with the other runtime memory areas, the memory they occupied by native method stacks need not be of a fixed size. It can expand and contract as needed by the running application. Implementations may allow users or programmers to specify an initial size for the method area, as well as a maximum or minimum size.
  就像其他运行时内存区一样,本地方法栈占用的内存区也不必是固定大小的,他可以根据需要动态扩展或者收缩。某些是实现也允许用户或者程序员指定该内存区的初始大小以及最大,最小值。


JVM学习笔记-特殊字符串(Special Strings)

Special Strings 特殊字符串

The symbolic references contained in the constant pool involve three special kinds of strings: fully qualified names, simple names, and descriptors. All symbolic references include the fully qualified name of a class or interface. Symbolic references to fields include a simple field name and field descriptor in addition to a fully qualified type name. Symbolic references to methods include a simple method name and method descriptor in addition to a fully qualified name.
  常量池中容纳的符号引用包括三种特殊的字符串:全限定名、简单名称和描述符。所有的符号引用都包括类或者接口的全限定名。字段的符号引用除了全限定类型名之外,还包括简单字段名和字段描述符。方法的符号引用除了全限定类型名之外,还包括简单方法名和方法描述符。

The same special strings are used to describe the class or interface that is defined by the class file. The class or interface name, the superclass name (if any), and the names of any superinterfaces are all given as fully qualified names. For each field declared by the class or interface, the constant pool contains a simple name and field descriptor. For each method declared by the class or interface, the constant pool contains a simple name and method descriptor.
  在符号引用中使用的特殊字符串也同样用来描述被class文件定义的类或者接口。例如,定义过的类或者接口会有一个全限定名。对于每一个在类或者接口中声明的字段,常量池中都会有一个简单名称和字段描述符。对于每一个在类或者接口中声明的方法,常量池中都会有一个简单名称和方法描述符。

Fully Qualified Names 全限定名

Whenever constant pool entries refer to classes and interfaces, they give the fully qualified name of the class or interface. In the class file, fully qualified names have their dots replaced with slashes. For example, the representation of the fully qualified name of java.lang.Object in the class file is java/lang/Object. The fully qualified name of java.util.Hashtable in the class file is java/util/Hashtable.
  当常量池入口指向类或者接口时,它们给出该类或者接口的全限定名。在class文件中,全限定名中的点用斜线取代了。例如,在class文件中,java.lang.Object的全限定名表示为java/lang/Object;在class文件中,java.util.Hashtable的全限定名表示为java/util/Hashtable。

Simple Names 简单名称

The names of fields and methods appear in constant pool entries as simple (not fully qualified) names. For example, a constant pool entry that refers to the String toString() method of class java.lang.Object would give its method name as “toString”. A constant pool entry that refers to the java.io.PrintStream out field of class java.lang.System would specify the field name simply as “out”.
  字段名和方法名以简单名称(非全限定名)形式出现在常量池入口中。例如,一个指向类java.lang.Object所属方法StringtoString()的常量池入口有一个形如“toStirng”的方法名。一个指向类java.lang.System所属字段java.io.PrintStream.out的常量池入口有个以形如”out”的字段名。

Descriptors 描述符

Symbolic references to fields and methods include a descriptor string in addition to a fully qualified class or interface name and a simple field or method name. A field descriptor gives the fieldís type. A method descriptor gives the methodís return type and the number and types of the methodís parameters.
  除了类(或接口)的全限定名和简单字段(或方法)名,指向字段和方法的符号引用还包括描述符字符串。字段的描述符给出了字段的类型;方法描述符给出了方法的返回值和方法参数的数量、类型以及顺序。

Field and method descriptors are defined by the context free grammar shown below. Nonterminals of this grammar, such as FieldType, are shown in italic font. Terminals, such as B or V, are shown in fixed width font. The asterisk character (*) stands for zero or more occurrences of the item that precedes it placed side by side (with no intervening white space).

  字段和方法的描述符由如下所示的上下文无关语法定义。该语法中非终结符号用斜体字标出,如FieldType;终结符号使用等宽度字体标出,如B或V;星号代表紧接在它前面的符号(中间没有空格)将会出现0次或者多次。

FieldDescriptor:

FieldType

ComponentType:

FieldType

FieldType:

BaseType
ObjectType
ArrayType

BaseType:

B
C
D
F
I
J
S
Z

ObjectType:

L;

ArrayType:

[ ComponentType

ParameterDescriptor:

FieldType

MethodDescriptor:

( ParameterDescriptor* ) ReturnDescriptor

ReturnDescriptor:

FieldType
V
The meaning of each of the BaseType terminals is shown in Table 6-5. The V terminal represents methods that return void.

Table 6-5. BaseType terminals

TerminalType
Bbyte
Cchar
Ddouble
Ffloat
Iint
Jlong
Sshort
Zboolean

Some examples of field descriptors are shown in Table 6-6.
Table 6-6. Examples of field descriptors

DescriptorField Declaration
Iint i;
[[Jlong[][] windingRoad;
[Ljava/lang/Object;java.lang.Object[] stuff;
Ljava/util/Hashtable;java.util.Hashtable ht;
[[[Zboolean[][][] isReady;

Some examples of method descriptors are shown in Table 6-7. Note that method descriptors don’t include the hidden this parameter passed as the first argument to all instance methods.
Table 6-7. Examples of method descriptors

DescriptorMethod Declaration
()Iint getSize();
()Ljava/lang/String;String toString();
([Ljava/lang/String;)Vvoid main(String[] args);
()Vvoid wait()
(JI)Vvoid wait(long timeout, int nanos)
(ZILjava/lang/String;II)Zboolean regionMatches(boolean ignoreCase, int toOffset, String other, int ooffset, int len);
([BII)Iint read(byte[] b, int off, int len);

JVM学习笔记-Class文件(Class File)

What is a Java Class File?
The Java class file is a precisely defined binary file format for Java programs. Each Java class file represents a complete description of one Java class or interface. There is no way to put more than one class or interface into a single class file. The precise definition of the class file format ensures that any Java class file can be loaded and correctly interpreted by any Java Virtual Machine, no matter what system produced the class file or what system hosts the virtual machine.
  Java class文件是对Java程序二进制文件格式的精确定义。每一个Javaclass文件都对一个Java类或者Java接口做出了全面描述。一个class文件中只能包含一个类或者接口。无论Java class文件在何种系统上产生,无论虚拟机在何种系统上运行,对Java class文件的精确定义使得所有Java虚拟机都能够正确地读取和解释所有Javaclass文件。

Although the class file is related to the Java language architecturally, it is not inextricably linked to the Java language. As shown in Figure 6-1, you could write programs in other languages and compile them to class files, or you could compile your Java programs to a different binary file format. Nevertheless, most Java programmers will likely use the class file as the primary vehicle for delivering their programs to Java Virtual Machines.
  尽管class文件与Java语言结构相关,但它并不一定必须与Java语言相关。如图6-1所示,可以使用其他语言来编写程序,然后将其编译为class文件,或者把Java程序编译为另一种不同的二进制文件格式。实际上,Javaclass文件的形式能够表示Java源代码中无法表达的有效程序,然而,绝大多数Java开发者几乎都会选择使用class文件作为程序传给虚拟机的首要方式。

As mentioned in earlier chapters, the Java class file is a binary stream of 8-bit bytes. Data items are stored sequentially in the class file with no padding between adjacent items. The lack of padding helps keep class files compact. Items that occupy more than one byte are split up into several consecutive bytes that appear in big-endian (higher bytes first) order.
  如前所述,Java class文件是8位字节的二进制流。数据项按顺序存储在class文件中,相邻的项之间没有任何间隔,这样可以使class文件紧凑。占据多个字节空间的项按照高位在前的顺序分为几个连续的字节存放。

Just as your Java classes can contain varying numbers of fields, methods, method parameters, local variables, and so on, the Java class file can contain many items that vary in size or number from one class file to another. In the class file, the size or length of a variable-length item precedes the actual data for the item. This allows class file streams to be parsed from beginning to end, reading in the size of an item first followed by the item data.
  和Java的类可以包含多个不同的字段,方法,方法参数,局部变量等一样,Java class文件也能够包含许多不同大小的项。在class文件中,可变长度项的大小和长度位于其实际数据之前。这个特性使得calss文件流可以从头到尾被顺序解析,首先读出项的大小,然后读出项的数据。
What’s in a Class File?
The Java class file contains everything a Java Virtual Machine needs to know about one Java class or interface. The remainder of this chapter describes the class file format using tables. Each table has a name and shows an ordered list of items that can appear in a class file. Items appear in the table in the order in which they appear in the class file. Each item has a type, a name, and a count. The type is either a table name or one of the “primitive types” shown in Table 6-1. All values stored in items of type u2, u4, and u8 appear in the class file in big-endian order.
  Java class文件中包含了Java虚拟机所知道的,关于类或者接口的所有信息。本章剩余部分将用表格形式描述class文件格式。每个表格都有一个名字,每个表格都显示了在class文件中出现的项的有序列表。这些项按照出现在class文件中的顺序在表中列出。每一项都包括类型、名称及该项的数量。类型或者未表名,或者为如表6-1所示的“基本类型”。所有存储在类型u2,u4和u8项中的值,在class文件中以高位在前的形式出现。

Table 6-1. Class file “primitive types”

--
u1a single unsigned byte
u2two unsigned bytes
u4four unsigned bytes
u8eight unsigned bytes

The major components of the class file, in their order of appearance in the class file, are shown in Table 6-2. Each of these components is described in more detail below.
  可变长度的ClassFile表中的项,如表6-2所示,按照它们在class文件中出现的顺序列出了主要部分。

Table 6-2. Format of a ClassFile Table

TypeNameCount
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count - 1
u2access_flags1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_count

JVM学习笔记-字段(Fields)

Each field (class variable and instance variable) declared in a class or interface is described by a field_info table in the class file. The format of the field_info table is shown in Table 6-20.
  在类或者接口中声明的每一个字段(类变量或者实例变量)都由class文件中的一个名为field_info的可变长度的表进行描述。在一个class文件中,不会存在两个具有相同名字和描述符的字段。需要注意的是尽管在Java程序设计语言中不会有两个相同名字的字段存在于同一个类或者接口中,但一个class文件中的两个字段可以拥有同一个名字——只要它们的描述符不同。换句话说,尽管在程序设计语言中无法在同一个类或者接口中定义两个具有同样名字和不同类别的字段,但是两个这样的字段却可以同时合法地出现在一个Javaclass文件中。表6-20中列出了field_info表的格式。

Table 6-20. Format of a field_info table

TypeNameCount
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

Field_info表中各项如下所示:
access_flags
The modifiers used in declaring the field are placed into the fieldís access_flags item. Table 6-21 shows the bits used by each flag.
  声明字段时使用的修饰符存放在字段的access_flags项中。表6-21列出了各个标志所使用的位。

Table 6-21. Flags in the access_flags item of field_info tables

Flag NameValueMeaning ifSet Set By
ACC_PUBLIC0x0001Field is publicClasses and interfaces
ACC_PRIVATE0x0002Field is privateClasses only
ACC_PROTECTED0x0004Field is protectedClasses only
ACC_STATIC0x0008Field is staticClasses and interfaces
ACC_FINAL0x0010Field is finalClasses and interfaces
ACC_VOLATILE0x0040Field is volatileClasses only
ACC_TRANSIENT0x0080Field is transientClasses only

For fields declared in a class (not an interface), at most one of ACC_PUBLIC, ACC_PRIVATE, and ACC_PROTECTED may be set. ACC_FINAL and ACC_VOLATILE must not both be set. All fields declared in interfaces must have the ACC_PUBLIC, ACC_STATIC, and ACC_FINAL flags set.
  类(不包括接口)中声明的字段,只能拥有ACC_PUBLIC,ACC_PRIVATE,ACC_PROTECTED这三个标志中的一个。ACC_FINAL和ACC_VOLATILE不能同时设置。所有接口中声明的字段必须有且只能有ACC_PUBLIC,ACC_STATIC和ACC_FINAL这三种标志。

All unused bits in access_flags must be set to zero and ignored by Java Virtual Machine implementations.
  Access_flags中没有用到的位都被设为0,Java虚拟机实现将忽略它们。

name_index

The name_index gives the index of a CONSTANT_Utf8_info entry that gives the simple (not fully qualified) name of the field.
  Name_index项提供了给出字段简单名称(不是全限定名)的CONSTANT_Utf8_info入口的索引。在class文件中的每一个字段的名称都必须符合Java程序设计语言中对名称的有效规定。

descriptor_index

The descriptor_index gives the index of a CONSTANT_Utf8_info entry that gives the descriptor of the field.
  Descriptor_index提供了给出字段描述符的CONSTANT_Utf8_info入口的索引。

attributes_count and attributes

The attributes item is a list of attribute_info tables. The attributes_count indicates the number of attribute_info tables in the list. Two kinds of attributes defined by the Java Virtual Machine specification that may appear in this item are ConstantValue and Synthetic. These two attributes are described in detail later in this chapter.
  Attributes项是由多个attribute_info表组成的列表。Attributes_count指出列表中attribute_info表的数量。一个字段在其列表中可以有任意数量的属性。由Java虚拟机规范定义的三种可能会出现在此项中的属性是:ConstantValue,Deprecated和Synthetic。Java虚拟机唯一需要识别的属性是ConstantValue属性。虚拟机实现必须忽略任何无法识别的属性。


JVM学习笔记-方法(Methods)

Methods

Each method declared in a class or interface or generated by the compiler is described in the class file by a method_info table. The two types of compiler-generated methods that may appear in class files are instance initialization methods (named < init) and class initialization methods (named < clinit). For more information on the compiler-generated methods, see Chapter 7, “The Lifetime of a Class.” The format of the method_info table is shown in Table 6-22.

  在class文件中,每个在类和接口中声明的方法,或者由编译器产生的方法,都由一个可变长度的method_info表来描述。同一个类中不能存在两个名字及描述符完全相同的方法。需要注意的是,在java程序设计语言中,尽管在同一个类或者接口中声明的两个方法不能有同样的特征签名(除返回类型之外的描述符),但在同一个class文件中,两个方法可以拥有同样的特征签名,前提是它们的返回值不能相同。换句话说:在Java源文件的同一个类里,如果声明了两个具有相同名字和相同参数类型、但返回值不同的方法,这个程序将无法编译通过。在Java程序设计语言中,不能仅仅通过返回值的不同来重载方法。但是同样的两个方法可以和谐地在一个class文件中共存。
  有可能在class文件中出现的两种编译器产生的方法是:实例初始化(名为)和类与接口初始化方法(名为)。Method_info表的格式如表6-22所示。

Table 6-22. Format of a method_info table

TypeNameCount
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

Method_info表中各项如下:

access_flags

The modifiers used in declaring the method are placed into the methodís access_flags item. Table 6-23 shows the bits used by each flag.
  在声明方法时使用的修饰符存放在方法的access_flags项中,表6-23列出了各个标志所使用的位。1.2版本中加进了ACC_STRICT标志,它指明方法中的所有表达式都必须使用FP-strict模式进行计算。

Table 6-23. Flags in the access_flags item of method_info tables

Flag NameValueMeaning if SetSet By
ACC_PUBLIC0x0001Method is publicClasses and all methods of interfaces
ACC_PRIVATE0x0002Method is privateClasses only
ACC_PROTECTED0x0004Method is protectedClasses only
ACC_STATIC0x0008Method is staticClasses only
ACC_FINAL0x0010Method is finalClasses only
ACC_SYNCHRONIZED0x0020Method is synchronizedClasses only
ACC_NATIVE0x0100Method is nativeClasses only
ACC_ABSTRACT0x0400Method is abstractClasses and all methods of interfaces

For methods declared in a class (not an interface), at most one of ACC_PUBLIC, ACC_PRIVATE, and ACC_PROTECTED may be set. If a methodís ACC_FINAL flag is set, then its ACC_SYNCHRONIZED, ACC_NATIVE, and ACC_ABSTRACT flags must not be set. If a methodís ACC_PRIVATE or ACC_STATIC flag is set, then its ACC_ABSTRACT flag must not be set. All methods declared in interfaces must have their ACC_PUBLIC and ACC_ABSTRACT flags set.
  类(不包括接口)中声明的方法只能拥有ACC_PUBLIC、ACC_PRIVATE、ACC_PROTECTED这三个标志中的一个。如果设定了一个方法的ACC_ABSTRACT标志,那么它的ACC_PRIVATE、ACC_STATIC、ACC_FINAL、ACC_SYNCHRONIZED、ACC_NATIVE以及ACC_STRICT标志都必须清除。接口中声明的所有方法必须有ACC_PUBLIC和ACC_ABSTRACT标志,除此以外接口方法不能使用其他标志,但接口初始化方法()可以使用ACC_STRICT标志。

Instance initialization (< init) methods may only use flags ACC_PUBLIC, ACC_PRIVATE, and ACC_PROTECTED. Because class initialization ( < clinit) methods are invoked by the Java Virtual Machine, never directly by Java bytecodes, the the access_flags for < clinit methods is ignored.
  实例初始化方法()可以只使用ACC_PUBLIC、ACC_PRIVATE和ACC_PROTECTED标志。因为类与接口初始化方法()只由Java虚拟机直接调用,永远不会被Java字节码直接调用,这样,< clinit>方法的access_flags中的标志位,除去ACC_STRICT之外的所有位都应该被忽略。

All unused bits in access_flags must be set to zero and ignored by Java Virtual Machine implementations.
  在access_flags中未用到的位都被设为0,Java虚拟机实现也将忽略它们。

name_index

The name_index gives the index of a CONSTANT_Utf8_info entry that gives the simple (not fully qualified) name of the method.
Name_index项提供了CONSTANT_Utf8_info入口的索引,该入口给出了方法的简单名称(不是全限定名)。在class文件中的每一个方法的名称,都必须或者未,或者为,或者是Java程序设计语言中有效的方法名称(简单名称,不是全限定名)。

name_index

The descriptor_index gives the index of a CONSTANT_Utf8_info entry that gives the descriptor of the method.
  Descriptor_index提供了CONSTANT_Utf8_info入口的索引,该入口给出了方法的描述符。

attributes_count and attributes

The attributes item is a list of attribute_info tables. The attributes_count indicates the number of attribute_info tables in the list. Three kinds of attributes that are defined by the Java Virtual Machine specification that may appear in this item are Code, Exceptions, and Synthetic. These three attributes are described in detail later in this chapter.
  Attributes项是由多个attribute_info表组成的列表。Attribute_count给出了列表中attribute_info表的数量。一个字段在其列表中可以有任意数量的属性。在此项中可能会出现的由Java虚拟机规范定义的四种属性是:Code、Deprecated、Exceptions和Synthetic。这四种属性将在本章后面进一步阐述。Java虚拟机只需要识别Code和Exception属性。虚拟机实现必须忽略任何无法识别的属性。


JVM学习笔记-属性(Attributes)

Attributes

As mentioned above, attributes appear in several places inside a Java class file. They can appear in the ClassFile, field_info, method_info, and Code_attribute tables. The Code_attribute table, an attribute itself, is described later in this section.
  如前所述,属性在Java class文件中多处出现。它们可以出现在ClassFile,field_info、method_info和Code_attribute表中。Code_attribute表本身即为一个属性。

Every attribute follows the same general format of the attribute_info table, shown in Table 6-24. The first two bytes of an attribute, the attribute_name_index, form an index into the constant pool of a CONSTANT_Utf8_info table that contains the string name of the attribute. Each attribute_info, therefore, identifies its “type” by the first item in its table much like cp_info tables identify their type by the initial tag byte. The difference is that whereas the type of a cp_info table is indicated by an unsigned byte value, such as 3 (CONSTANT_Integer_info), the type of an attribute_info table is indicated by a string.

Following the attribute_name_index is a four-byte attribute_length item, which gives the length of the entire attribute_info table minus the initial six bytes. This length is necessary because anyone, following certain rules (outlined below), is allowed to add attributes to a Java class file. Java Virtual Machine implementations are allowed to recognize new attributes. Implementations must ignore any attributes they donít recognize. The attribute_length allows virtual machines to skip unrecognized attributes as they parse the class file.

Anyone who wishes to add a new attribute to a Java class file must follow these two rules:

  1. Any attribute that is not predefined by the specification must not
    affect the semantics of class or interface types. New attributes can
    only add more information to the class file, such as information
    used during debugging.
  2. The attribute must be named using the reverse Internet domain name
    scheme that is defined for package naming in the Java Language
    Specification. For example, if your Internet domain name were
    artima.com and you wished to create a new attribute named
    CompilerVersion, you would name the attribute:
    COM.artima.CompilerVersion.

  如果需要向Java class文件中加入新的属性,除Sun公司以外的任何人都必须遵循下列两个步骤:
  1. 任何不是由规范进行与定义的属性都不能影响类或者接口类型的语义。新的属性只能向class文件添加新的信息,如在调试过程中用到的信息等。
  2. 属性必须使用与Internet域名方案颠倒的命名方式,Internet域名方案是针对Java语言规范中包的命名所定义的。例如,如果拥有Internet域名artima.com,而需要创建的新属性为CompilerVersion,那么属性则应该命名为com.artima.CompilerVersion。

Table 6-24. Format of an attribute_info table

TypeNameCount
u2attribute_name_index1
u4 attribute_length1
u1infoattribute_length

attribute_name_index

The attribute_name_index gives the index in the constant pool of a CONSTANT_Utf8_info entry that contains the name of the attribute.

attribute_length

The attribute_length item indicates the length in bytes of the attribute data excluding the initial six bytes that contain the attribute_name_index and attribute_length.

info

The info item contains the attribute data.


JVM学习笔记-属性格式(Attributes Types)

Attributes

The Java Virtual Machine specification defines eight types of attributes, shown in Table 6-25. All Java Virtual Machine implementations must recognize three of these attributes: Code, ConstantValue, and Exceptions. Implementations can choose whether to recognize or ignore the other predefined attributes. (The InnerClasses and Synthetic attributes were added in Java 1.1). All of these predefined attributes are described in detail later in this chapter.
  如表6-24所示,Java虚拟机规范定义了8种属性(实际上是9种)。为了正确地解释Javaclass文件,所有Java虚拟机实现都必须能够识别下列三种属性:Code,ConstantValue和Exception。为了正确地实现Java和Java 2 平台类库,虚拟机实现必须能够识别InnerClasses和Synthetic属性,但可以自主选择究竟是识别还是忽略其他一些与定义的属性(在Java1.1中,加入了Deprecated、InnerClasses和Synthetic属性(attribute))。所有这些预定义的属性将在本章详细阐述。

Table 6-25. Types of attribute_info tables defined by the specification

NameUsed ByDescription
Codemethod_infoThe bytecodes and other data for one method
ConstantValuefield_infoThe value of a final variable
Deprecatedfield_info、method_infoThe fields and methods are discouraged from using
Exceptionsmethod_infoThe checked exceptions that a method may throw
InnerClassesClassFileA description of the inner classes defined inside a class
LineNumberTableCode_attributeA mapping of line numbers to bytecodes for one method
LocalVariableTableCode_attributeA description of the local variables for one method
SourceFileClassFileThe name of the source file
Syntheticfield_info, method_infoAn indicator that a field or method was generated by the compiler

JVM学习笔记-动态连接和解析(Dynamic Linking and Resolution)

When you compile a Java program, you get a separate class file for each class or interface in your program. Although the individual class files may appear to be independent, they actually harbor symbolic connections to one another and to the class files of the Java API. When you run your program, the Java Virtual Machine loads your programís classes and interfaces and hooks them together in a process of dynamic linking. As your program runs, the Java Virtual Machine builds an internal web of interconnected classes and interfaces.
  当编译Java程序的时候,会得到程序中每一个类或者接口的独立的class文件。虽然独立看上去毫无关联,但是他们之间通过接口(harbor)符号互相联系,或者与Java API的class文件相联系。当运行程序的时候,Java虚拟机装载程序的类和接口,并且在动态连接的过程中把它们互相勾连起来。在程序运行中,Java虚拟机内部组织了一张互相连接的类和接口的网。

A class file keeps all its symbolic references in one place, the constant pool. Each class file has a constant pool, and each class or interface loaded by the Java Virtual Machine has an internal version of its constant pool. The internal constant pool is an implementation-specific data structure that maps to the constant pool in the class file. Thus, after a type is initially loaded, all the symbolic references from the type reside in the typeís internal constant pool.
  class把他们所有的引用符号放在一个地方——常量池。每一个class文件有一个常量池,每一个被Java虚拟机装载的类或者接口都有一份内部版本常量池,被称作运行时常量池。运行时常量池是特定与实现的数据结构,数据结构映射到class文件中的常量池。因此,当一个类型被首次装载的时候,所有来自于类型的符号引用都装载到了类型的运行时常量池。

At some point during the running of a program, if a particular symbolic reference is to be used, it must be resolved. Resolution is the process of finding the entity identified by the symbolic reference and replacing the symbolic reference with a direct reference. Because all symbolic references reside in the constant pool, this process is often called constant pool resolution.
  在程序运行的过程中,如果某个特定的符号引用将要被使用,它首先要被解析。解析过程就是首先根据符号引用查找到实体,再把符号引用替换成直接引用的过程。因为所有的符号引用都是保存在常量池中,所以这种解析叫做常量池解析。

As described in Chapter 6, “The Java Class File,” the constant pool is organized as a sequence of items. Each item has a unique index, much like an array element. A symbolic reference is one kind of item that may appear in the constant pool. Java Virtual Machine instructions that use a symbolic reference specify the index in the constant pool where the symbolic reference resides. For example, the getstatic opcode, which pushes the value of a static field onto the stack, is followed in the bytecode stream by an index into the constant pool. The constant pool entry at the specified index, a CONSTANT_Fieldref_info entry, reveals the fully qualified name of the class in which the field resides, and the name and type of the field.
  在第6章中描述过,常量池按照一系列项组织。每一项拥有一个唯一的索引,很像数组元素。符号引用是可以出现在常量池的一种项目。使用符号引用的Java虚拟机指令指定位于常量池中的符号引用的索引。比如,getstatic操作码(它把一个静态字段的值压入栈中)在字节码流中会跟有一个常量池索引。常量池中指定索引指向的入口,是一个Constant_Fieldref_info入口,它显示出这个字段所在类的全限定名以及字段的名字和类型。

Keep in mind that the Java Virtual Machine contains a separate internal constant pool for each class and interface it loads. When an instruction refers to the fifth item in the constant pool, it is referring to the fifth item in the constant pool for the current class, the class that defined the method the Java Virtual Machine is currently executing.
  记住,java虚拟机为每一个装载的类和接口保存一份独立的常量池。当一条指令引用常量池的第5个元素的时候,它指向的是当前类的常量池中的第5个元素,即定义Java虚拟机当前正执行的方法的类。

Several instructions, from the same or different methods, may refer to the same constant pool entry, but each constant pool entry is resolved only once. After a symbolic reference has been resolved for one instruction, subsequent attempts to resolve it by other instructions take advantage of the hard work already done, and use the same direct reference resulting from the original resolution.
Linking involves not only the replacement of symbolic references with direct ones, it also involves checking for correctness and permission. As mentioned in Chapter 7, “The Lifetime of a Class,” the checking of symbolic references for existence and access permission (one aspect of the full verification phase) is likely performed during resolution. For example, when a Java Virtual Machine resolves a getstatic instruction to a field of another class, the Java Virtual Machine checks to make sure that:

  • The other class exists.
  • This class has permission to access the other class.
  • The named field exists in the other class.
  • The field has the expected type (symbolic references to fields
    include the field type).
  • This class has permission to access the field.
  • That the field is really static–a class variable and not an instance variable.

If any of these checks fail, an error is thrown and resolution fails. Otherwise, the symbolic reference is replaced by the direct reference and resolution succeeds.

As described in Chapter 7, “The Lifetime of a Class,” different implementations of the Java Virtual Machine are permitted to perform resolution at different times during the execution of a program. An implementation may choose to link everything up front by following all symbolic references from the initial class, then all symbolic references from subsequent classes, until every symbolic reference has been resolved. In this case, the application would be completely linked before its main() method was ever invoked. This approach is called early resolution. Alternatively, an implementation may choose to wait until the very last minute to resolve each symbolic reference. In this case, the Java Virtual Machine would resolve a symbolic reference only when it is first used by the running program. This approach is called late resolution. Other implementations could choose a resolution strategy in-between these two extremes.

Although a Java Virtual Machine implementation has some freedom in choosing when to resolve symbolic references, every Java Virtual Machine must give the outward impression that it uses late resolution. No matter when a particular Java Virtual Machine performs its linking, it will always throw any error that results from attempting to resolve a symbolic reference at the point in the execution of the program where the symbolic reference was actually used for the first time. In this way, it will always appear to the user as if the linking were late. If a Java Virtual Machine does early linking, and during early linking discovers that a class file is missing, it wonít report the class file missing by throwing the appropriate error until later in the program when something in that class file is actually used. If the class is never used by the program, the error will never be thrown.


JVM学习笔记-引用计数收集器(Reference Counting Collectors)

Reference Counting Collectors

Reference counting was an early garbage collection strategy. In this approach, a reference count is maintained for each object on the heap. When an object is first created and a reference to it is assigned to a variable, the objectís reference count is set to one. When any other variable is assigned a reference to that object, the object’s count is incremented. When a reference to an object goes out of scope or is assigned a new value, the object’s count is decremented. Any object with a reference count of zero can be garbage collected. When an object is garbage collected, any objects that it refers to have their reference counts decremented. In this way the garbage collection of one object may lead to the subsequent garbage collection of other objects.
  引用计数是垃圾收集的早期策略。在这种方法中,堆中每一个对象都有一个引用计数。一个对象被创建了,并且指向该对象的引用被分配给一个变量,这个对象的引用计数被置为1。当任何其他变量被赋值为对这个对象的引用时,计数加1。当一个对象的引用超过了生存期或者被设置一个新的值时,对象的引用计数减1。任何引用计数为0的对象可以被当作垃圾收集。当一个对象被垃圾收集的时候,它引用的任何对象计数值减1。在这种方法中,一个对象被垃圾收集后可能导致后续其他对象的垃圾收集行动。

An advantage of this approach is that a reference counting collector can run in small chunks of time closely interwoven with the execution of the program. This characteristic makes it particularly suitable for real-time environments where the program can’t be interrupted for very long. A disadvantage is that reference counting does not detect cycles: two or more objects that refer to one another. An example of a cycle is a parent object that has a reference to a child object that has a reference back to the parent. These objects will never have a reference count of zero even though they may be unreachable by the roots of the executing program. Another disadvantage of reference counting is the overhead of incrementing and decrementing the reference count each time.
  这种方法的好处是,引用计数收集器可以很快地执行,交织在程序的运行之中。这个特性对于程序不能被长时间打断的实时环境很有利。坏处就是,引用计数无法检测出循环(即两个或者更多的对象互相引用)。循环的例子如,父对象有一个对子对象的引用,子对象又反过来引用父对象。这些对象永远都不可能计数为0,就算它们已经无法被执行程序的根对象可触及。还有一个坏处就是,每次引用计数的增加或者减少都带来额外开销。

Because of the disadvantages inherent in the reference counting approach, this technique is currently out of favor. It is more likely that the Java Virtual Machines you encounter in the real world will use a tracing algorithm in their garbage-collected heaps.
  因为引用计数方法固有的缺陷,这种技术现在已经不为人所接受。现实生活中所遇到的Java虚拟机更有可能在垃圾收集堆中使用追踪算法。


JVM学习笔记-跟踪收集器(Tracing Collectors)

Tracing Collectors

Tracing garbage collectors trace out the graph of object references starting with the root nodes. Objects that are encountered during the trace are marked in some way. Marking is generally done by either setting flags in the objects themselves or by setting flags in a separate bitmap. After the trace is complete, unmarked objects are known to be unreachable and can be garbage collected.
  跟踪收集器追踪从根节点开始的对象引用图,在追踪过程中对活动对象打上标记。总的来说要么在对象本身设置标记,要么用一个独立的位图来设置标记,当追踪结束时,未被标记的对象就是无法触及的,从而可以被收集。

The basic tracing algorithm is called “mark and sweep.” This name refers to the two phases of the garbage collection process. In the mark phase, the garbage collector traverses the tree of references and marks each object it encounters. In the sweep phase, unmarked objects are freed, and the resulting memory is made available to the executing program. In the Java Virtual Machine, the sweep phase must include finalization of objects.
  基本的追踪算法被称作“标记并清除”。这个名字指出垃圾收集的两个阶段。标记阶段,垃圾收集器遍历引用树,为遍历到对象增加标记,清除阶段,把没有被标记的对象清除,释放其占用的内存空间,在JVM中清除对象必须包含对象的终结。


JVM学习笔记-压缩收集器(Compacting Collectors)

Compacting Collectors

Garbage collectors of Java Virtual Machines will likely have a strategy to combat heap fragmentation. Two strategies commonly used by mark and sweep collectors are compacting and copying. Both of these approaches move objects on the fly to reduce heap fragmentation. Compacting collectors slide live objects over free memory space toward one end of the heap. In the process the other end of the heap becomes one large contiguous free area. All references to the moved objects are updated to refer to the new location.
  Java虚拟机的垃圾收集器可能有对付堆内存碎块的策略。标记并清除收集器通常使用的两种策略是压缩和拷贝,这2种方法都是快速的移动对象来减少堆碎块,。压缩收集器把活动的对象越过空闲区滑动到堆得一端,这个过程中,堆的另一端出现一个大的连续空闲区,所有被移动的对象引用也会被更新指向新的位置。

Updating references to moved objects is sometimes made simpler by adding a level of indirection to object references. Instead of referring directly to objects on the heap, object references refer to a table of object handles. The object handles refer to the actual objects on the heap. When an object is moved, only the object handle must be updated with the new location. All references to the object in the executing program will still refer to the updated handle, which did not move. While this approach simplifies the job of heap defragmentation, it adds a performance overhead to every object access.
  更新被移动的对象的引用通过一个间接对象引用层可以变的更简单。不直接引用堆中的对象,对象的引用时间上是指向一个对象句柄表,对象句柄才指向堆中对象的实际位置。当对象被移动时候,只有这个句柄需要被更新为新位置,这个方法简化了消除堆碎块的工作,但是每一次对象访问都带来了性能的损失。


JVM学习笔记-拷贝收集器(Copying Collectors)

Copying Collectors

Copying garbage collectors move all live objects to a new area. As the objects are moved to the new area, they are placed side by side, thus eliminating any free space that may have separated them in the old area. The old area is then known to be all free space. The advantage of this approach is that objects can be copied as they are discovered by the traversal from the root nodes. There are no separate mark and sweep phases. Objects are copied to the new area on the fly, and forwarding pointers are left in their old locations. The forwarding pointers allow the garbage collector to detect references to objects that have already been moved. The garbage collector can then assign the value of the forwarding pointer to the references so they point to the objectís new location.

A common copying collector algorithm is called “stop and copy.” In this scheme, the heap is divided into two regions. Only one of the two regions is used at any time. Objects are allocated from one of the regions until all the space in that region has been exhausted. At that point program execution is stopped and the heap is traversed. Live objects are copied to the other region as they are encountered by the traversal. When the stop and copy procedure is finished, program execution resumes. Memory will be allocated from the new heap region until it too runs out of space. At that point the program will once again be stopped. The heap will be traversed and live objects will be copied back to the original region. The cost associated with this approach is that twice as much memory is needed for a given amount of heap space because only half of the available memory is used at any time.

You can see a graphical depiction of a garbage-collected heap that uses a stop and copy algorithm in Figure 9-1. This figure shows nine snapshots of the heap over time. In the first snapshot, the lower half of the heap is unused space. The upper half of the heap is partially filled by objects. That portion of the heap that contains objects is painted with diagonal gray lines. The second snapshot shows that the top half of the heap is gradually being filled up with objects, until it becomes full as shown in the third snapshot.

At that point, the garbage collector stops the program and traces out the graph of live objects starting with the root nodes. It copies each live object it encounters down to the bottom half of the heap, placing each object next to the previously copied object. This process is shown in snapshot four.
Snapshot five shows the heap after the garbage collection has finished. Now the top half of the heap is unused, and the bottom half is partially filled with live objects. The sixth snapshot shows the bottom half is now becoming gradually filled with objects, until it too becomes full in snapshot seven.
Once again, the garbage collector stops the program and traces out the graph of live objects. This time, it copies each live object it encounters up to the top half of the heap, as shown in snapshot eight. Snapshot nine shows the result of the garbage collection: the bottom half is once again unused space and the top half is partially filled with objects. This process repeats again and again as the program executes.

  拷贝收集器把所有的活动的对象移动到一个新的区域。在拷贝过程中,被紧挨着布置,这样可以消除原本它们在旧区域的空隙。即空闲区。

  好处:对象可以在根对象开始的遍历过程中随着发现而拷贝,不再有标记和清除的区分。对象被快速拷贝到新区域,同时转向指针仍然留在原来的位置。转向指针可以让垃圾收集器发现已经被转移对象的引用。然后垃圾收集器可以把这些引用设置为转向指针的值,因此它们现在指向对象的新的位置。

  一般的拷贝收集器算法被称为“停止并拷贝”。此方案中,堆被分成两个区域,任何时候都使用一个区域。对象在同一个区域中分配直到被耗尽。此时,程序执行被中止,堆被遍历,遍历时遇到活动的对象被拷贝到另个区域。当停止和拷贝过程结束时,程序恢复执行。依次往复,对于指定大小的堆来说需要两倍大小的内存,由于任何时候都只使用其中的一半,这就是该方法带来的代价。


JVM学习笔记-分代收集器(Generational Collectors)

Generational Collectors

One disadvantage of simple stop and copy collectors is that all live objects must be copied at every collection. This facet of copying algorithms can be improved upon by taking into account two facts that have been empirically observed in most programs in a variety of languages:
  简单的停止并拷贝收集器的缺点是,每一次收集时,所有的活动对象都必须被拷贝。大部分语言的大多数程序都有如下特点,如果我们认真考虑这些,拷贝算法的这个缺点是可以被改进的。
Most objects created by most programs have very short lives.

Most programs create some objects that have very long lifetimes.
  1) 大多数程序所创建的大部分对象都只有很短的生命周期
  2) 大多数程序都创建一些具有非常长生命周期的对象

A major source of inefficiency in simple copying collectors is that they spend much of their time copying the same long-lived objects again and again.
  简单的拷贝收集器浪费效率的一个主要原因就是,它们每次都把这些生命周期很长的对象来回拷贝,消耗大量的时间。

Generational collectors address this inefficiency by grouping objects by age and garbage collecting younger objects more often than older objects. In this approach, the heap is divided into two or more sub-heaps, each of which serves one “generation” of objects. The youngest generation is garbage collected most often. As most objects are short-lived, only a small percentage of young objects are likely to survive their first collection. Once an object has survived a few garbage collections as a member of the youngest generation, the object is promoted to the next generation: it is moved to another sub-heap. Each progressively older generation is garbage collected less often than the next younger generation. As objects “mature” (survive multiple garbage collections) in their current generation, they are moved to the next older generation.
  按代收集的收集器通过把对象按照寿命来分组解决这个效率低下的问题,更多的收集那些短暂出现的年幼对象,而非寿命较长的对象。在这种方法里,堆被划分为两个或者更多的子堆,每一个子堆为一“代”对象服务。最年幼的那一代进行最频繁的垃圾收集。因为大多数对象都是短促出现的,只有很小部分的年幼对象可以在它们经历第一次收集后存活。如果一个最年幼的对象经历了好几次垃圾收集后仍然存活,那么这个对象就成长为寿命更高的一代;它被转移到另外一个子堆中去。年龄更高的每一代的收集都没有年轻的那一些来的频繁。每当对象在它所属的年龄层(代)中变得成熟(逃过了多次垃圾收集)之后,它们就被转移到更高的年龄层中去。

The generational collection technique can be applied to mark and sweep algorithms as well as copying algorithms. In either case, dividing the heap into generations of objects can help improve the efficiency of the basic underlying garbage collection algorithm.
  按代进行的垃圾收集除了可以应用于拷贝算法,也可以应用于标记并清楚算法。不管在哪种情况下,把堆按照对象年龄层分解都可以提高最基本的垃圾收集算法性能


JVM学习笔记-调用Java方法(Invoking a Java Method)

Invoking a Java Method

As mentioned in Chapter 5, “The Java Virtual Machine,” the virtual machine creates a new stack frame for each Java (not native) method it invokes. The stack frame contains space for the method’s local variables, its operand stack, and any other information required by a particular virtual machine implementation. The size of the local variables and operand stack are calculated at compile-time and placed into the class file, so the virtual machine knows just how much memory will be needed by the method’s stack frame. When it invokes a method, it creates a stack frame of the proper size for that method. The virtual machine pushes the new stack frame onto the Java stack.
  如第5章所述,虚拟机为每一个调用的Java(非本地)方法建立一个新的栈帧。栈帧包括:为方法的局部变量所预留的空间,该方法的操作数栈,以及特定虚拟机实现需要的其他所有的信息.局部变量和操作数栈大小在编译时计算出来,并设置到class文件中去,然后虚拟机就能够了解到方法的栈帧需要多少内存.当虚拟机调用一个方法的时候,它为该方法创建恰当大小的栈帆,再将新的栈帧压入Java栈。

For an instance method, the virtual machine pops the objectref and args from the operand stack of the calling method’s stack frame. It places the objectref on the new stack frame as local variable 0, and all the args as local variable 1, 2, and so on. The objectref is the implicit this pointer that is passed to any instance method.
  处理实例方法时,虚拟机从所调用方法栈帧内的操作数栈中弹出objectref和args。虚拟机把objectref作为局部变量0放到新的栈帧中,把所有的args作为局部变量1,2,………等处理。objectref是隐式传给所有实例方法的this指针。

For a class method, the virtual machine just pops the args from the operand stack of the calling method’s frame and places them onto the new stack frame as local variable 0, 1, 2, and so on.
  对于类方法,虚拟机只从所调用方法栈帧中的操作数栈中弹出参数,并将它们放到新的栈帧中去作为局部变量0,1,2……….

Once the objectref and args (or just the args, for a class method) have been placed into the local variables of the new frame, the virtual machine makes the new stack frame current and sets the program counter to point to the first instruction in the new method.
  当objectref和args(对于类方法则只有args)被赋给新栈帧中的局部变量之后,虚拟机把新的栈帧作为当前栈帧,然后将程序计数器指向新方法的第一条指令。


JVM学习笔记-本地方法调用(Invoking a Native Method)

Invoking a Native Method
As mentioned in Chapter 5, “The Java Virtual Machine,” the virtual machine invokes native methods in an implementation dependent manner. When invoking a native method, the virtual machine does not push a new stack frame onto the Java stack for the native method. At the point in which the thread enters the native method, it leaves the Java stack behind. When the native method returns, the Java stack will once again be used.
   如第5章所述,虚拟机使用一种“与实现相关”的风格调用本地方法。当调用本地方法时,虚拟机不会将一个新的栈帧压入Java栈。当线程进入到本地方法的那一刻,它就将Java栈抛在身后。直到本地方法返回以后,Java栈才被重新使用。

转载自:http://denverj.iteye.com/category/184615
参考:http://wangwengcn.iteye.com/blog/1622195

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值