1 IO ,NIO,NIO2
其实就是几个主要的类:
Buffers :IO没有缓冲,是面向流的。NIO有缓冲,可以提供更加灵活的API。Buffer和Channel一定要配合使用,数据是从buffer写入channel,从channel读入buffer。
Channels :IO没有channel,一个线程管理一个连接,是阻塞式的,连接中当前没有数据,线程阻塞。NIO提供channel,可以让一个线程同时管理多个连接或者读取多个文件,是非阻塞的。注意FileChannel总是阻塞的
Selector :通过selector来选择当前需要处理的channel,如何工作取决于底层操作系统如何实现。比如linux的版本不同,可以支持select poll 或者epoll,epoll是2.6以上才支持,优势是提供回调注册机制,不必轮询整个list来获取需要处理的通道。常用的Nginx就是通过epoll实现高并发连接支持的。
一般连接数较多,而且通信量不密集,可以用NIO比较好。
这里提到容易混淆的两个概念:用户线程请求内核线程读取网络数据。
阻塞 :内核线程在读取过程中,用户线程会询问是否有数据读入内核缓存,如果没有,用户线程会等待,直到下次询问。比如java IO,一个用户线程只能处理一条channel。
非阻塞:等待过程中用户线程去干其他的事情,比如java NIO,询问一个channel没数据,不是阻塞在该channel,而是去处理其他channel,直到下次询问。一个用户线程同时处理多条channel。
同步 :上面两种情况都属于同步,同步还是异步是针对内核线程而言的。用户线程提出请求后,内核线程不会立刻回答,用户线程轮询取得数据。同步的好处就是,用户的请求最终一定会得到响应,或者成功或者失败,只是需要花费cpu轮询,轮询也是一种同步等待。
异步:内核线程立刻响应,失败或者成功。失败,比如无法建立网络连接。成功,表示收到请求,正在处理,有数据到了会通知,不必询问。比如java NIO2。异步通常做法就是callback,或者event的方式。epoll支持。异步的好处显而易见,没有花费无用的cpu时间片在轮询上,不好的地方在于,通知可能不可达。
总之,同步还是异步,描述的是用户与内核的交互关系,一个主动,一个被动。而阻塞非阻塞,描述的是用户的状态。因为同步一般表现为阻塞,所以容易弄混。
当然,NIO还提供许多扩展功能,比如文件属性的操作,随机读写等。
2 序列化
1 Serializable会保存class信息,Externalizable只有类名。效率会好一些,但不会提高明显。
2 static和transit不会序列化。父类没有声明接口,他的字段也不会序列化。
3 final字段要注意。Serializable反序列化时不会调用构造函数,所以final变量的初始化不要放在构造函数,应该直接初始化,否则当你更新类版本启用新的final变量值的时候,反序列化回来的还是旧的字段值。Externalizable反序列化会调用默认构造函数,如果没有提供,会报错。
4 serialVersionUID要一致才能反序列化,版本控制。如果字段不变,自动生成的ID也不变,说明序列化的是对象信息。ID 由字段决定,如果不指定ID,jvm会自动生成一个,当字段发生改变,反序列化会失败。
5 同一个对象重复序列化两次,即使字段值前后有改变,jvm优化后只会存一个,反序列化出来的两个对象相等,其实还是一个对象,变动的字段值丢失。
6 同一个类new两个对象,连续序列化后,jvm也会优化存储,但是反序列化出来2个不同的对象。
7 单例模式反序列化会生成新的对象,需要改写readResolve方法返回同一个实例。
8序列化方式并不仅仅局限于二进制,也可以基于文本,比如json xml。
9 hadoop有自己的序列化框架实现,storm使用kryo。
3 垃圾收集
1 堆区(新代 老代), 栈区, 方法区(永久代), native区。垃圾收集,指的是堆区的内存回收。堆的特点是动态的灵活分配,缺点是慢。栈的存取速度快,但是大小固定,即需要编译时确定。
2 并行:多个收集线程同时垃圾回收。 并发:收集线程和工作线程同时运行。
3 新代:minor GC。都是复制算法。分为三块,eden和两个survivor1 2。默认比例811。
a) 回收eden和s1的非存活对象。b) 将存活的copy到s2。c) 重复。 d) 在s1和s2来回多次还存活的进入老代。
Serial串行收集器:client模式启动时的默认新代收集器。单线程。不能并行,不能并发。
ParNew并行收集器:多线程版的串行收集器。CMS的默认搭配。可以并行,不能并发。
Paralle Scavenge:吞吐量优先收集器。可以控制业务线程运行时间与cpu总时间的比值。可以并行,不能并发。
-XX:MaxGCPauseMillis 每次收集的停顿时间,它通过修改新代的大小来达到时间控制。
-XX:GCTimeRatio
手机时间占总时间的比例。
-XX:+UseAdaptiveSizePolicy
自适应调节
4 老代:full GC
Serial
Old收集器:client模式启动时的默认老代收集器。单线程。与新代的Serial搭配使用。不能并行,不能并发。标记整理算法。
Parallel
Old收集器:与Paralle Scavenge搭配使用。可以并行,不能并发。标记整理算法。
CMS收集器:只能与ParNew搭配使用。追求短的收集停顿时间,提高用户体验。标记清除算法。通过参数配置整理功能,以消除内存碎片。可以并行,可以并发。注意因为并发,收集过程中会不断有用户线程新的无用对象产生,所以不能等老代填满,通过参数设置比例触发full
gc。
G1 收集器:jdk7支持。标记整理算法。特点是 内存区域划分, 然后优先级队列,在指定的回收时间内,优先回收垃圾对象多的区域,以达到最好的回收效率。
4 多线程
1
volatile 对象创建在堆中,当线程的某个方法调用使用到该对象的某个成员变量时,会首先在该线程栈里分配一个frame,然后将变量的值在frame里做一份copy,原始类型就是值,引用类型就是句柄。这样无论对象成员变量如何变化,该线程方法调用中始终无法感知,造成不一致的问题。使用volatile
,强制线程每次使用堆内存中的值。volatile并不能代替独占锁,即不能依靠它来实现同步。