《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新

时间:2022-12-27 14:25:24


第一章  一起走进 Java 世界


Java 应用程序的开发周期包括编译、下载、解释和执行几个部分。

当 C 编译器编译生成一个对象的代码时,该代码是为在某一特定硬件平台运行而产生的。因此在编译过程中,编译程序通过查表将所有符号的引用转换为特定的内存偏移量。

Java 编译器不将对变量和方法的引用编译为数值引用,也不确定程序执行过程中的内存布局,而是将这些符号引用信息保留在字节码中。由解释器在运行过程中创立内存布局,然后再通过查表来确定一个方法所在的地址。(java 可移植性和安全性)

Java 源文件 -> java 编译器 -> 字节码文件 -> java 解释器 (分不同系统) -> 运行


Java 虚拟机 = 一套字节码指令集 +

             一组寄存器 +

             一个栈 +

             一个垃圾回收堆 +

             一个存储方法域

Java 指令集 相当于 java 程序的汇编语言(大约 248 个字节码)

虚拟机内层循环的执行过程:do {

                                                  取一个操作符字节;

                                                根据操作符的值执行一个动作;

                                        } while(程序未结束)


Java 寄存器:pc      (java 程序计数器)、

                   optop (指向操作数栈顶端的指针)、

                   frame (指向当前执行方法的执行环境的指针)、

                     vars    (指向当前执行方法的局部变量区第一个变量的指针)


java 栈:局部变量区   (固定大小的局部变量集,按照与 vars 寄存器的字偏移量来寻址。局部变量都是 32 位)、

              运行环境区   (动态链接,正常的方法返回,异常和错误传播)、

              操作数栈区   (操作数栈是 32 位的,用于方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果)

              无用单元收集堆   (运行时数据区,类的实例从中分配空间)

              方法区   (保存方法代码和符号表)


HotSpot VM 目前使用范围最广


需要避免的影响平台无关性的因素:java 平台的部署、

                                                      java 平台版本、

                                                     本地方法、

                                                     非标准运行时库、

                                                     对虚拟机的依赖(及时终结 Finalization, 线程优先级  Thread Prioritization)、

                                                     Java 平台实现中的 bug、

                                                     测试







第二章  JDK 编译测试

 

http://openjdk.java.net   ->  OpenJDK

http://download.java.net/openjdk/jdk7/  Source Releases-> JDK、JDK Plug

http://www.cygwin.com/setup.exe  -> Cygwin 模拟 Linux(User URL http://www.cygwin.cn/pub/)

Devel  -> binutils  gcc  gcc-mingw  gdb

C-Free 构建 – 构建选项 – 构建配置 – 编译器类型(CYGWIN) – 编译器安装目录

 

Visual Studio C++ 2010  or  Visual Studio C++ 2010 Express

 

在 PATH 中写入 Cygwin 、 VS2010 的bin目录路径,后者在前(link.exe)

 

关闭 Windows 文件保护 gpedit.msc 计算机配置 管理模板 系统

 

http://developers.sun.com/downloads/   -> JDK7 Update 1(java -version)

 

http://ant.apache.org/bindownload.cgi   -> Apache Ant(C:\Program Files\Adobe\Adobe Flash Builder 4.0.0\ant\lib  flexTasks.jar  -- > ANT … \lib)

环境变量 :ANT_HOME – 解压路径 …\ant\apache-ant-1.8.1

                  Path: %ANT_HOME%\bin

 

JDK Plug :java jdk-7-ea-plug-b121-windows-i586-09_dec_2010.jar

环境变量 :ALT_BINARY_PLUGS_PATH 安装路径

 

Optional Import JDK :Bootstrap JDK

环境变量 :ALT_JDK_IMPORT_PATH  JDK安装目录

 

FreeType(2.3) : ALT_FREETYPE_LIB_PATH   ALT_FREETYPE_HEADERS_PATH

                           分别指向 FreeType 安装目录下的 bin 和 include 目录

                           还需要把 FreeType 的 bin 目录加入到 PATH 环境变量中

 

Microsoft DirectX 9.0 SDK:ALT_DXSDK 指向 DirectX 9.0 SDK 安装目录

 

MSVCR 100.DLL 动态链接库,如果安装了 Visual Studio 2012 能找到这个文件

ALT_MSVCRNN_DLL_PATH 指向这个文件所在的目录 (2003—MSVCR73.DLL)








第三章  安全性的考虑


Java 沙箱:类加载体系结构、

                 class 文件检验器、

                 内置于 java 虚拟机(及语言)的安全特性、

                 安全管理器及 java API


类加载器体系结构:防止恶意代码去干涉善意的代码,守护了被信任的类库的边界,将代码归入某类,该类确定了代码可以进行哪                                 些操作 (委托双亲模式)

启动类装载器 – 标准扩展类装载器 – 路径类装载器 – 网络类装载器                                      


Class 文件检验器:class 文件的结构检查 (魔数 0xCAFEBABE)、

                             类型数据的语义检查、

                             字节码验证、

                             符号引用的验证 (NoClassDefFoundError、NoSuchMethodError)


内置于 java 虚拟机(及语言)的安全特性:类型安全的引用转换、 

                                                             结构化的内存访问(无指针算法)、

                                                            自动垃圾收集(不必显示地释放被分配的内存)、

                                                            数组边界检查、

                                                            空引用检查


安全管理器:一个 API 级别的、可以自定义的安全策略管理器 (SecurityMananger)

                   安装 (-Djava.security.manager)

                   启动 (System.setSecurityMananger)

                   权限检查 (SecurityMananger.checkPermission(Permission perm))

                   实现不同的权限验证 (Permission  ->  new FilePermission(“test.txt”,”read”))


Java API : check 方法(checkRead()、checkWrite()…安全异常)


Jarsigner(SDK 1.2) :对整个 JAR 文件签名 (jar – 散列(64or128) – 签名 –>  JAR 末尾[公钥/  私钥])

访问控制策略:java.security.Policy 抽象类一个子类的单个实例 (任何时候,每个应用程序实际上都只有一个 Policy 对象)


安全策略:一个从描述运行代码的属性集合 到 这段代码所拥有的权限的映射 (代码来源 java.security.CodeSource(java.net.URL))

