路边飞的博客

直面血淋淋、湿漉漉、暖洋洋的程序人生

JAVA并发编程实践-笔记

| Comments

  1. 介绍
    1. 线程的优点
      1. 使用多处理器。这是多核的时代。
      2. 模型的简化。有些应用模型就是需要并发处理,如servlet,一个内置的多线程功能,可以很好的匹配这种模型,并且不需要考虑诸如任务的创建、切换、负载均衡等细节问题。
      3. 对异步事件的简单处理。单线程环境下,对于一些可以导致阻塞的任务,如socke.read,需要使用异步事件的机制,这使得实现起来复杂得多。而多线程很多时候可以解决这类问题。
      4. 没有多线程的GUI,容易由于某一个事件完成时间长,而导致界面冻洁。
    2. 线程的风险
      1. 安全风险。 并发处理共享变量时,容易导致意外的执行结果。
      2. 活跃度的危险。 死锁、饥饿、活锁等。
      3. 性能危险。 上下文切换导致的性能开销。
  2. 基础

    1. 线程安全。

      1. 线程安全,关键在于“正确性”,这意味着一个类与它的规约保持一致,通常可简单认为在多线程环境下不会比单线程环境下有更多的隐患。

      2. 线程安全的代码本质就是:

        管理对状态的访问,并且通常是共享的、可变的状态。
        一个对象的状态就是它的数据,包含了任何会对它的外部可见行为产生影响的数据。
        共享是指一个变量可以被多个线程访问。
        可变是指其生命周期内可以被改变。

      3. 当多线程访问一个类时,如果不用考虑这些线程在运行环境下的调度和交替执行,并且不需要额外的同步及调用方代码不必作其他的协调,这个类的行为仍然正确的,那么称这个类是线程安全

      4. 无论何时,只要多于一个线程访问给定的状态变量,而且其中某个线程会写入该变量,此时必须使用同步来协调线程对变量的访问。

    2. 原子性

      1. 竞争条件(race codition)
        1. 当正常性依赖于运行时的时序时,会产生竞争条件
        2. 常见:读-改-写 (read-modify-write),检查-运行(check-then-act)
        3. 如 单例的LazyInit
        4. ref:数据竞争(data race)
      2. 为了保护状态的一致性,要在单一的原子操作中更新相互关联的状态变量

        1. 内部锁 ssynchronized ,每一个java对象都可以当作一个内部锁(intrinsic lock),或监控器锁(monitor lock)
        2. 重入锁(reentryncy)
          1. 是per-thread 而非 per-invocation。 通过记录计数器占用线程实现
        3. 锁策略
          1. 每个共享的可变变量都需要由唯一一个确定的锁保护,而维护者应用清楚这个锁。

          2. 对于每一个涉及多个变量的不变约束,需要同一个锁保护其所有的变量

      3. 活跃度与性能
        1. 通常简单性与性能之间是互联牵制的。实现一个同步策略时,不要过早地为了性能而牺牲简单性。

        2. 有些耗时的计算或操作,比如网络或控制台I/O,难以快速完成,执行这些操作期间,不要占用锁。

    3. 共享对象

      1. 可见性。
        1. 重排序。在单个线程里,只要重排序对结果不会产生影响,那么不能保证程序按写定的顺序执行。这是为了更好的利用CPU的性能。 但是多线程环境下,确可能产生不良影响。
        2. 只要数据被跨线程共享,就进行恰当的同步

        3. 过期数据

        4. 非原子的64位操作。 64位数据,double和long是非原子的,jvm允许将其读或写划分为两个32位操作。所以有可能出现读的一值的高32位和另一个值的低32位。因而,一般作为共享变量时,申请为volatile或用锁保护起来民,以保证其可见性。

        5. volatile。保证可见性,并且不会被重排序。

          用于确保它们所引用的对象状态的可见性,或者用于标识重要的生命周期事件的发生。

      2. 发布和逸出

        1. 发布 一个对象的意思是使它能被当前范围之外的代码所引用
        2. 逸出 是指一个对象在尚未准备好的时候就将它发布
      3. 线程封闭。

        1. 将对象封闭在某一个线程,如ThreadLocal或JDBC线程池Connect
        2. ad-hoc
        3. 栈限制
        4. ThreadLocal
      4. 不可变性

        1. 不可变对象总是线程安全的
        2. final域。 保证域值的不可为,并且保证初始化安全性。
        3. 满足以下条件,才是不可变:
          • 它的状态不能在创建后再被修改
          • 所有域都是final类型,并且
          • 它被正确创建(创建期间没有发生this引用逸出)
      5. 安全发布

        1. 一个正确创建的对象可以通过以下条件安全地发布:
          1. 通过静态初始化器初始化对象的引用
          2. 将它的引用存储到volatile域或AtomicReference
          3. 将它的引用存储到正确创建的对象的final域中
          4. 或者将它的引用存储到由锁正确保护的域中

    3.组合对象

     1. 不变约束,后验条件,先验条件
     2. 不理解对象的**不变约束**和**后验条件**,你就不能保证线程安全,要约束状态变量的有效或者状态转换,就需要原子性与封闭性。
    
  3. 构建并发应用程序

  4. 活跃度、性能和测试
  5. 高级主题

