潇湘夜雨

分类

  • 源代码阅读 (20)
  • 设计模式 (3)
  • 科研 (6)
  • 思想 (2)
  • 技术 (18)
  • 工具 (4)
  • 虚拟机 (1)
  • java (11)
  • c语言 (4)
  • 读书 (1)
  • 操作系统 (1)
  • 英语 (10)
  • 计算机系统 (3)
  • 生活 (1)

归档

  • 2014-08 (1)
  • 2014-07 (5)
  • 2014-05 (12)
  • 2014-04 (3)
  • 2014-03 (11)
  • 2014-02 (6)
  • 2014-01 (3)
  • 2013-11 (10)
  • 2013-10 (3)
  • 2010-09 (1)
  • 单例模式
    1. 2014-07-27

    单例模式


    • 定义

    单例模式确保一个类只有一个实例,并提供一个全局访问点

    • 解释

    从定义可以看出,特点是这个类只有一个实例。那么,为什么要这么做呢?原因在于,有些时候,这个类只有一个实例会节约资源,或者只有一个实例才能保证整个程序运行正确,一致。例如:线程池,缓存,对话框,日志对象等等 。

    Read More ...
  • 外观模式
    1. 2014-07-27

    外观模式


    • 定义

    外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统的更容易使用。

    • 解释

    我有一个多功能的盒子,盒子让提供了100个接口,每个都有不同的功能。现在我要把这个盒子给别人用了,我只想让别人用到其中5个接口,或者别人只需要其中5个接口。那么,我可以再做一个盒子,这个盒子套在原来的盒子外面,并且只提供5个接口。这样,用的人会觉得很方便,我也觉得这样很安全。

    我们可能要问,为什么原来的盒子要有100个接口呢?为什么不直接提供5个接口呢?原因可能会很多,可能这个盒子我们没法控制,是别人给我们的。也可能这个盒子要与其它众多盒子配合使用,其它盒子要用到其它的95个接口。

    Read More ...
  • 观察者模式
    1. 2014-07-25

    观察者模式


    • 定义

    观察者模式定义了对象之间的一对多依赖,当一个对象改变时,所以依赖于它的对象都会得到通知。

    Read More ...
  • OpenJDK 源码阅读之 Java 输入输出(I/O) 之 字节流输出
    1. 2014-07-12

    OpenJDK 源码阅读之 Java 输入输出(I/O) 之 字节流输出

    标签(空格分隔): 源代码阅读 Java 封神之路

    Read More ...
  • OpenJDK 源码阅读之 Java 输入输出(I/O) 之 字节流输入
    1. 2014-07-11

    OpenJDK 源码阅读之 Java 输入输出(I/O) 之 字节流输入

    标签(空格分隔): 源代码阅读 Java 封神之路

    Java 的输入输出总是给人一种很混乱的感觉,要想把这个问题搞清楚,必须对各种与输入输出相关的类之间的关系有所了解。只有你了解了他们之间的关系,知道设计这个类的目的是什么,才能更从容的使用他们。

    我们先对 Java I/O 的总体结构进行一个总结,再通过分析源代码,给出把每个类的关键功能是如何实现的。

    Read More ...
  • OpenJDK 源码阅读之 LinkedList
    1. 2014-05-28

    OpenJDK 源码阅读之 LinkedList


    概要

    • 类继承关系
    java.lang.Object
        java.util.AbstractCollection<E>
            java.util.AbstractList<E>
                java.util.AbstractSequentialList<E>
                    java.util.LinkedList<E>
    
    Read More ...
  • 开始OpenJDK源代码阅读
    1. 2014-05-27

    开始OpenJDK源代码阅读


    在阅读了一周的 OpenJDK 源代码后,我才写这篇文章。因为除非你已经开始阅读,否则是不知道自己是不是应该读下去的。所以,不要贸然说自己要干嘛,先做一段时间,觉得感觉还好,再决定做下去。

    这一周,主要是看 java.util 中和容器相关的几个文件,虽然还没看太多,但是已经有一些收获了。看到了以前学过的数据结构在Java的标准库中是如何被实现的。也明白了平时使用的一些类的原理是什么。另外,由于最近在看 《Java编程思想》,也能把书中讲的和标准库的源代码对应起来,感觉还不错。还有一个收获就是明白了,基础越扎实,阅读源代码收获也越大,否则根本就看不出一些设计的初衷是什么。之前看到源代码中一些编写程序的方式,我觉得没有必要那样写,后来看《Java编程思想》,才知道为什么会这样写。也有一些是我觉得可以从源代码中学习的东西,从《Java编程思想》中看到,标准库中的编写方式有些是历史遗留问题,不得不那么写,而不是说我们写的时候,也要那样做。这就是说不要迷信那些你不明白的东西,即使他们看起来很权威。

    Read More ...
  • OpenJDK 源码阅读之 TreeMap
    1. 2014-05-26

    OpenJDK 源代码阅读之 TreeMap


    概要

    • 类继承关系
    java.lang.Object
        java.util.AbstractMap<K,V>
            java.util.HashMap<K,V>
    
    • 定义
    public class TreeMap<K,V>
        extends AbstractMap<K,V>
        implements NavigableMap<K,V>, Cloneable, java.io.Serializable
    
    • 要点

    1) 基于 NavigableMap 实现的红黑树 2) 按 natrual ordering 或者 Comparator 定义的次序排序。 3) 基本操作 containsKey,get,put 有 log(n) 的时间复杂度。 4) 非线程安全

    Read More ...
  • OpenJDK 源码阅读之 HashMap
    1. 2014-05-25

    OpenJDK 源代码阅读之 HashMap


    概要

    • 类继承关系
    java.lang.Object
        java.util.AbstractMap<K,V>
            java.util.TreeMap<K,V>
    
    • 定义
    public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, Serializable
    
    • 要点

    1) 与 Hashtable 区别在于:非同步,允许 null 2) 不保证次序,甚至不保证次序随时间不变 3) 基本操作 put, get 常量时间 4) 遍历操作 与 capacity+size 成正比 5) HashMap 性能与 capacity 和 load factor 相关,load factor 是当前元素个数与 capacity 的比值,通常设定为 0.75,如果此值过大,空间利用率高,但是冲突的可能性增加,因而可能导致查找时间增加,如果过小,反之。当元素个数大于 capacity * load_factor 时,HashMap 会重新安排 Hash 表。因此高效地使用 HashMap 需要预估元素个数,设置最佳的 capacity 和 load factor ,使得重新安排 Hash 表的次数下降。

    Read More ...
  • OpenJDK 源码阅读之 ArrayList
    1. 2014-05-24

    OpenJDK 源码阅读之 ArrayList


    概要

    • 类继承关系
    java.lang.Object
        java.util.AbstractCollection<E>
            java.util.AbstractList<E>
                java.util.ArrayList<E>
    
    • 定义
    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
    }
    
    Read More ...
  • OpenJDK 源码阅读之 String
    1. 2014-05-23

    OpenJDK 源代码阅读之 String


    概要

    • 类继承关系
    java.lang.Object
        java.lang.String
    
    • 定义
    public final class String
    extends Object
    implements Serializable, Comparable<String>, CharSequence
    
    • 要点

    一旦创建就不可改变

    实现

    • storage
    /** The value is used for character storage. */
    private final char value[];
    

    可以看出 String 中的数据是如何存储的。

    • 初始化
        public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
        }
    

    可以看出使用 String 类型初始化,新 String 实际上与原来的 String 指向同一块内存。

    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
    

    如果用 char[] 初始化,可以看出,新分配了内存,并复制,保证了两者相互独立,只是内容相同。

        public String(StringBuffer buffer) {
            synchronized(buffer) {
                this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
            }
        }
    

    注意用 StringBuffer 初始化时,对同一 buffer 是线程安全的,即初始化 String 的过程中,其它线程不会改变 buffer 的内容。

    另外,能告诉我下面这段代码是怎么回事么?

       public String(StringBuilder builder) {
            this.value = Arrays.copyOf(builder.getValue(), builder.length());
        }
    

    为啥这次不同步了呢?

    Read More ...
  • OpenJDK 源码阅读之 HashSet
    1. 2014-05-22

    OpenJDK 源代码阅读之 HashSet


    概要

    • 类继承关系
    java.lang.Object
        java.util.AbstractCollection<E>
            java.util.AbstractSet<E>
                java.util.HashSet<E>
    
    • 定义
    public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, Serializable
    
    • 要点
    1. 不保证元素次序,甚至不保证次序不随时间变化
    2. 基本操作(add, remove, contains, size)常量时间
    3. 迭代操作与当前元素个数加底层容量大小成正比
    4. 不保证同步

    思考

    • 总体实现

    底层是用 HashMap 实现的,Set 中的数据是 HashMap 的 key,所有的 key 指向同一个 value, 此 value 定义为:

    // Dummy value to associate with an Object in the backing Map
        private static final Object PRESENT = new Object();
    

    再看一下 add,大概就能明白了

    /**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    
    • load factor
        public HashSet(Collection<? extends E> c) {
            map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
            addAll(c);
        }
    

    初始化中,注意使用的 HashMap 的 load factor 设置为 0.75,如果太小,就设置成 16. 为什么要 0.75 呢? 有什么依据吗?

    Read More ...
  • OpenJDK 源码阅读之 LinkedList
    1. 2014-05-21

    OpenJDK 源码阅读之 LinkedList


    定义

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    {
    }
    

    盲点

    • serialVersionUID
        private static final long serialVersionUID = 876323262645176354L;
    

    序列化版本号,如果前一版本序列化后,后一版本发生了很大改变,就使用这个号告诉虚拟机,不能反序列化了。

    问题

    • writeObject

    比例一下 ArrayList 与 LinkedList 中的 writeObject

        private void writeObject(java.io.ObjectOutputStream s)
            throws java.io.IOException{
            // Write out element count, and any hidden stuff
            int expectedModCount = modCount;
            s.defaultWriteObject();
    
            // Write out array length
            s.writeInt(elementData.length);
    
            // Write out all elements in the proper order.
            for (int i=0; i<size; i++)
                s.writeObject(elementData[i]);
    
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
    
        }
    
        private void writeObject(java.io.ObjectOutputStream s)
            throws java.io.IOException {
            // Write out any hidden serialization magic
            s.defaultWriteObject();
    
            // Write out size
            s.writeInt(size);
    
            // Write out all elements in the proper order.
            for (Node<E> x = first; x != null; x = x.next)
                s.writeObject(x.item);
        }
    

    注意后者没有检查 modCount,这是为什么呢?之前看 ArrayList的时候觉得是为线程安全考虑的,可是现在为什么又不检查了呢?虽然两个文件的注释中都说到,如果有多个线程操作此数据结构,应该从外部进行同步。但是一个检查,一个不检查是几个意思呀?

    思考

    • 维护数据结构一致性
        private void linkFirst(E e) {
            final Node<E> f = first;
            final Node<E> newNode = new Node<>(null, e, f);
            first = newNode;
            if (f == null)
                last = newNode;
            else
                f.prev = newNode;
            size++;
            modCount++;
        }
    

    注意代码第 5行对 f 的检查,6 行对 last 的调整。一定要细心,保证操作后, 所有可能 涉及的数据都得到相应更新。

    • 隐藏实现
        public E getFirst() {
            final Node<E> f = first;
            if (f == null)
                throw new NoSuchElementException();
            return f.item;
        }
    

    注意返回的是数据,而不是Node,外部根本不需要知道 Node 的存在。 另外,为什么 f == null 要抛出异常而不是返回 null?

    Read More ...
  • 使用JVMTI获取Java多线程程序指令执行次序
    1. 2014-04-28

    使用JVMTI获取Java多线程程序指令执行次序


    在Java多线程程序中,由于线程调度,指令间的次序在每次运行时都可能不相同,有时候,我们需要得到指令次序,用来分析程序的行为。这样细粒度的底层行为用一般方法很难完成,我们需要借助 JVM Tool Interface,即JVMTI,来帮助我们获取Java虚拟机执行时的信息。本文先介绍编写JVMTI程序的基本框架,然后介绍如何使用JVMTI来获取多线程程序中指令之间的次序。

    JVMTI简介

    JVMTI是用于编写开发与监视工具的编程接口,使用它可以检查并控制运行于Java虚拟机上的程序。使用它可以完成性能分析,调试,监视(monitoring),线程分析,覆盖分析(coverage analysis)等工具。

    使用JVMTI可以编写出一个agent。在运行Java程序时,指定这个agent,那么当虚拟机运行程序时,如果agent中指定的一些事件发生,虚拟机就会调用agent中相应的回调函数。JVMTI提供了一系列可以指定的事件,以及获取虚拟机中信息的函数接口。

    Read More ...
  • 对C语言中的static关键字的深入理解
    1. 2014-04-21

    对C语言中的static关键字的深入理解

    在阅读一些项目源代码时,我发现很多时候,会把函数和变量声明为static,所以,很好奇为什么要这样做,于是有了下面这篇文章。

    基本概念

    使用static有三种情况:

    • 函数内部static变量
    • 函数外部static变量
    • static函数

    函数内部的static变量,关键在于生命周期持久,他的值不会随着函数调用的结束而消失,下一次调用时,static变量的值,还保留着上次调用后的内容。

    Read More ...
  • WSGI 简介
    1. 2014-01-26
    2. 技术

    WSGI 简介

    背景

    Python Web 开发中,服务端程序可以分为两个部分,一是服务器程序,二是应用程序。前者负责把客户端请求接收,整理,后者负责具体的逻辑处理。为了方便应用程序的开发,我们把常用的功能封装起来,成为各种Web开发框架,例如 Django, Flask, Tornado。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。

    这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

    Python Web开发中,这个标准就是 The Web Server Gateway Interface, 即 WSGI. 这个标准在PEP 333中描述,后来,为了支持 Python 3.x, 并且修正一些问题,新的版本在PEP 3333中描述。

    WSGI 是什么

    WSGI 是服务器程序与应用程序的一个约定,它规定了双方各自需要实现什么接口,提供什么功能,以便二者能够配合使用。

    WSGI 不能规定的太复杂,否则对已有的服务器来说,实现起来会困难,不利于WSGI的普及。同时WSGI也不能规定的太多,例如cookie处理就没有在WSGI中规定,这是为了给框架最大的灵活性。要知道WSGI最终的目的是为了方便服务器与应用程序配合使用,而不是成为一个Web框架的标准。

    另一方面,WSGI需要使得middleware(是中间件么?)易于实现。middleware处于服务器程序与应用程序之间,对服务器程序来说,它相当于应用程序,对应用程序来说,它相当于服务器程序。这样,对用户请求的处理,可以变成多个 middleware 叠加在一起,每个middleware实现不同的功能。请求从服务器来的时候,依次通过middleware,响应从应用程序返回的时候,反向通过层层middleware。我们可以方便地添加,替换middleware,以便对用户请求作出不同的处理。

    Read More ...
  • Python包管理不同方式的区别
    1. 2013-11-01
    2. 技术

    Python包管理不同方式的区别

    学习Python已经有一段时间,经常会遇到安装各种包的问题,一会 setup.py, 一会 easy_install,一会又是pip,还有一些概念比如distutils, setuptools等等,搞不清楚谁是谁,什么时候应该用什么,今天就把这些概念 澄清一下。

    distutils

    distutils是Python标准库的一部分,其初衷是为开发者提供一种方便的打包方式, 同时为使用者提供方便的安装方式。

    Read More ...
  • 字符指针与字符数组真正的区别
    1. 2013-10-25

    字符指针与字符数组真正的区别


    问题缘起

    先看一个示例

    示例1

    #include <stdio.h>
    
    int main() {
        char *p = "hello";
        char q[] = "hello";
    
        printf ("p: %s\n", p);
        printf ("q: %s\n", q);
    
        return 0;
    }
    

    上面的例子会给出这样的输出

    p: hello
    q: hello
    

    这样看,char *p 和 char q[] 好像没什么区别, 那么我们再看一个例子

    示例2

    #include <stdio.h>
    
    int main() {
        char *p = "hello";
        char q[] = "hello";
    
        p[0] = 's';
        q[0] = 's';
    
        return 0;
    }
    

    如果你在Linux下,运行时可能得到这样的结果

    Segmentation fault (core dumped)
    

    这时候你看到了区别,出现了段错误, 你一定想明白,到底发生了什么, 经过几个小实验,你可能会发现使用 char *p 定义时,p指向的数据是无法改变的。 然后你Google, 别人可能会告诉你

    • char 指针指向的数据不能修改
    • char 指针指向的数据没有分配
    • ...

    你听了还是一头雾水,不能修改是什么意思,没有分配?没有分配为什么能输出?

    作为一个好奇心很重的人,你是绝对不能容忍这些问题的困扰的,且听我慢慢道来

    Read More ...
Copyright (c) minixalpha 2014