Policy.getPolicy() -> Policy 对象 -> getPermission() -> Permission -> CodeSource -> java.security.PermissionCollection


访问控制器: java.security.AccessController 默认的安全策略执行机制 (static checkPermission() -> AccessControlException)


Boolean Java.security.AccessController.implies(Permission) 判断 Permission 之间的包含关系


Java 安全工具:密钥和证书管理工具 (keytool)、

                        Java 文档处理工具 (jar)、

                        Java 文档签名及验证工具 (jarsigner)、

                       策略编辑器 (policytool)

基础安全 API :对安全性支持的接口 (java.security.Certificate指定类型的证书

                                                       /java.security.Key 证书的密钥

                                                       /java.security.Principal 可以提供 identify 的任何实体

                                                       /java.security.PrivateKey 提供私有密钥

                                                       /java.security.PublicKey 提供公有密钥)

                        支持访问控制的接口 (java.security.acl.Acl   多个 acl 的入口的集合

                                                       /java.security.acl.AclEntry   acl 的入口

                                                       /java.security.acl.Group 一组 Principal

                                                       /java.security.acl.Owner   acl 的管理者

                                                       /java.security.acl.Permission 控制信息)


构造安全应用程序:a。产生公钥和私钥对

                                        [得到密钥产生器]

                                    KeyPairGenerator keyGen = KeyPairGenerator.getInstance(“DSA”,”SUN”);

                                       [初始化密钥产生器]

                                   SecureRandom random = SecureRandom.getInstance(“SHA1PRNG”,”SUN”);

                                   KeyGen.initialize(1024,random);

                                       [产生公钥和密钥]

                                   KeyPair pair = keyGen.generateKeyPair();

                                   PrivateKey priv = pair.getPrivate();

                                   PublicKey pub = pair.getPublic();

                            b。对数据进行签名

                                       [得到一个签名对象]

                                   Signature dsa = Signature.getInstance(“SHA1withDSA”,”SUN”);

                                       [初始化签名对象]

                                   Dsa.initSign(priv);

                                       [对数据签名]

                                   Dsa.update(buffer,0,len);

                                       [得到签名的数据]

                                   Byte[] realSig = dsa.sign();

                             c。 存储签名和公钥

                                       [签名结果直接按字节流存储;公钥通过 pub.getEncoded(),先转换为字节流来处理]

                             d。从文件中取得公钥

                                       [从文件中读到字节流中]

                                   encKey

                                       [构造一个密钥说明类]

                                   KeyFactory keyFactory = KeyFactory.getInstance(“DSA”,”SUN”);

                                       [取得公钥]

                                   PublicKey pubKey = keyFacotory.generatePublic(pubKeySpec);

                             e。验证签名

                                       [同生成签名一样先取得签名对象]

                                   Signature sig = Signature.getInstance(“SHA1withDSA”,”SUN”);

                                       [用公钥初始化签名对象]

                                   Sig.initVerify(pubKey);

                                       [取得被签名对象]

                                   Sig.update(buffer,0,len);

                                       [验证]

                                   Boolean verifies = sig.verify(sigToVerify);







第四章  通过网络实现移动性


JINI 服务对象(Java Intelligent Network Infrastructure), 将成组的硬件设备 和 软件组件联合成一个单一的、动态的分布式系统。

服务(Service) 是一个独立的功能实体,可以被人、程序或者其他服务使用。(可以试一次计算过程或者存储操作)

客户(Client)   需要利用服务的硬件设备或软件组件。

查找服务(Lookup Service) JINI中的一种服务协议,它允许软硬件发现网络并变成联盟中的成员,同时将所提供的服务广播给联盟 中其他成员。


完整的 JINI 系统:基础设备 (Infrastructure)、

                            编程模型 (Programming Model)、

                            服务 (Serivce)


基础设备, Java RMI 扩展实现、分布式安全系统、发现/加入协议(Discovery/Join)、查找服务(Lookup)

编程模型, 租用(Leasing)、分布式事件(Distributed Events)、事务(Transaction)

服务,        JavaSpace、事务管理器


http://www.sun.com/software/JINI/  ->   JINI Technology Starter Kit(2.0)