复述

  1. 一句话:

这是以javaSE5、6为原型讲并发编程的原理和实践的书

JVM虚拟机-笔记

| Comments

Java虚拟机是什么

不同的场景,可能意味着不同的东西,包括:

  • 一种抽象规范
  • 一种规范实现
  • 一个运行实例

生命周期

从main()函数开始,当所以非守护进程退出,或System.exit()时结束

体系结构

需要先明确: JVM规范定义的子系统、运行时数据区等,都只是一个抽象定义,只要能满足这些外部的行为,内部的具体实现完全可以由设计者决定。

  1. 系统运行时结构

  2. Java栈运行时结构

  3. 数据类型

    字(word) 一个字没有固定长度,但要求:

    • 一个字可以表示int,byte,float等
    • 两个字可以表示long,double
    • 一个字可以表示一个引用

    所以,通常一个字最少32bit。一般是特定平台上的指针长度。

    特殊类型

    • boolean用byte或int表示
    • returnAddress,只用于虚拟机内部

    引用类型

    三种引用

    • 类类型
    • 接口类型
    • 数组类型

    还有一个特殊的null值

    取值范围

  4. 装载class

    ClassLoader

    1. findClass,loadClass
    2. defineClass
    3. resovleClass

    具体见: 基本沙箱/类加载器(ClassLoader)

  5. 方法区

    存储加载的类型信息

  6. 存储类实例和数组

    垃圾收集

    规范要求提供垃圾收集功能,但是不强求收集算法。

    对象的内部表示

    两种可选的模型:

    一定是需要有一个类型信息的引用。因为:

    1. 强制转型时需要检查;
    2. 引用并不一定引用真正的类型信息,在动态绑定时查找需要。

    对象的特殊信息

    1. 方法表。用于加快方法访问。

    2. 锁及其wait Set

      每一个对象都可以当做锁。但是,并不是每一个对象都会使用到,所以有可能把所有锁保存在以对象地址为索引的搜索树中。

      wait set也是同样的原理。

    3. 垃圾收集标志

      是否被回收了,是否调用finalizer方法等。

    数组对象的内部表示

    数据对象也是对象

  7. 程序计数器(PC)

    • 每一线程都有独立的PC
    • PC的长度为一个字(word).既可以保存一个引用,也可以保存一个returnAdress
    • PC指向下一条指令的地址(可能是内存地址,也可能是相对偏移量),如果是本地方法,则为undefined
  8. Java栈

    • Java栈的基本单位是栈帧,只有两种操作,压栈跟出栈
    • 每一次方法调用都是一个栈帧。当前方法的栈帧称为当前帧,当前方法所属的类称为当前类,当前类的常量池称为当前常量池
    • 每种方法返回(出栈),一个是return,一个是抛出异常
    • 栈数据是私有的,其他线程是无法访问的
  9. 栈帧

    由三部分组成:

    • 局部变量区
    • 操作数栈区
    • 帧数据区

    其中,局部变量区和操作数栈的大小在编译期已经确定。

    局部变量区

    局部变量是以字长为单位的数组,包括了方法实参和运行过程中的局部变量。每一个基本类型(long跟double除外)和引用占一个字,而long和double占用两个字

    左边是一个类方法,而右边是一个实例方法。实例方法的第一个变量是对自身的引用。

    操作数栈区

    操作数栈也是以字长为单位的栈,用于存储字节码执行过程的参数、结果等。两个数的相加过程:

    帧数据区

    用于支持常量池解析、正常方法返回、异常派发及其他一些任务。

    • 常量池解析

      例子:

        public class Main {
            private final static int n = 3;
      
            public static void main(String[] args) {
                int a = n + 1;
            }
        }
      

      常量池为:

        Constant pool:
        #1 = Class              #2             //  com/troy/Main
        #2 = Utf8               com/troy/Main
        #3 = Class              #4             //  java/lang/Object
        #4 = Utf8               java/lang/Object
        #5 = Utf8               n
        #6 = Utf8               I
        #7 = Utf8               ConstantValue
        #8 = Integer            3
        #9 = Utf8               <init>
        #10 = Utf8               ()V
        #11 = Utf8               Code
        #12 = Methodref          #3.#13         //  java/lang/Object."<init>":()V
        #13 = NameAndType        #9:#10         //  "<init>":()V
        #14 = Utf8               LineNumberTable
        #15 = Utf8               LocalVariableTable
        #16 = Utf8               this
        #17 = Utf8               Lcom/troy/Main;
        #18 = Utf8               main
        #19 = Utf8               ([Ljava/lang/String;)V
        #20 = Utf8               args
        #21 = Utf8               [Ljava/lang/String;
        #22 = Utf8               a
        #23 = Utf8               SourceFile
        #24 = Utf8               Main.java
      
    • 正常方法返回

      当return时,需要出栈,并将PC设置被调用方法的指令,并将返回值压入栈(如果有返回值)

    • 异常派发

      方法基本信息会保存一个异常表,表示每一个catch保护的范围。当异常发生的时候,需要查找此表,看是否有合适的处理(catch代码),如果没有,则出栈,并在当前栈执行同样的判断。

    • 其他任务

      比如调试信息(行号等)

    栈帧的可能实现方式

    • 每一个栈帧都是独立的内存空间
    • 相邻两个栈帧间交叠一部分区域。将前一个栈帧的操作数栈区的栈顶作为下一个栈帧的局部变量区的起始。如:

  10. 本地方法栈

    当线程调用本地方法的时候,就进入一个不受虚拟控制的世界。本地方法本质上是依赖于实现的,虚拟机设计者可以自由地决定Java如何调用本地方法。

    一个C模型的例子:

    对于本地方法的要求:

  11. 执行引擎

    执行引擎是JVM的核心,系统的行为就是由它执行字节码流决定的。每一个用户线程其实都是一个执行引擎实例(垃圾收集线程等系统线程不一定)。

    指令集

    指令=操作码 + 0到多个操作数 , 一个操作码一个字的长度。操作数可以来自常量池、操作数栈以及局部变量表。

    例子:

    public class Main {
        private int m = 0;
    
        public void test() {
            m++;
    
            int i=0;
            for(int j=0;j<10;j++){
                i++;
            }
        }
    }
    

    编译后的test方法的字节码:

     Code:
       stack=3, locals=3, args_size=1
          0: aload_0
          1: dup
          2: getfield      #12                 // Field m:I
          5: iconst_1
          6: iadd
          7: putfield      #12                 // Field m:I
         10: iconst_0
         11: istore_1
         12: iconst_0
         13: istore_2
         14: goto          23
         17: iinc          1, 1
         20: iinc          2, 1
         23: iload_2
         24: bipush        10
         26: if_icmplt     17
         29: return
    
    • iconst_0 的操作数是0,它是往栈顶添加常量0
    • getfield #12 的操作数是常量池的符号
    • iinc 的操作数是局部变量
    • iadd 的操作是1,是取栈顶的数+1,并且再压回栈顶。

    影响指令集设计的几个方面

    • 为了达到平台无关性,指令集的设计是以栈为中心的,大部分指令的操作数都是基于操作数栈的,而局部变量通常被视为类似于寄存器,而常量池主要是存储的一些符号。
    • 以栈为中心还有另一个目的是(没看懂。。。*)

    • 字节码的紧凑性。但是严格的字节对齐,牺牲了一些优化的可能性。

    • 字节码的可验证性。为了安全,在加载的class的时候,需要进行字节码验证。为了更好的支持这个过程,相当部分指令需要指明指令的类型(方便校验),比如store操作,对于int是istore,对于float是fstore,虽然它们的动作是一模一样,甚至可能实现也是一样的。

    决定下一条指令的三种可能

    • 紧跟在当前字节码后的下一条字节码
    • goto 或 return 指令
    • 抛出异常时,需要检查异常表,决定是处理还是抛出

    执行技术

    • 直接解释字节码。
    • 即时编译。全部转换成本地代码)
    • 自适应编译。把最频繁的10-20%代码编译成本地代码。
    • 硬件芯片编译。

    线程

    Java定义了线程模型,目标是希望可以在不同的体系结构上实现它。

    • 线程的优先级是非常宽松的,可能对应本地的线程级别,也可以是另外的实现。优先级仅仅是表示这种时间片分配的期望,而非一个严格的标准。
    • 线程必须支持同步的两个方面: 对象锁定 以及 线程等待与通知
    • 线程的对于基本类型的操作必须是原子性的,除了非volatile的double和long(因为它们占了两个字,所以有可能是两步操作)
    • 等等

JVM网络移动性-笔记

| Comments

为什么需要网络移动性?

  1. 计算的模式发展

    从大型分时计算机到个人计算机集群的转变,使得计算模式从中心分时架构,变为N层C/S架构,再到分布式架构

  2. 一种新的软件模式

    逻辑跟数据 跟 运行容器 分离,可以在运行时,按需从网络获取相关的逻辑跟数据,组装成系统。其实就是B/S模式。

    可以解决代码的管理、升级、配置问题。

  3. 对网络移动性的支持

    • 动态连接。热加载class,避免一次性下载。
    • 动态扩展。允许根据需要动态地扩展class集。
    • class文件格式的紧凑性
    • JAR包。减少下载次数。
  4. applet

  5. JINI

    • 技术: 服务注册中心 +  对象序列化 + RMI
    • 角色: 查找服务 + 服务提供者 + 客户机
    • 协议: 网络协议(探索) + 对象协议(加入、查找)
  6. 代码与对象的网络移动性是Java的设计中心

    安全性 和 平台无关性 很多程度上为这上目标服务的。

JVM安全-笔记

| Comments

为什么需要安全?

安全模型使Java 成为网络环境的技术,因为它们建立了对网络移动代码安全执行的必要的可信机制。Java安全模型侧重于保护终端用户免受从网络下载的、来至于不可靠来源的、恶意程序的侵犯。而“沙箱”机制成为了这一目的的支持机制,在“沙箱”中存放不可信的 Java程序。“沙箱”对不可靠的程序的活动进行了限制,程序可以在“沙箱”的安全边界内做任何事,但是不能进行任何跨越这些边界的举动。