JINI-2_0-src.zip (HTTP 服务器   [JINI2_0\script\ 创建 start-httpd.bat  java –jar lib\tools.jar –port 8080 –dir lib –verbose ]、

                          RMI 激活守护线程   [JINI2_0\script\ 创建 start-rmid.bat  rmid –J-Dsun.rmi.activation.execPolicy=none]、

                          查找服务    [JINI2_0\config\ 创建安全策略文件 all.policy  grant codeBase “file:lib${ /}*”

                                            {permission java.security.AllPermission;}; 

                          为Reggie创建启动配置文件 start-reggie.config \\Import com.sun.JINI.config.ConfigUtil;

Improt com.sun.JINI.start.NonActivatableServiceDescriptor;

Improt com.sun.JINI.start.ServiceDescriptor;

Com.sun.JINI.start{

Private static codebase = ConfigUti.concat(new Object[]{“http://”,ConfigUtil.getHostName(),”:8080/reggie-d1.jar”});

Private static policy = “config${ /} all.policy”;

Private static classpath = “lib${ /} reggie.jar”;

Private static config = “config${ /} reggie.config”;

Static ServiceDescriptors = new ServiceDescriptor[]{new NonActivatableServiceDescriptor(codebase,policy,classpath,”com.sun.JINI,reggie.TransientRegistrarImpl”,new String[]{config})};

}

                          \JINI2_0\config\ 为Reggie创建配置文件 reggie.config

Import net.JINI.jrmp.JrmpExproter;

Com.sun.JINI.reggie{serverExporter = new JrmpExporter();initialMemberGroups = new String[]{“example.JINI.sun.com”}};

}

                          \JINI2_0\script\ 为了简化 Reggie 查找服务的启动过程 start-reggie.bat

Java –Djava.security.policy=config\all.policy

-jar lib\start.jar config\start-reggie.config

 

                          启动 HTTP 服务器、RMI 激活守护线程、Reggie 查找服务:

                                         D:\JINI2_0> start script\start-httpd

                                        D:\JINI2_0> start script\start-rmid

                                        D:\JINI2_0> start script\start-reggie

 

                         启动 Browser(查询当前 JINI 网络中所有服务),d:\JINI2_0\script\ 中创建 start-browser.bat

                                        Java –Djava.security.policy=config\all.policy                                                                                

                                                –Djava.rmi.server.codebase=http://%computername%:8080/browser-d1.jar –jar lib\browser.jar

                                                D:\JINI2_0> start script\start-browser

                         ])





第五章  浅谈 Java 虚拟机的内部机制


Java 虚拟机:抽象规范、

                      一个具体的实现、

                     一个运行中虚拟机实例

程序运行,内存中 (字节码、

                             从已装载的 class 文件中得到的其他信息、

                            程序创建的对象、

                            传递给方法的参数、

                            返回值、

                            局部变量、

                            运算的中间结果)

基本类型:数值类型 (浮点类型[float、double]、

                 整数类型 [byte、short、int、long、char]/正负零、

                正负无穷大、

                非数字标识 (Not-a-Number,NaN))

                 returnAddress (内部用来实现 finally 子句, 被 java 虚拟机的 jsr、ret、jsr_w 指令所使用)

                 Boolean (值:int (!0:true)、数组:byte[])

引用类型:引用 (类类型、接口类型、数组类型[真正的对象]/null)

 

Java 虚拟机规范定义了 每一种 数据类型的取值范围,却没有定义它们的位宽,由虚拟机实现的设计者决定。

 

Java 虚拟机 最基本数据单元:字(Word), 由虚拟机设计者来决定,至少 32 位为字长

                                              定义了栈帧的两个部分 —— 局部变量、操作数栈

 

类装载器子系统:负责查找并装载类型

                 装载: 查找并装载类型的二进制数据

                 连接:执行验证 (确保被导入类型的正确性)、

                           准备        (为类变量分配内存,并将其初始化为默认值)、

                          解析        (把类型中的 符号引用 转换为 直接引用,可选)

              初始化:把类型中的符号引用转换为正确初始值

    启动类加载器:只要给定某个类型的全限定名,启动类装载器就必须能够以某种方式得到定义该类型的数据

用户自定义类装载器:protected final Class<?> defineClass(String name,byte[] b,int off,int len);

                                  protected final Class<?> defineClass(String name,byte

                                                          [] b,int off,int len,ProtectionDomain protectionDomain);

                                  protected final Class<?> findSystemClass(String name);

                                  protected final void resolveClass(Class<?> c);) 

               命名空间:每个类加载器都有自己的命名空间,确保一个类型的全限定名 不足以在一个 Java 虚拟机中 确定其唯一性

 

方法区:装载类型信息的一块逻辑内存 (包括类变量,

                                                            对数据的访问必须是线程安全的,

                                                            允许程序员定义大小)

 

基本类型信息:类型的全限定名、

                        类型的直接超类的全限定名 (除非是 java.lang.Object,没有超类)、

                        类型是 类类型 还是 借口类型、

                        类型的 访问修饰符(public、abstract、final)、

                        任何 直接超接口 的全限定名的 有序列表

特殊类型信息:类型的常量池、

                       字段信息、

                        方法信息、

                        除了常量 以外的所有类 (静态)变量、

                       一个到类 ClassLoader 的引用、

                        一个到 Class 类的引用

 

堆:java 程序在运行时创建的所有 类实例 或 数组 都放在同一个堆中。

      而一个 java 虚拟机实例中只存在一个堆空间,因此 所有线程都将共享这个堆。

      又由于一个 java 程序独占一个 java 虚拟机实例,因此每个 java 程序都有它自己的堆空间 —— 它们不会彼此干扰。

 

垃圾收集:如果没有设计 垃圾收集器,只是抛出 OutOfMemory 可以说符合规范的

 

堆空间设计:一个句柄池、

                    一个对象池 (指针需要跳转两次)

                    使对象指针直接指向一组数据 (碎片整理时,需要在整个 运行时 数据区中,更新指向 被移动对象的引用)

 

程序计数器:每一个线程都有自己的 PC (程序计数器) 寄存器,在该线程启动时创建。

                    (一个字长,既能持有一个本地指针,也能够持有一个 returnAddress[undefined])

 

Java 栈:每当启动一个新线程时,java 虚拟机都会为它分配一个 java 栈。

              以帧为单位保存 线程的运行状态。

             (虚拟机只会直接对 java 栈执行两种操作:以帧为单位的 压栈、

                                                                                                  出栈 )

 

栈帧:局部变量区  (一个字长为单位,从0开始计数的数组 [索引访问]。

                              byte、short、char 在存入数组前被转换为 int 值,因而同样占据一项。

                              long、double占连续的两项 [64位, 可以将数据放在低项,保持高项为空] ) +

          操作数栈      (一个字长为单位的数组 [通过标准的栈操作  — — 压栈、出栈] ) + 

          帧数据区      (保存 支持常量池解析、正常方法返回 以及 异常派发机制 的数据信息。

                              允许 相邻方法 栈帧可以相互重叠)

 

本地方法栈:当某个线程调用本地方法,那它的能力就和 java 虚拟机一样。

                    当回调 java 虚拟机中 java 方法时,将会产生一个新栈帧并压入 java 栈

《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新

 

 

 

执行引擎:抽象的规范、

                 具体的实现、

                 正在运行的实例

《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新

验证字节码:解释、

                    即时编译、

                   自适应优化、

                   芯片级直接执行

 

没有声明为 volatile 的 long、double 变量,某些实现是 两个原子性的 32 位值,多线程应确保线程安全

 

本地方法接口,Sun 的 java 本地接口,或称为 JNI,是为 可移植性 准备的

 

操作耗费时间对照表:

本地赋值                i = n                         1.0

实例赋值               this.i = n                  1.2

方法调用               Funct()                     5.9

新建对象               New Object()           980

新建数组               New int[10]             3100

 

垃圾收集器(Grabage Collector),为了能正确释放对象,必须监控每一个对象的运行状态,

                                                    包括对象的申请、

                                                                     引用、

                                                                     被引用、

                                                                     赋值等。

                                                   开始回收“垃圾”时,系统会暂停 应用程序的执行,而独占CPU

 