比如说, 在版本 1.0 中的沙箱对于很多不可靠的 applet 进行了如下限制:

  • 对本地硬盘的读写
  • 进行任何网络连接,但是不能连接到提供这个applet 的源主机
  • 创建新的进程
  • 装载新的动态链接库

JVM的安全发展历史

  1. 1.0 基本沙箱。 可以严格控制代码可以做什么,不能做什么(访问控制)。
  2. 1.1 代码签名与认证。 基本沙箱的权限控制太过严格,导致很多正常的代码不能运行,所以引入代码签名与认证。可以针对一系列信任的代码(如JAR)提供信任策略。
  3. 1.2 细粒度的控制。 代码签名与认证的信任策略只能是完全信任和完全不信任,1.2引入细粒度的控制

1.0 基本沙箱

  1. 基本组成

    • 类加载器结构
    • Class文件检验器
    • 内置于Java 虚拟机的安全特性
    • 安全管理器及Java API

    1.0、1.1一般需要定制安全管理器(ScurityManager)来实现定制,1.2后提供一个默认的安全管理器,这个管理器支持使用策略文件的形式来定制。

  2. 类加载器(ClassLoader)

    类加载器在以下三个方面对Java 的沙箱起着作用:

    • 它防止恶意代码去干涉善意的代码
    • 它守护了被信任的类库的边界
    • 它将代码归入某类(称为保护域),该类确定了代码可以进行哪些操作

    双亲委派

    这是一个ClassLoader的查找过程,会在双亲中查找有没有加载相关的类,如果有,则有使用双亲中的类,如果没有,再由它加载。其中,启动类装载器标准扩展类装载器是JVM的系统装载器,不可更改的,而类路径装载器网络装载器是属于用户可定制的装载器。

    这样保证了基本类总由可信任的ClassLoader加载,避免一些类似Java.lang.Integer这样的类被篡改。

    命名空间

    不同ClassLoader之间,除非有父子关系(直接或间接),不然彼此之间是不可见的。比如同样一个类com.troy.Test,如果由两个不同的ClassLoader加载,就是不同的两个类(虽然行为是一样的),在JVM里分属不同的运行时包

  3. Class文件检验器

    保证装载的class文件有正确的内部结构,以及各个class文件是协调的。其安全目标就是保证程序的健壮性。毕竟,class文件不一定是由正常的编译器编译的,也有可能是黑客炮制的。

    总共四趟检查:

    1. 第一趟:检查class文件的结构。

      就是检查class文件是否符合class文件格式,比如以0xCOFEBABE开头

    2. 第二趟:类型数据的语义检查

      检查各个部分组成部分是否所属类型的实例,结构是否正确。

      比如,检查方法描述符是否已经存储为字符器,并且符合上下文件无关文法。

      检查是否符合特定条件

      比如,检查一个类(除Object)外,是否都有有超类;final类没有被子类化,final方法没有被覆盖等。

    3. 第三趟:字节码验证。对字节码流进行数据流分析。

      保证指序操作的合法性

      比如,检查方法调用的参数类型是否正确,检查局部变量在初始化前不能被使用,操作数栈的总是包含正确的数值和类型等

      安全检查

      类似于“停机问题”,JVM也不可能写出一个可以检查出所有安全问题的程序。所以,策略是判定字节码流是否符合特定的规则集合,如果是,则判定为安全,如果不是,则视为不安全。

    4. 第四趟:符号引用验证。

      确保引用的正确性——从被验证的class文件,到被引用的class文件。因为JVM通常会使用懒加载机制,所以,这一趟通常是符号真正被引用的时候,才会进行。

      动态连接

      符号引号解析为直接引用的过程。当JVM的操作码,它第一次使用了另一个类的引用时,需要进行解析:

       1. 查找被引用的类(如果需要,就加载它)
       2. 将符号引用替代成直接引用,例如一个类的方法、字段的指针或偏移量。
      

      解析操作只有第一次引用时需要,JVM会记住相关的指针或偏移量,以后就直接使用直接引用。

      保证二进制兼容性

      比如:

      • 被引用的类是存在并且可以被合法加载的;
      • 被引用的类的方法、字段是存在的;
      • 被引用的类的方法返回的类型是合法的;
      • 被引用的类的字段类型是合法的。
  4. 内置于Java 虚拟机的安全特性

    • 类型安全的引用转换
    • 结构化的内存访问(无指针算法)
    • 自动垃圾收集(不必要显式的释放被分配的内存)
    • 数组边界检查
    • 空引用检查
  5. 安全管理器及Java API

JDK文档中的安全管理器(SecurityManager)的定义:

安全管理器是一个允许应用程序实现安全策略的类。它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它。应用程序可以允许或不允许该操作。

SecurityManager 类包含了很多名称以单词 check 开头的方法。Java 库中的各种方法在执行某些潜在的敏感操作前可以调用这些方法。对 check 方法的典型调用如下:

SecurityManager security = System.getSecurityManager();
if (security != null) {
    security.checkXXX(argument,  . . . );
}

因此,安全管理器通过抛出异常来提供阻止操作完成的机会。如果允许执行该操作,则安全管理器例程只是简单地返回,但如果不允许执行该操作,则抛出一个 SecurityException。该约定的唯一例外是 checkTopLevelWindow,它返回 boolean 值。

兼容问题

在新的JVM版本已经提供了一个默认实现,SecurityManager.checkXXX是老式方式,并不建议使用,但为了向下兼容,使用了以下的委派链。事实上,真正实现安全策略的是在AccessController类

SecurityManager的checkXXX系列方法 (委派)—> SecurityManager.checkPermission (委派)—> AccessController.checkPermission

1.1 代码签名与认证

认证策略可以让用户在一个沙箱中,实现多种安全策略

  1. 代码签名

基础知识:散列、数字指纹、公私钥、数字签名。

签名过程:

验证签名过程:

  1. 认证

基础知识证书链

为的是解决签名过程中公私钥的发布的安全问题

1.2 细精度控制

  1. 策略

    依赖于代码签名与认证的能力,可以使用一个策略描述文件,来描述不同的签名或codebase的权限。

    策略的类结构

    一个策略文件的例子

  2. 保护域

    策略文件中,一段策略描述就是一个保护域。保护域描述了CodeSource和PermissionCollection,在ClassLoader加载某一个类的时候,会将这个类与一个保护域关联起来。如下:

  3. 访问控制器(AccessController)

    原理

    AccessController是SecurityManager默认实现的真正执行类。由Accesscontroller的checkPermission() 实现的基本算法决定了调用栈中的每个侦是否有权执行潜在不安全的操作。 每一个栈帧代表了由当前线程调用的某个方法, 每一个方法是在某 个类中定义的, 每一个类又属于某个保护城,每个保护城包含一些权限,因此, 每个栈帧间接地和一些权限相关。

    Accesscontroller的checkPermission()至顶向下检查栈,只有有一个栈帧的权限不够,就会抛出异常。

    AccessController.implies方法

    这是Permission的方法,用于判断一个Permission是否蕴含另一个Permission,比如/var/* => /var/temp。

    一般来说,是调用我们描述的Permission.implies方法,来判断当前的Permission请求是否可以通过。

    AccessController.doPrivileged方法

    默认情况下,AccessController是会检查所有的堆栈,隐藏的结果就是,如果一个比较低权限的类调用比较高权限的类时,只能使用比较低的权限,而自动放弃同的权限。这在很多场合下是过分严格的,比如,一个受限无访问文件权限的类,调用了一个类,而这个需要去读取一个配置文件,这样的操作是会被禁止的。

    所以,为了避免这个情况,提供了一个方法doPrivileged静态方法,这个静态方法的作用是截断权限检查的栈帧,如下:

    这样的话,检查的栈帧就只会检查到此方法而已,如下(假设在Firend调用了以上的doPrivileged代码):

目前的不足

  1. 不能应付以下两种情况:

    • 耗尽内存
    • 不断开新的Thread
  2. 没有跟系统用户集成

基于此章写的示例:一个类似于ACM竞赛的判题系统

判题系统

简单的实现,包括沙箱跟题目判定,目前没有限内存,也没有严格取得内存的使用量

STEP 1 打开 wall 工程的 com.wall.main.Test,修改这两个路径

1
2
String testClasspath = "file:/C:/troy/java_workspace/wallTest/bin/"; // wallTest工程的路径
String problemDir = "C:\\troy\\java_workspace\\wall\\problems";  // wall工程下的problems文件夹路径

STEP 2 运行Test, jvm 参数:

1
java -Djava.security.manager -Djava.security.policy=C:\troy\java_workspace\wall\bin\policy.txt com.wall.main.Test

java.security.policy 为 wall工程下的policy文件的路径

JVM平台无关性-笔记

| Comments

为什么需要平台无关性?

  1. 设备多样化,可以不加修改地运行。
  2. 设备连网化,平台无关性,使得不同平台之间可以无缝地交换对象。比如,使用JINI技术
  3. 减少针对多平台开发的难度和工作量。

对平台无关性的支持

  1. Java平台

    扮演着Java程序跟操作系统的缓冲角色,使得程序无需直接跟特定平台打交道

  2. Java语言

    基本类型的值域跟行为都是自己的规范,跟平台无关

  3. class文件

    特定于JVM的二进制格式,与平台无关,如字节序等

  4. 可伸缩性

    Java平台可在不同的设备上实现,包括PC、嵌入式设备等。可根据不同的设备进行适当的裁剪,但要符合规范定义的功能子集。

影响平台无关性的因素

  1. Java平台的部署

    目标平台是否有相关的Java平台实现

  2. Java平台的版本

    • 各版本间的兼容性问题
    • 不同的功能子集的问题
    • 不同厂商针对特定平台的扩展功能问题
  3. 本地方法

  4. 非标准运行时库
  5. 对JVM行为的依赖
    • 不要依赖对象的finalization行为
    • 不要依赖线程优先级
  6. 对用户界面的依赖
  7. Java平台的Bug 如果目标平台集中有一个实现有Bug,会导致其他平台此功能不可使用。需要多测试。

JVM体系结构介绍-笔记

| Comments

为什么使用Java?

网络化大潮,Java是为网络而设计。安全性网络移动性平台无关性都是围绕这个目标设计。

体系结构

四个独立但是相关的技术:

  • Java程序设计语言
  • Java class文件格式
  • Java应用编程接口(API)
  • Java虚拟机

Java虚拟机

Java虚拟机的主要任务是装载class文件,并且执行其中的代码。

不同的虚拟机,可能有不同的执行引擎

  • 直接解释字节码。
  • 即时编译。全部转换成本地代码)
  • 自适应编译。把最频繁的10-20%代码编译成本地代码。
  • 硬件芯片编译。

本地方法与Java方法

本地方法(JNI)用于与操作系统打交道,是平台相关的 Java方法就是纯Java实现的方法,是平台无关的

ClassLoader

解决的是安全性(隔离性)与网络移动性(动态加载class).分为两类:一类是JVM的系统加载器,称为bootstrap classloader,另一个类是用户自定义的加载器

Java class文件

在平台无关性、网络移动性更适应网络

  1. 平台无关性 独立于底层主机的二进制形式。以以字节码的形式,避免编译时与特定系统绑定。
  2. 网络移动性
    • 更紧凑的格式
    • 动态连接(使用时才加载)
    • 动态扩展(可动态增加class)

Java API

Java API是运行库的集合,是Java平台都要实现的必要部分,可以安全地假设可以被获取到。在需要与主机资源打交道的时候,Java可以调用Java API来实现,从而避免平台相关。

Java程序设计语言

一门通用语言,并不局限与网络相关的领域。其核心思想是使得程序员更有效率,程序更加健壮,而且在于为新兴的网络为中心的计算环境提供一个工具。

  • 面向对象
  • 多线程
  • 结构化错误处理
  • 垃圾收集
  • 动态连接
  • 动态扩展

缺点:

  1. 性能的问题
  2. 内存管理。回收的不确定性。
  3. 线程调度。过于宽松的线程规范,可能导致不同平台的调度方式不同。
  4. 功能的最小公分母问题。这是为实现跨平台的必然困难。
  5. 易于反编译。这是由于为了实现动态连接性,一个类对另一个类的引用是符号引用,而不是指针或偏移量。所以,被引用的类的方法,字段等,需要详细描述。使得反编译变得容易。混淆器可以减轻这个问题。

如何阅读一本书

| Comments

笔记

  1. 讨论什么是阅读,以及本书所指的阅读

    1. 阅读必须是主动的阅读
    2. 阅读的目标应该是为了获得知识和增加理解力
    3. 阅读是无辅助的自我学习,让书本成为你的老师
  2. 将阅读分为4个层次,讨论每种层次的定义、特点,以及它们间的关系。

    1. 四种阅读:基础阅读、检视阅读、分析阅读、主题阅读
  3. 什么是基础阅读,它的定义、发展与存在的问题

    1. 基础阅读教育法的历史与发展趋势
      1. 从ABC教育法 –>发音法,分析法 –>目前百花齐放
    2. 基础阅读的几个阶段,以及最终应该达到的目标
      1. 阅读准备阶段(0-6、7岁,幼稚园)
      2. 认字(至一年级)
      3. 快速建立词汇(至四年级,能够读懂交通说明、图片说明、填写简单表格等)
      4. 精炼与增进前三个阶段所学的技巧(至八九年级,至少在词义上,可以阅读几乎所有的读物)
    3. 高等教育在超越基础阅读的教学上的问题
      1. 存在不足,导致学生阅读能力并没有继续上升
      2. 需要改正
    4. 阅读对于开启民智、应对未来的挑战非常重要
  4. 什么是检视阅读,它是在有限时间内阅读一本书的艺术,可以了解一本书的概貌,帮助我们确定一本书是否值得仔细读

    1. 检视阅读读有两种
      1. 有系统的预读(pre-read,原书译文是说略读或粗读,但个人感觉预读这个词更准确一些)
      2. 粗浅的阅读
    2. 有系统的预读
      1. 用快速浏览的方式阅读一本书,了解作者的重要主张,或者这到底是怎样的一本书
      2. 目标是要发现这个书是否真的值得仔细读,之后就算是决定了不再多花时间阅读,也可以知道跟这个书有关的事
      3. 如何预读的一些技术
        1. 看书名和序,了解这书的主题跟类型
        2. 看目录,了解这书的基本架构
        3. 检阅索引,找出重要词汇
        4. 看看出版者介绍,了解一下主旨
        5. 阅读主题相关的篇章,重点注意这些篇章前后的摘要(如果有的话)
        6. 以跳读的方式快速翻阅全书,寻找主要论点讯号,留意主题的基本脉动。尤其注意最后两三页,作者一般会在最后重新整理一下重要的观点。
    3. 粗浅的阅读(浅读)
      1. 面对一本难读的书,从头到尾先读完一遍,只注意你能理解的部分,略过不懂的部分,不要一遇到不懂的地方就停下来查询或思索。
    4. 检视阅读的要点
      1. 阅读的速度。
        1. 读出速度感。检视阅读的阅读速度,理想上,不只是要读得快,而且能针对不同的目标和部分,运行不同的速度来阅读。慢不慢到不值得,快不快到有损于满足与理解。
      2. 逗留与倒退。
        1. 很多人阅读时有眼光逗留与倒退的问题,这会导致速度过慢。
        2. 应该学会”一目十行“。
        3. 最简单的练习方式是,使用手指一行一行移动,速度比眼睛感觉的要快一些,强迫眼睛跟着手指移动。
      3. 理解的问题
        1. 没有经过分析阅读,你就没法理解一本书
  5. 做一个自我要求的读者。阅读的艺术不是随手可得,需要准备好你的心态跟技术。

    1. 主动的阅读。阅读的艺术就是,根据阅读层次的不同,提出合理的问题,并尝试回答这些问题
      1. 整体来说,这本书在谈什么?主题、次主题,如何发展和分解这些主题
      2. 作者细部说什么,怎么说的?主要的想法、声明、论点
      3. 这本书说得有道理吗?是全部有道理,还是部分有道理?
      4. 这本书跟你有什么关系?
    2. 做笔记,才会让一本书真正属于你
      1. 为什么要作笔记
        1. 笔记让你保持清醒
        2. 表达出来才能考验你是否真正明白
        3. 写出来,能帮助你记住作者的思想
      2. 笔记的分类
        1. 检视阅读,结构笔记 –> 回答人问题是:这是怎么一本书?在谈什么?作者是如何整体架构的?
        2. 分析/主题阅读,概念笔记
        3. 主题阅读,辩证笔记
      3. 如何作笔记
        1. 为重点画底线或画圈
        2. 底线下再划底线,用于强调其此为重中之重
        3. 空白处做星号或其他符号,再折上(或书签),用于强调书中最重要的声明或段落(不要超过十来个)
        4. 在空白处顺序编号,用于组织作者的陈述
        5. 在空白处记下其他页码(如CF p33),用于表示参照
        6. 在空白处记下你的问题、答案、心得、理解、简述作者观点等。
        7. 书的最后页,作为个人索引页,将作者的主要观点依序记下来
        8. 书的前面页,记下本书的大纲
    3. 培养阅读习惯。什么是好的阅读习惯,为什么需要培养习惯,以及如何培养习惯
      1. 只有不断地依照规则练习,才能养成习惯,这才是真正掌握
      2. 练习阅读就像滑雪一样,需要先分解动作,逐个击破,再组合起来运用
  6. 什么是分析阅读,它有哪些的操作规则

    1. 确定一本书的分类
      1. 一定要知道自己在读的是哪一类书,越早知道越好,最好在阅读之前就知道
        1. 为什么要去分类?
          1. 有时候书籍的分类并不明显,需要用心区分
          2. 就像不同的学科有不同的教法一样,不同的课程也有不同的学习方法,区分后方能更好的运用规则去阅读
        2. 基础分类
          1. 文学类(想象文学,故事、戏剧、诗歌等)
          2. 论说类。一般是用于传递知识。
            1. 实用性(手册、规则等)。
            2. 理论性(科学、数学、哲学、社会科学、历史)。
        3. 如何区分?运行检视阅读法,注意审视书名,书名可以告诉我们很多。
          1. 如何区分理论性跟实用性?
            1. 实用是与某种有效的做法有关,不管是立即或长程的功效,而理论所关注的却是去明白或了解某件事。 实用跟理论的关系好比知识跟行动的关系。 质疑某件事的有效性是理论,而质疑任何事的目的,却是实用。
            2. 理论到实践,通常需要中间的操作规则。超越“知道这是怎么回事”,进而明白“如果我们想做些什么,应该怎么它”。
            3. 指南、学习技巧、伦理、经济、演说、道德规劝等都归为实用性
            4. 实用性跟作者正不正确,我们同不同意没有关系
          2. 如何区分不同的理论性书籍?
            1. 历史就是纪事( Chronotopic ),处理一些发生在特定时间,特定地点的真实事件。“纪事”这两个字就是要提醒你这一点。
            2. 科学则不会太在意过去的事,它所面对的是可能发生在任何时间、地点的事
            3. 哲学比较像科学,不像历史,追求的是一般的真理,而非发生在过去的特定事件,不管那个过去是近代或较远的年代。
            4. 如何区别科学与哲学?如果一本理论的书所强调的内容,超乎你日常、例行、正常生活的经验,那就是科学的书。否则就是一本哲学的书。如果我们说科学家是以实验为基础,或仰赖精确的观察研究,而哲学家只是坐在摇椅上的思考者。
    2. 透视一本书。找出一本书的基本骨架(架构)。

      1. 使用最简短的句子(最多几句话)来描述本书的内容
        1. 能确切清楚地说出整体内容才是真正理解一本书
        2. 要注意: 作者经常用帮你整理出重点,但还是必须要自己表达才可以;没有唯一准确的摘要,甚至跟作者的不同也没关系,当然,一个好的摘要还是要客观上贴近事实。
      2. 将书中重要的篇章列举出来,说明它们是如何按照顺序组成一个整体的架构
        1. 为了驾驭书的复杂性,需要捋清各个部分,并了解部分是如何组合成一个有机整体的
        2. 这个规则与Rule2是一体两面、互相促进
        3. 通用公式,如:
          1. 作者将全书分为3个部分,第一部分谈什么,第二部分谈什么,第三部分谈什么
          2. 第一部分又分为3个段落,第一段落为X,第二段落为Y,第三段落为Z
          3. 在第一部分的X段落,作者有4个重点,分别为A、B、C、D
          4. 等等
        4. 这个通用公式只是理想情况,事实上需要根据你想读的程度来权衡。可能只需要粗浅地分析,或者只针对某一部分分析。
      3. 阅读与写作是一本两面的事
        1. 为什么作者不直接列出大纲,而要读者去发挖?没有人喜欢干瘪瘪的大纲,不符合正常人的审美,情绪上不喜欢,当然也就没办法达到传达思想的作用了,所以使用艺术手法来表达自己的思想是很必要的。就好比食物,需要色香味俱全,才会有足够的吸引力。关于这点,原文的精彩论述:
          1. 尽管这些规则是一体两面,但实行起来却不相同。读者是要“发现”书中隐藏着的骨架。而作者则是以制造骨架为开始,但却想办法把骨架“隐藏”起来。他的目的是,用艺术的手法将骨架隐藏起来,或是说,在骨架上添加血肉。如果他是个好作者,就不会将一个发育不良的骨架埋藏在一堆肥肉里,同样的,也不会瘦得皮包骨,让人一眼就看穿。如果血肉匀称,也没有松弛的赘肉,那就可以看到关节,可以从身体各个部位的活动中看出其中透露的言语。
          2. 为什么这么说呢?为什么论说性的书,这种本来就想条理井然地传达一种知识的书,不能光是把主题纲要交待清楚便行?原因是,不仅大多数人都不会读纲要,而且对一位自我要求较高的读者来说,他并不喜欢这样的书,他会认为他可以做自己分内的事,而作者也该做他自己分内的事。还有更多的原因。对一本书来说,血肉跟骨架是一样重要的。
        2. 一个作品应该有整体感,清楚明白,前后连贯。这是优秀写作的基本准则。Rule2、3是根据这条准则而来。
      4. 找出作者要问的或想要解决的问题。找出主要问题、次要问题,和它们是如果组织、发展的。
        1. 避免”意图谬误“。
          1. 题外:新批评。http://baike.baidu.cn/view/999001. htm
            1. 新批评提出要更关注于作品,而非作者,指出两种常见错误,一个是“意图谬误”,即对作者意图的探究为谬误。一个是”感受谬误“,即以读者情感波动弧度的大小评判作品的优劣。
            2. 这是对天朝赤裸裸的嘲讽啊
      5. 前4个规则,为分析阅读第一阶段。这4个规则的共同目标是: 完成对一本书的架构的认识
    3. 与作者找出共通的词义

    4. 公正地判断一本书

    5. 赞同或反对作者

    6. 辅助阅读

  7. 如何阅读不同的读物?要分析阅读的基础规则之上,根据不同类型的书籍的特点,需要对基础规则作相应的调整

本书练习

  • 一句话描述

    本书是一本指导手册,指导你如何超越基础阅读的层次,通过更高层次的阅读来获得知识,增加理解力。作者将更高层次的阅读划分为检视阅读、分析阅读和主题阅读,逐次讨论每个层次定义、要点和操作规则。并针对不同类型的书籍给出不同的应对策略。其次,作者还讨论了阅读的一些基本技术要点,包括做笔记、心态、阅读速度等。另外,作者反复强调了只有能够确切、清楚地解构并用自己的语言复述,才是真正的理解一本书。

  • 书的架构

    1. 这本书在讲什么? 序言,c1,c2
    2. 为什么需要本书?序言,c3-3,c3-4,c5-3
    3. 阅读的基本技术要点? c4-4,c5-1,c5-2
    4. 阅读的几个层次 基础阅读 –> c3-1,c3-2 检视阅读 –> c4 分析阅读 –> 主题阅读 –>
    5. 针对不同的类型的书籍,如果作分析阅读
  • 书的问题

    1. 如何阅读一本书?
      1. 为什么需要阅读?
      2. 阅读都有哪一些层次?
        1. 每个层次包含哪些规则,以及为什么需要这些规则,这些规则是如何组织起来的
      3. 阅读有哪一些基本技术要点
        1. 阅读的速度
        2. 做笔记
        3. 心态
      4. 以上规则更多针对论说性作品的,如果是其他类型的作品,这些规则是否有效?
        1. 是的,有效,但是需要变型
        2. 那么,具体怎么变型呢?