对象池,一般用于 网络 和 数据库连接 这类重量级的对象

 

 




 

第六章  详解 Class 文件

 

Java Class 文件是 8 为字节的二进制流

 

数据类型:u1、u2、u4 (分别表示一个无符号类型的、占1、2、4个字节的数据)

项:描述 java class 文件格式的结构的内容

表:由项组成,用于几种Class文件结构中


ClassFile{

              u4 magic (magic number) : 0xCAFEBABE;

              u2 minor_version : 次版本号;

              u2 major_version : 主版本号;

                u2 constant pool count : 常量池计数项;

              cp info constant pool [constanct pool count-1] : 常量池列表;

              u2 access flags : 描述该 java 类型的一些访问标志信息;

              u2 this Class : 值为一个对常量池表项的索引;

              u2 super Class : 该项必须是对常量池表项的一个有效索引或者值为0;

              u2 interfaces count;

              interfaces [interfaces count] : 表示由该类直接实现 或者 由该接口所扩展的超接口的数量;

              u2 fields count;

              field info fields [fields count] : 表示该 java 类型声明的类变量 和 实例变量的个数总和;

              u2 methods count;

              method info methods [method count] : 该 java 类型中所声明的方法的描述;

              u2 attributes count;

              attribute info attributes [attributes count] : 给出了在该 java 类型中所定义的属性的基本信息;

}


Class 文件的常量表:CONSTANT_Utf-8:1;

                                CONSTANT_Integer:3;

                                CONSTANT_Float:4;

                                CONSTANT_Long:5;

                                CONSTANT_Double:6;

                                CONSTANT_Class:7;

                                CONSTANT_String:8;

                                CONSTANT_Fieldref:9;

                                CONSTANT_Methodref:10;

                                CONSTANT_InterfaceMethodref:11;

                                CONSTANT_NameAndType:12(byte);


常量池 容纳的 符号引用 包括 特殊字符串:全限定名 ( java.lang.Object -> java/lang/Object )、

                                                               简单名称 ( 字段名 和 方法名以 简单名称 [非全限定名] 形式出现在 常量池入口 中 )、

                                                                描述符    ( 字段描述符 [给出字段的类型]、

                                                                                 方法描述符 [给出了方法的 返回值 和 方法参数 的 数量、

                                                                                                                                                            类型、

                                                                                                                                                            顺序] )

基本类型终结符 (byte-B / char-C / double-D / float-F / int-I / long-J / short-S / boolean-Z)


字符描述举例:I                                                  int i;

                       [Ljava/lang/Object;                     java.lang.Object[] obj;

                       ([BII)Ljava/lang/String;               String method (byte[] b, int i, int j)

                      ZILjava/lang/String;II()Z             boolean method (boolean b, int i, String s, int j, int k)



常量池:一个可变长度 cp_info 表的有序序列


Java 数组最多只能有 255 维,数组描述符 最多只能有 255 个引导符”[”

字段: 在 类 或者 接口 中声明的每一个字段 (类变量 或者 实例变量),都由 Class 文件中的一个名为 fileld_info 的 可变长度的表 进 

         行描述 (在一个Class文件中,不会存在 两个 相同名字 和 描述符 的字段)。

Access_flags:声明字段时使用的修饰符、

Name_index:简单名称、

Descriptor_index:提供给 字段描述符 的 CONSTANT_Utf8_info 入口的引索、

Arrtibutes_count & attributes:后者由多个 attribute_info 表组成的列表 [Attributes_count 说明数量]


方法:由一个可变长度的 method_info 表来描述


属性:java虚拟机规范定义了9种属性。

为了正确地解释 java Class文件,所有 java 虚拟机实现都必须能够识别下列三种属性,分别是

           Code (在可变长度的 Code_attribut 表中,定义了方法的字节码序列 和 其他信息)、

          ConstantValue (固定长度的 ConstantValue 属性出现在值为常量的字段的 field_info 表中)、

           Exception (可变长度的 Exception 属性列出了方法可能抛出的异常)。

Java中的类:系统类、

                   扩展类、

                   由程序员自定义的类


装载方式:隐式装载 (new / 隐式调用类装载器加载对应的类到 JVM)、

                 显式装载 (class.forName())

 

类装载器:Bootstrap Loader   — —  负责加载系统类 (C++,逻辑上并不存在类实体,装载:JRE 的核心类库)

                 ExtClassLoader       —— 负责加载扩展类 (static,装载:ext 下的 jar 包)

                 AppClassLoader     —— 负责加载引用类 (static 分工、委托模型,装载:classpath 路径下的类包)








第七章  栈和局部变量操作


Java 虚拟机通过 装载、连接 和 初始化 一个java类型,使该类型可以 被正在运行的 java 程序 所使用


实例化一个类:new、

                       调用 Class 或者 java.lang.reflect.Construtor 对象的 newInstance() 方法、

                       调用现有对象的 clone() 方法 (不会调用构造函数)、

                       通过 java.io.ObjectInputStream 类的 getObject() 方法反序列化


一个已经加载的类型 被卸载的几率很小至少被卸载的时间是不确定的。

      (不应该对虚拟机的类型卸载 做任何假设地前提下 来实现系统中的特定功能)


两次加载     的 classloader 是相同的,两个class实例也是相同的,

第二次加载  不是真正意义上的加载,而是直接返回了上次加载的结果


不要等待指定类型的以前版本被卸载,因为卸载行为对 java 开发人员是透明的。

比较靠谱的做法是,每次创建 特定类加载器 的新实例来 加载指定类型 的不同版本。


jClassLib 工具,查看栈操作


对于大数据 (long / double / string),编译器会将其放入到常量池中 (char & byte & short ->  int)








第八章  内存异常和垃圾处理


Java内存分配涉及区域:寄存器             [程序中无法控制]、

                                      栈                   [基本类型的数据 和 对象的引用]、

                                     堆                   [用 new 产生的数据]、

                                     静态域            [在对象中用 static 定义的静态成员]、

                                     常量池            [常量]、

                                     非 RAM 存储  [硬盘等 永久存储空间]


                                                    《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新


栈(栈内存):在函数中定义的一些 基本类型的变量数据,还有 对象的引用变量 都在函数的 栈内存 中分配

在 线程创建 时创建,线程结束,栈就over

栈(Stack) {

            栈帧 (Stack Frame){

                                      方法引索 (Method Index)+

                                      输入输出参数 (Parameters)+

                                      本地变量 (Local Varibales)+

                                      Class File (类)+

                                      父帧 (Return Frame)+

                                      子帧 (Next Frame)…

                                        }

                }

}

堆:堆内存用来存放由关键字new创建的对象和数组

      (栈中的引用变量,可以取值等于数组或对象在堆内存中的首地址,这就是java中的指针)


堆内存 : 新生区(Young Generation Space伊甸区(Eden Space) ->

                        幸存0区(Surivor 0 Space)  +->

                        幸存1区(Surivor 1 Space)) +->    

             养老区(Tenure Generation Space) +

             永久存储区(Permanent Space)

常量池 (Constant Pool):在编译期被确定,并被保存在已编译的 .class 文件中的一些数据

                                        (代码中已定义的各种基本类 [int/long…]、

                                         对象型 [String / 数组…]、

                                         以文本形式出现的 符号引用 [类和接口的全限定名 /

                                                                                    字段的名称的描述符 /

                                                                                    方法和名称的描述符] )

JVM堆和栈的分离原因:JVM栈代表了 处理逻辑,JVM堆代表了 数据;

                                     JVM堆中的内容可以被 多个JVM栈 (线程) 共享;

                                     JVM栈虽然也可以存储数据,只能向上增长,会限制住JVM栈存储内容的能力。

                                              [如果只是存储堆中的地址首地址(句柄),就能实现动态增长];

                                     体现了 面向对象 的编程思想


String s = new String(“asdf”);  ->  String s = “asdf”;

String 不是基本数据类型,是一个对象,默认值是null。

New String() 和 New String(“”) 都是声明一个新的空字符串,但不是 null;


String s = “asdf”; String s1 = “as”+”df”;  s==s1(true)

String s = new String(“asdf”); String s1 = “as”+new String(“df”);  s==s1 (false)

String s = “asdf”; String s1 = new String(“asdf”); s1.intern();  s==s1(false)  s==(s1=s1.intern()) (true)

String str = “as”+” ”+”d”+”f”; 因为 String 是不可变的,会产生很多临时变量 (StringBuffer)

 

Java运行时的数据区域:程序计数器        (Program Counter Register 较小的内存,相当于当前线程所执行的字节码的行指示器)+

                                     Java 的虚拟机栈 (VM Stack 类中方法的执行过程的 内存模型)+

                                    本地方法栈        (Native Method Stack 用 C、C++编写的,提供虚拟机使用本地方法服务的)+               

                                    Java 堆              (Java Heap 类实例和数组的分配空间,是一块所有线程共享的内存区域)+

                                     方法区               (Method Area 虚拟机启动时创建,所有线程共享的内存区域,用于存储被虚拟机加载的

                                                               类信息、

                                                               常量、

                                                              静态变量、

                                                              即时编译器编译后的代码…)+

                                     运行时常量池   (Runtime Constant Pool 方法区的一部分,

                                                             用于存放 编译期生成的 各种字面量 和 符号引用)+

                                     直接内存        (Direct Memory 从JDK1.4版本开始,新加入NIO类,且引入了一种基于

                                                            通道 (Channel) 与

                                                           缓冲区 (Buffer)

                                                            的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在java

                                                堆里面的 DirectByteBuffer 对象作为这块内存的 引用 进行操作)

《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新

 

《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新



主流的对象访问方式:使用句柄、

                                  直接指针


-XX:+HeapDumpOnOutOfMemoryError (OOM java堆溢出)

          可以让虚拟机在出现内存溢出异常时,Dump 出当前的内存堆转储快照 (-Xmx、-Xms 堆参数 [最大值、最小值])


单线程,无论是请求栈帧太大,还是虚拟机栈容量 太小,虚拟机都将抛出 *Error

建立线程太多时,在不能减少线程数 或者 更换 64位虚拟机 的情况下,可以通过 减少最大堆 和 减少栈容量 来换取更多的线程。

              (减少内存 -> 解决内存溢出)


-XX:PermSize   -XX:MaxPermSize 限制方法区大小,间接 限制其中 常量池的容量


向 常量池 添加内容:String.intern() [如果有,就返回代表池中这个 字符串 的对象]


CGLib 之类字节码技术,应用于主流框架 Spring、Hibernate 对类进行加强 (增强的类越多,就需要越大的方法区)


-XX:MaxDirectMemorySize 指定 本机直接内存


内存泄露:堆内存的泄露(不是物理上的消失,而是由于计算错误,失去了对该段内存的控制。

                                      malloc、realloc、new ->  free、delete)

                 [常发性/偶发性/一次性/隐式]

检测内存泄露:MS C-Runtime Library、

                        Purify、

                       BoundsChecker、

                       Performance Monitor (可以检测 隐式 内存泄露)


《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新


常见垃圾收集策略:Reference Counting (引用计数) 、

                              跟踪收集器 (卡片标记 、

                                                 是否有引用指向对象 [Strong reference /

                                                                                 Soft reference /

                                                                                 Weak reference /

                                                                                 Phantom reference [ Mark-Sweep Collector 标记-清除收集器 /

                                                                                                                   Copying Collector 复制收集器 /

                                                                                                                   Mark-Compact Collector 标记-整理收集器]])


JVM 分代收集的策略:新生代 (Mark-Compact 策略)、

                                   老生代 (Full Gc)

System.gc() 强制执行的是 Full Gc


判断对象的存活:根搜索算法 (GC Root Tracing)

宣告对象的死亡:第一次标记 (进行根搜索 没有 和 GC Roots 相连接的 引用链)

                           第二次标记 (是否 有必有执行 finalize()  ->  try-finally)


永久代 的 垃圾收集 主要回收 两部分内容:废弃常量、

                                                                 无用的类 (该类所有实例都被回收,也就是java堆中不存在该类的任何引用;

                                                                                 加载该类的ClassLoader已经被回收;

                                                                                 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方访

                                                                          问该类的方法)

 

垃圾收集算法:新生代 (标记 - 复制算法)、

                       老年代 (标记 - 整理算法)


《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新


垃圾收集器:Serial、

                    ParNew、

                    Parallel Scavenge、

                    Serial Old、

                    Parallel Old、

                    CMS、

                    G1


内存分配 与 回收策略:对象优先在 Eden 分配 、

                                    大对象 直接 进入 老年代 、

                                    长期存活的对象将进入老年代 (Survivor区存放对象,每熬过一次Minor GC年龄就+1,

                                                                                  到达一定数值就进入老年区 [默认 15 ages])、

                                    动态对象 年龄 判断、

                                    空间分配 担保







第九章  高效手段之性能监控工具和优化部署


Sun JDK 监控 和 故障处处理工具:jps    (VM Process Status Tool)、

                                                     jstat  (JVM Statistics Monitoring Tool)、

                                                    jinfo  (Configuration Info for Java)、

                                                    jmap  (Memory Map for Java)、

                                                     jhat    (JVM Heap Dump Browser)、

                                                     jstack  (Stack Trace for Java)

Jps(计算机进程状态工具):列出运行的虚拟机进程,并显示虚拟机执行主类(Main?Class,?main函数所在的类)的名称,

                                         以及这些进程的本地虚拟机的唯一ID(LVMID,Local?Virtual?Machine?Identifier)


                                        Jps [ options ( -q [只输出 LVMID (Local Virtual Machine Identifier),省略主类的名称]、

                                                              -m [输出虚拟机进程启动时 传递给主类 main() 函数的参数]、

                                                                -l [输出 主类的全名,如果进程执行的是 jar 包,则输出 jar 路径]、

                                                               -v [输出 虚拟机进程 启动时 JVM 的参数])] 

                                             [ host id ]

 

Jstat (虚拟机统计信息监视工具):显示 本地 或 远程虚拟机 继承中的 类装载、

                                                                                                       内存、

                                                                                                      垃圾收集、

                                                                                                      JIT编译等运行数据 (多用在 纯文本 控制台 环境的服务器)

 

Jinfo (java 配置信息工具):实时 查看 和 调整虚拟机 的各项参数

 

Jmap (java 内存映像工具):可以生成堆转储快照 (一般为 heapdump、dump),

                                           还可以查询 finalize 执行队列,

                                                                          java 堆 和

                                                                          永久代 的 详细信息 (空间使用率、当前使用的收集器)

 

Jhat (虚拟机 堆 转储 快照分析工具):与 jmap 搭配使用,来分析 jmap 生成的 堆转储快照。

                                                        [内置了一个微型的 HTTP/HTML 服务器]

 

Jstack (java 堆栈跟踪工具):用于生成 虚拟机 当期时刻 的线程快照 (一般称为 threaddump 或 javacore)

 


JDK可视化工具:JConsole    (java 监控 与 管理控制台)

                           VisualVM  (多合一 故障 处理工具)

 






第十章  JVM 参数分析和调优实战


JVM 参数:-server:                    默认根据 服务器硬件 自动选择不同模式

                  -client:                     启动较快,没有 server 模式优化

                  -Xmx:                       java heap 最大值,默认 机器物理内存 的 1/4

                  -Xms:                       java 堆初始化大小,默认 机器物理内存 的 1/64

                  -XX:PermSize:          初始化 永存区域 大小 (Permanent Generation space)

                                                             [运行期不对  PermGen space 进行清理,会出现错误。

                                                              常见于 web 服务器 对 JSP 进行 pre compile , WEB APP 下有大量的第三方 jar,

                                                              大小超过 jvm 默认的 PermSize大小 (4M),就会产生错误信息]

                  -Xmn:                       设置 青年代 大小 (JVM可用内存 = 青年代 + 老年代 + 持久代)

                                                              [持久代一 般固定大小64M,增大年轻代后,将会减小老年代大小。

                                                               Sun官方推荐配置 为整个堆的 3/8]

                  -XX:NewRatio:          控制默认的 Young 代大小。

                                                      如果等于3,意味着 Young 代 和 Old 代比率是 1:3,

                                                                         也就是 Eden 和 Survivor 空间总和是整个堆大小的 1/4

                  -XX:SurivorRation:   设置年轻代中 Eden 区与 Survivor 区的大小比值。

                                                              [设置为 4, 则两个 Survivor 区与一个 Eden 区的比值为 2:4,

                                                                                一个 Survivor 区 占整个年轻代的 1/6。

                                                                越大的 Survivor 空间可以允许 短期对象 尽量在 年轻代 消亡;

                                                                如果 Survivor 空间太小,Copying 收集将直接 将其转移到 老年代 中,

                                                                                                     这将加快 老年代 的 空间使用速度,引发频繁的 完全垃圾回收]

                  -XX:NewSize:            对包含短期存活对象的池的大小 进行设置,

                                                使该池的对象存活时间 不会超过一个 垃圾回收循环。

                                                      新生成的池的大小由 NewSize 和 MaxNewSize 参数决定。

                                                               [java 新对象生成堆内存,通常为 1024 的整数倍,并且大于 1MB。

                                                                一般情况下,这个值是 最大堆内存 (Maximum Heap Size) 的 1/4。

                                                                这个选项用来 增大 较大数量的 短生命周期对象。

                                                                增加 java 新对象生产 堆内存 相当于增加了 处理器 的数目]

                 -XX:MaxNewSize:     最大 java 新队形生产堆内存

                                                               [数值规则 和 -XX:NewSize 相同,且 一般要将 NewSize 和 MaxNewSize 设成一致]








第十一章  虚拟机类的加载机制


类从加载到虚拟机到卸载,整个生命周期:

                                                            加载      (Loading)

                                                           验证      (Verification)

                                                           准备      (Preparation)

                                                           解析      (Resolution)

                                                           初始化  (Initialization)

                                                           使用      (Using)

                                                           卸载     (Unloading)


加载:通过一个 类的 全限定名 来获取定义此类的二进制字节流

          将这个 字节流 所表示的 静态存储结构 转化为 方法区域 的运行时数据结构

          在 Java 堆中生成的一个代表这个类的 java.lang.Class 对象,作为 方法域 数据 的访问 入口


验证:文件格式 验证

          元数据 验证

          字节码 验证


准备:正式为 类变量 分配内存,并设置 类变量 初始值 的阶段

解析:类 或 接口 解析

          字段 解析

          类方法 解析

          接口方法 解析


初始化:根据 程序员 通过 程序 制定的计划来 赋值


类加载器 (安全方面):保护 善意代码 不受恶意代码的干扰、

                                  保护 已验证 的 类库、

                                  代码放入不同的 行为限制 的 各个保护域中


ClassLoader 中与 加载类 相关的方法:getParent ()

                                                           loadClass (String name) 

                                                          findClass (String  name)

                                                          findLoadedClass (String name)

                                                          defineClass (String  name, byte[] b, int off, int len) 

                                                          resolveClass (Class<?> c)


类加载器:引导类加载器 (Bootstrap Class Loader)、

                 扩展类加载器 (Extendsions Class Loader)、

                 系统类加载器 (System Class Loader)

                                       [ClassLoader.getSystemClassLoader() 来获得]

线程上下文类加载器 (Context Class Loader):getContextClassLoader()、

                                                                       setContextClassLoader(ClassLoader cl)


Class.forName (String name、boolean initalize、ClassLoader loader):name 类的全名、

                                                                                                                  Initalize 是否初始化类、

                                                                                                                  loader 当前类的类加载器


双亲委派模型:启动类加载器 (C++ 实现,虚拟机的一部分)、

                       所有其他的类加载器 (都有 java 语言实现 , 独立于虚拟机外部 , 并全部继承自抽象类 java.lang.ClassLoader)


自己开发虚拟机:文件系统类 加载器、

                           网络类 加载器

 

OSGi:java 上动态模块系统 (提供了面向服务 和 基于组件 的 运行环境 , 并提供 标准的方式 用来管理软件 的 生命周期)







第十二章  研究高效之魂


Java 虚拟机相对于 硬件机器:没有 寄存器、

                                              使用 精简 指令集、

                                              一些指令为面向对象设计的 , 比如有一个指令 取出 指定的对象实例 的 某个字段


栈帧 (Stack Frame):用于支持虚拟机进行 方法调用 和 方法执行 的数据结构

                                       [是虚拟机运行时 数据区 中的虚拟机栈

                                                                                   (Virtual Machine Stack) 的栈元素]


栈:(数据结构):操作数栈 (存放 指令 的参数)

                          局部变量表

       动态连接         [每个栈帧都 包含 一个指向 运行时常量池 中 该栈帧所属方法的引用]

       方法返回地址  [执行引擎遇到 任意一个方法 返回 的字节码指令,这种方法称为 正常完成 出口  / 

                              在方法执行过程中遇到 无法处理 的异常,这种方法称为 异常完成出口]


每当启用一个 线程 时,JVM 就为它分配一个 java 栈,栈 是以 帧 为单位保存当前 线程的运行状态。


操作数栈 (操作栈):后入先出的栈


附加信息:允许 具体的虚拟机 实现增加一些规范里 没有描述 的 信息到栈帧中 [动态链接、

                                                                                                                       方法返回、

                                                                                                                      其他附加信息,称为栈帧信息]


方法调用:确定被 调用方法 的 版本,即 调用哪一个方法,暂时 还不涉及方法内部 的 具体运行过程


方法调用 的种类:invokestatic  +  invokevirual  +  invokespecial  +  invokeinterface


Class类方法的 存放地方 和 属性:JVM 有一个所有线程间共享的“方法区”

                               [存储每个类结构 的常量池、域、方法数据、方法 和 构造函数]

方法的早绑定:static 方法

                        private 方法

解析:调用目标在 程序代码写好、编译器进行编译时 就确定下来。

          这类方法的调用称为解析。

          (解析调用一定是个 静态的过程,在编译期间就完全确定,在类装载的解析阶段就会把 涉及的符号引用 全部转变为 可确定的

           直接引用,不会延迟 到 运行期 再去完成)


面向对象 的 三个基本特征:继承、

                                          封装、

                                         多态 (重写 [Override])


向上转型是安全的:byte -> short -> char -> int -> long -> float -> double -> character  -> serializable -> Object


目前 Java 语言是一门 静态多分派、动态单分派 的语言


为了避免 动态分派 的 频繁搜索:在类的方法去建立一个虚方法表 (Virtual Method Table, vtable)

                                                  在 invokeinterface 执行时,会用到接口方法表 (Interface Method Table, itable)

                                                         [条件允许,还会使用内联缓存 (InLine  Cache) 和

                                                          基于“类型继承关系分析 (Class Hierarchy  Analyss, CHA)”技术的

                                                                    守护内联(Guarded Inlining) 这两种非稳定的“激进优化”手段]


Java 的 本地代码 的编译器 (Gun Compiler for the Java , GCJ)


基于栈的指令集:        可移植性,

                                   寄存器由 硬件 直接提供,

                                   程序 直接依赖这些 硬件寄存器,则不可避免地受到 硬件的约束

基于寄存器的指令集: 执行速度相对速度慢,出栈、入栈操作、频繁的 栈访问、频繁的 内存访问







第十三章  类加载器和执行子系统


一个功能健全的 Web 服务器:部署在同一个服务器上的两个 Web 应用程序所使用的 java 类库可以实现互相 隔离;

                                              部署在同一个服务器上的两个 Web 应用程序所使用的 java 类库可以相互 共享;

                                             服务器需要尽可能地保证自身的安全不受部署的 Web 应用程序影响;

                                             支持 JSP 应用的 Web 服务器,十有八九都需要支持 HotSwap 功能。


《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新


可以存放 java 类库的 tomcat 目录结构: /common(可被 Tomcat 和所有的 Web 应用程序共同使用)

                                                               /server(可以被 Tomcat 使用,对所有的 Web 应用程序都不可见)

                                                               /shared(可被所有 Web 应用程序共同使用,但对 Tomcat 自己不可见)

                                                               /WEB-INF(仅仅可以被此 Web 应用程序使用,对 Tomcat 和其他 Web 应用程序都不

                                                                              可见)


Tomcat 加载顺序:准备要启动 Tomcat,调用 Bootstrap 的 main 方法

                              在 Tomcat 启动之前,需要加载类,就需要类加载器,于是调用方法完成初始化工作 init()

                             init() 方法开始工作后再调用 initClassLoaders() 方法

                              发现需要初始化 3 个类型的类加载器,要调用 createClassLoader (name, parent),通过此方法告诉它要初始

                        化哪些类型

                             通过 CatalinaProperties 类去联络 catalina.properties,获得哪种 类加载器 加载 哪些类的信息

                             完成初始化,并返回结果

                             Tomcat 加载其他资源(待续),启动成功


OSGi 的 类加载器构架 (Open Service Gateway Initiative):一个基于 java 语言的 动态模块化规范


字节码生成技术:JaVassist   CGLib   ASM


《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》 - 温故而知新


动态代理:就是程序运行的时候,JVM 能动态得知道 它要对 哪个类 的 哪些方法 进行代理,实现程序的 可重用性

                 [java.lang.reflect.Proxy

                  java.lang.reflect.InvocationHandler

                  java.lang.ClassLoader]


实现java动态代理:实现 InvocationHandler 接口创建自己的 调用处理器;

                              为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建 动态代理类;

                             通过 反射机制 获得动态代理类的 构造函数,其唯一参数类型是调用处理器接口类型;

                             通过 构造函数 创建 动态代理实例,构造时 调用处理器对象 作为参数被传入。

接口数量上限:65535







第十四章  编译优化


Java编译优化:方法 尽量短,大方法 要分解成 小方法;

                        不要重复发明*。

编译类型:即时编译、

                HotSpot 动态编译、

                持续重新编译、

                栈上 (On-stack) 替换


C:>javac options filename.java


Options 选项 :   -classpath path : 用于设定路径,在该路径上 javac 寻找需要被调用的类。

                                                     该路径是一个用 分号 分开的目录列表;

                          -d directory :      指定一个根目录。该目录用来创建反映 软件包 继承关系的 目录数;

                         -g :                     在代码产生器中打开调试表,以后可凭此调试产生字节代码;

                         -nowarn :           禁止编译器产生警告;

                         -o :                     告诉 javac 优化由内联的 static、final 以及 private 成员函数产生代码;

                         -verbose :          告知 java 显示出有关被编译的 源文件 和 任何被调用类库 的信息


注解 处理器:initPorcessAnnotations() 中 初始化 +

                     processAnnotations() 中完成 执行

语义分析 与 字节码生成:检查 标注 +

                                       数据 及 控制流 分析 +

                                       解 语法糖 +

                                       字节码 生成


Java语法糖:泛型 与 类型擦除

                           [仅仅是对 方法的 Code属性 中的 字节码 进行擦除,

                            实际上原数 据中还是保留了泛型信息,

                            是我们能通过 反射手段 取得 参数化 类型的根本依据]

                           (public static String method (List<String> list));

                    自动装箱、自动拆箱 与 遍历循环


JDK 1.6 新增的 javax.lang.model 中定义了16类 Element,

                                   包                                     (PACKEAGE)  

                                   枚举                                  (ENUM) 

                                  类                                     (CLASS)  

                                  注解                                  (ANNOTATION_TYPE)  

                                  接口                                  (PACKAGE)  

                                  枚举值                               (ENUM_CONSTANT)  

                                  字段                                  (FIELD)  

                                  参数                                  (PARAMETER)  

                                  本地变量                           (LOCAL_VARIABLE)  

                                  异常                                  (EXCEPTION_PARAMETER)  

                                  方法                                  (METHOD)

                                  构造函数                           (CONSTRUCTOR) 

                                  静态语句块                        (STATIC_INIT) 

                                  实例语句块                        (INSTANCE_INIT)  

                                  参数化类型                        (TYPE_PARAMETER)  

                                  未定义的其他语法树节点    (OTHER)






第十五章  运行期优化


Java编程语言:动态“安全的”

                        在“堆(heap)”上对 所有对象 进行分配

                       大部分 方法调用 时“虚拟的”(潜在是 多态的)

                      由于其强大的 动态类装载 的能力,因而可“在运行中”发生改变

Java HotSpot 性能引擎:热点 Hot Spot 检测 [基于 采样 的 热点探测、

                                                                      基于 计数器 的 热点探测]

                                       方法内嵌

                                      动态逆优化

                                      优化编译器


热点代码:被 多次 调用 的 方法

                 被 多次 执行 的 循环体


优化技术:公共 子 表达式 消除

                 数组 边界 检查 消除

                 方法 内联

                 逃逸 分析






第十六章  内存模型和线程


线程操作某个对象,执行顺序:从 主存 复制变量 到 当前工作内存 (read and load)

                                               执行代码,改变 共享变量值 (use and assign)

                                              用工作内存数据 刷新 主存 相关内容 (store and write)

Java 内存模型定义了8种操作:lock

                                               unlock

                                              read

                                              load

                                              use

                                              assign 

                                              复制 

                                              store 

                                              存储 

                                              write


Volatile 型变量


并发:原子性 、

          可见性 、

          有序性


线程的实现:使用 内核线程 实现、

                    使用 用户线程 实现、

                    混合 实现

                    [ java 线程的实现 “绿色线程”(Green Threads)]






第十七章  安全和优化合二为一


各种操作共享的数据:不可变

                                  绝对线程安全

                                 相对线程安全

                                 线程兼容

                                 线程对立

 

线程安全的实现方法:互斥同步、

                                 非阻塞同步、

                                 无同步方案

 

无状态类:线程安全的类一般是无状态的对象,或者类里面的变量时不可变的

 

锁优化:自旋锁 与 自适应自旋

              锁消除

              锁膨胀

              轻量级锁

              偏向锁







☆ 知新 ☆


.class以字节对齐、以字对齐

全限定名:可以用于配置文件,在工程中定位到相应的class

Java编译器给出了init() ,可以被程序员创建的init()覆盖

JIT编译器、逃逸分析技术

为什么数组最高255维

实例化一个类的方式有?哪一种不需要构造函数

为什么接口的数量最多是65535