JAVA 虚拟机深入研究(一)——关于Java的一些历史
内容目录:
JAVA 虚拟机深入研究(二)——JVM虚拟机发展以及一些Java的新东西
有本书叫做《深入Java 虚拟机》周志明写的,最近买来准备研读一下,然后从今天开始不定期的写一些读这本书之后的一些感受和学习记录。今天这就是第一篇。
从广义上说,Java的技术体系包含了所有运行于Java虚拟机上的语言都算是Java的技术体系内的一员,比如Clojure,JRuby,Groovy等都算是广义的Java体系内一员。但是如果按照传统的Java的体系Sun当年官方的体系包括:
1.Java程序设计语言
2.各种硬件平台上的Java虚拟机
3.Class文件格式
4.Java API类库
5.来自商业机构的以及来自开源社区的第三方Java类库。
然后就要说到必须说的一个东西,那就是JDk,JDK包括了几个东西,第一是Java程序设计语言,Java虚拟机,Java API 类库这三部分统称JDK,这个东西是我们开发Java的最小环境。然后说说Java API类库,它的一个子集Java SE API 子集和Java的虚拟机部分也被称为JRE,这个是程序运行的标准环境。我给大家贴一个图在这,这部分就算是说完了。
Java的发展历史没啥可说的,我只是把我觉得很重要的一些点提出来。
1997年2月19日,JDK发了1.1版本,这个时候出现了几个关键的东西比如JDBC。在这个版本中必须提一提的是(具体的一些关键点有一些会在以后再讲):JAR文件格式,JDBC,JavaBean,RMI,内部类,反射。
1998年12月4日,JDK一个里程碑版本1.2,这个版本代表性技术:EJB、Java Plug-in、Java IDL,Swing,第一次内置了JIT编译器,添加了关键字strictfp(用于声明精确浮点数,被这个声明后表示严格遵守FP-strict)。
2002年2月13日,JDK发布了1.4,添加了正则表达式,异常链,NIO,日志类,XML解析器和XSLT转换器。
2004年9月30日,JDK的经典版本1.5,比较经典的东西,例如自动装箱,泛型,动态注解,枚举,可变长参数,遍历循环(foreach).另外这个版本改进了Java的内存模型(JMM)、提供了java.util.concurrent并发包。
后续的几个版本我就不写了,相信大家都是了解的。在Java这么多版本中,我记录下以下关键词,后面会单独的讲讲。
1.RMI(远程调用)
2.JIT编译器
3.NIO(非阻塞调用)
4.异常链
5.动态注解
6.Lambda表达式
7.Jigsaw
8.动态语言支持
9.G1垃圾回收器
10.Coin(语言细节化)
=============分割线=================================
后面的关于上面提到的关键词,都会在这下面更新,我会逐步的查找然后把这些关键词解释清楚。这些东西有的是通过连接给出了一些博客或者其他人的解释,愿意看就看看,不看也没影响。
1RMI (远程接口调用)
关于这个东西,一百度一大堆,我就整理下吧。http://www.blogjava.net/zhenyu33154/articles/320245.html 这是一个博客,讲解了RMI这个东西。
关于远程调用,这里转载一个博客来自http://blog.sina.com.cn/s/blog_56fd58ab0100mrl6.html
-------------------------------------------------------转载---------------------------------------------------------------------------------------------------
在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯 的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是些什么关系呢,它们背后到底是 基于什么原理实现的呢,了解这些是实现分布式服务框架的基础知识,而如果在性能上有高的要求的话,那深入了解这些技术背后的机制就是必须的了,在这篇 blog中我们将来一探究竟,抛砖引玉,欢迎大家提供更多的实现远程通讯的技术和原理的介绍。
基本原理
要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输
到另外一台计算机,基于传输协议和网
络IO来实现,其中传输协议比较
出名的有http、tcp、udp等等,http、tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、aio三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了
应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。
应用级协议
远程服务通讯,需要达到的目标是在一台计算机发起请求,另外一台机器在接收到请求后进行相应的处理并将结果返回给请求端,这其中
又会有诸如one way
request、同步请求、异步请求等等请求方式,按照网络通信原理,需要实现这个需要做的就是将请求转换成流,通过传输协议传输至远端,远端计算机在接
收到请求的流后进行处理,处理完毕后将结果转化为流,并通过传输协议返回给调用端。
原理是这样的,但为了应用的方便,业界推出了很多基于此原理之
上的应用级的协议,使得大家可以不用去直接操作这么底层的东西,通常应用级的远程通信协议会提供:
1、为了避免直接做流操作这么麻烦,提供一种更 加易用或贴合语言的标准传输格式;
2、网络通信机制的实现,就是替你完成了将传输格式转化为流,通过某种传输协议传输至远端计算机,远端计算机在
接收到流后转化为传输格式,并进行存储或以某种方式通知远端计算机。
所 以在学习应用级的远程通信协议时,我们可以带着这几个问题进行学习:
1、传输的标准格式是什么?
2、怎么样将请求转化为传输的流?
3、 怎么接收和处理流?
4、传输协议是?
不过应用级的远程通信协议并不会在传输协议上做什么多大的改进,主要是在流操作方面,让应用层生成流
和处理流的这个过程更加的贴合所使用的语言或标准,至于传输协议则通常都是可选的,在java领域中知名的有:RMI、XML-RPC、Binary-
RPC、SOAP、CORBA、JMS,来具体的看看这些远程通信的应用级协议:
--------------------------------------------------------------------------------------------------------------------------------------------------
RMI
RMI 是个典型的为java定制的远程通信协议,我们都知道,在single vm中,我们可以通过直接调用java object
instance来实现通信,那么在远程通信时,如果也能按照这种方式当然是最好了,这种远程通信的机制成为RPC(Remote
Procedure Call),RMI正是朝着这个目标而诞生的。
来看下基于RMI的一次完整的远程通信过程的原理:
1、客户端发起请求,请求转交至RMI 客户端的stub类;
2、stub类将请求的接口、方法、参数等信息进行序列化;
3、基于socket将序列化后的流传输至服务器端;
4、服务器端接收到流后转发至相应的 skelton类;
5、skelton类将请求的信息反序列化后调用实际的处理类;
6、处理类处理完毕后将结果返回给skelton类;
7、 Skelton类将结果序列化,通过socket将流传送给客户端的stub;
8、stub在接收到流后反序列化,将反序列化后的Java
Object返回给调用者。
来看jboss-remoting对于此过程的一个更好的图示:
根据原理来回答下之前学习应用级协议带着的几个问题:
1、传输的标准格式是什么?
是Java ObjectStream。
2、怎么样将请求转化为传输的流?
基于Java串行化机制将请求的java object信息转化为流。
3、怎么接收和处理流?
根据采用的协议启动相应的监听端口,当有流进入后基于Java串行化机制将流进行反序列化,并根据RMI协议获取到相应的处理对象信息,进行调用并处理,
处理完毕后的结果同样基于java串行化机制进行返回。
4、传输协议是?
Socket。
--------------------------------------------------------------------------------------------------------------------------------------------------
XML-RPC
XML-
RPC也是一种和RMI类似的远程调用的协议,它和RMI的不同之处在于它以标准的xml格式来定义请求的信息(请求的对象、方法、参数等),这样的好处
是什么呢,就是在跨语言通讯的时候也可以使用。
来看下XML-RPC协议的一次远程通信过程:
1、客户端发起请求,按照XML-RPC协 议将请求信息进行填充;
2、填充完毕后将xml转化为流,通过传输协议进行传输;
3、接收到在接收到流后转换为xml,按照XML- RPC协议获取请求的信息并进行处理;
4、处理完毕后将结果按照XML-RPC协议写入xml中并返回。
图示以上过程:
同样来回答问题:
1、传输的标准格式是?
标准格式的XML。
2、怎么样将请求转化为传输的流?
将XML转化为流。
3、怎么接收和处理流?
通过监听的端口获取到请求的流,转化为XML,并根据协议获取请求的信息,进行处理并将结果写入XML中返回。
4、传输协议是?
Http。
--------------------------------------------------------------------------------------------------------------------------------------------------
Binary-RPC
Binary- RPC看名字就知道和XML-RPC是差不多的了,不同之处仅在于传输的标准格式由XML转为了二进制的格式。
同样来回答问题:
1、传输 的标准格式是?
标准格式的二进制文件。
2、怎么样将请求转化为传输的流?
将二进制格式文件转化为流。
3、 怎么接收和处理流?
通过监听的端口获取到请求的流,转化为二进制文件,根据协议获取请求的信息,进行处理并将结果写入XML中返回。
4、 传输协议是?
Http。
--------------------------------------------------------------------------------------------------------------------------------------------------
SOAP
SOAP 原意为Simple Object Access
Protocol,是一个用于分布式环境的、轻量级的、基于XML进行信息交换的通信协议,可以认为SOAP是XML
RPC的高级版,两者的原理完全相同,都是http+XML,不同的仅在于两者定义的XML规范不同,SOAP也是Webservice采用的服务调用协
议标准,因此在此就不多加阐述了。
--------------------------------------------------------------------------------------------------------------------------------------------------
CORBA
Common Object Request Broker
Architecture(公用对象请求代理[调度]程序体系结构),是一组用来定义“分布式对象系统”的标准,由OMG(Object
Menagement
Group)作为发起和标准制定单位。CORBA的目的是定义一套协议,符合这个协议的对象可以互相交互,不论它们是用什么样的语言写的,不论它们运行于
什么样的机器和操作系统。
CORBA在我看来是个类似于SOA的体系架构,涵盖可选的远程通信协议,但其本身不能列入通信协议这里来讲,而且
CORBA基本淘汰,再加上对CORBA也不怎么懂,在此就不进行阐述了。
--------------------------------------------------------------------------------------------------------------------------------------------------
JMS
JMS
呢,是实现java领域远程通信的一种手段和方法,基于JMS实现远程通信时和RPC是不同的,虽然可以做到RPC的效果,但因为不是从协议级别定义的,
因此我们不认为JMS是个RPC协议,但它确实是个远程通信协议,在其他的语言体系中也存在着类似JMS的东西,可以统一的将这类机制称为消息机制,而消
息机制呢,通常是高并发、分布式领域推荐的一种通信机制,这里的主要一个问题是容错(详细见ErLang论文)。
来看JMS中的一次远程通信的过 程:
1、客户端将请求转化为符合JMS规定的Message;
2、通过JMS API将Message放入JMS Queue或Topic中;
3、如为JMS Queue,则发送中相应的目标Queue中,如为Topic,则发送给订阅了此Topic的JMS
Queue。
4、处理端则通过轮训JMS Queue,来获取消息,接收到消息后根据JMS协议来解析Message并处理。
回答问 题:
1、传输的标准格式是?
JMS规定的Message。
2、怎么样将请求转化为传输的流?
将参数信息放入Message中即可。
3、怎么接收和处理流?
轮训JMS
Queue来接收Message,接收到后进行处理,处理完毕后仍然是以Message的方式放入Queue中发送或Multicast。
4、传 输协议是?
不限。
基于JMS也是常用的实现远程异步调用的方法之一。
可选实现技术
当然,在上面的原理中并没有介绍到所有的java领域可选的远程通信协议了,例如还有EJB采用的ORMI、Spring自己
定义的一个简单的Http Invoker等等。
看完原理后我们再来看看目前java领域可用于实现远程通讯的框架或library,知名的
有:JBoss-Remoting、Spring-Remoting、Hessian、Burlap、XFire(Axis)、ActiveMQ、
Mina、Mule、EJB3等等,来对每种做个简单的介绍和评价,其实呢,要做分布式服务框架,这些东西都是要有非常深刻的了解的,因为分布式服务框架
其实是包含了解决分布式领域以及应用层面领域两方面问题的。
当然,你也可以自己根据远程网络通信原理(transport protocol+Net
IO)去实现自己的通讯框架或library。
那么在了解这些远程通讯的框架或library时,会带着什么问题去学 习呢?
1、是基于什么协议实现的?
2、怎么发起请求?
3、怎么将请求转化为符合协议的格式的?
4、使用什么传输协议传 输?
5、响应端基于什么机制来接收请求?
6、怎么将流还原为传输格式的?
7、处理完毕后怎么回应?
--------------------------------------------------------------------------------------------------------------------------------------------------
JBoss-Remoting
Jboss-
remoting是由jboss编写的一个java领域的远程通讯框架,基于此框架,可以很简单的实现基于多种传输协议的java对象的RPC。
直 接来回答问题:
1、是基于什么协议实现的?
JBoss-Remoting是个通讯框架,因此它支持多种协议方式的通信,例如纯粹的socket+io方式、rmi方式、http+io方式等。
2、 怎么发起请求?
在JBoss-Remoting中,只需将需要发起的请求参数对象传入jboss-remoting的InvocationRequest对象即可,也可
根据协议基于InvocationRequest封装符合需求的InvocationRequest对象。
3、怎么将请求转化为符合协议的格式 的?
JBoss-Remoting基于Java串行化机制或JBoss自己的串行化实现来将请求转化为对象字节流。
4、使用 什么传输协议传输?
支持多种传输协议,例如socket、http等。
5、响应端基于什么机制来接收请求?
响应端只需将自己的处理对象注册到JBoss-Remoting提供的server端的Connector对象中即可。
6、怎么将流还原为传输 格式的?
JBoss-Remoting基于java串行化机制或jboss自己的串行化实现来将请求信息还原为java对象。
7、 处理完毕后怎么回应?
处理完毕后将结果对象直接返回即可,jboss-remoting会将此对象按照协议进行序列化,返回至调用端。
另外,jboss- remoting支持多种通信方式,例如同步/异步/单向通信等。
--------------------------------------------------------------------------------------------------------------------------------------------------
Spring-Remoting
Spring- remoting是Spring提供java领域的远程通讯框架,基于此框架,同样也可以很简单的将普通的spring
bean以某种远程协议的方式来发布,同样也可以配置spring bean为远程调用的bean。
1、是基于什么协议实现的?
和JBoss-Remoting一样,作为一个远程通讯的框架,Spring通过集成多种远程通讯的library,从而实现了对多种协议的支持,例如
rmi、http+io、xml-rpc、binary-rpc等。
2、怎么发起请求?
在Spring中,由于其对于远程调用的bean采用的是proxy实现,发起请求完全是通过服务接口调用的方式。
3、怎么将请求转化为符合协议 的格式的?
Spring按照协议方式将请求的对象信息转化为流,例如Spring Http
Invoker是基于Spring自己定义的一个协议来实现的,传输协议上采用的为http,请求信息是基于java串行化机制转化为流进行传输。
4、 使用什么传输协议传输?
支持多种传输协议,例如rmi、http等等。
5、响应端基于什么机制来接收请求?
响应端遵循协议方式来接收请求,对于使用者而言,则只需通过spring的配置方式将普通的spring
bean配置为响应端或者说提供服务端。
6、 怎么将流还原为传输格式的?
按照协议方式来进行还原。
7、处理完毕后怎么回应?
处理完毕后直接返回即可,spring-remoting将根据协议方式来做相应的序列化。
--------------------------------------------------------------------------------------------------------------------------------------------------
Hessian
Hessian 是由caucho提供的一个基于binary-RPC实现的远程通讯library。
1、是基于什么协议实现的?
基于Binary-RPC协议实现。
2、怎么发起请求?
需通过Hessian本身提供的API来发起请求。
3、怎么 将请求转化为符合协议的格式的?
Hessian通过其自定义的串行化机制将请求信息进行序列化,产生二进制流。
4、使用什么 传输协议传输?
Hessian基于Http协议进行传输。
5、响应端基于什么机制来接收请求?
响应端根据Hessian提供的API来接收请求。
6、怎么将流还原为传输格式的?
Hessian根据其私有的串行化机制来将请求信息进行反序列化,传递给使用者时已是相应的请求信息对象了。
7、处理完毕后怎么回应?
处理完毕后直接返回,hessian将结果对象进行序列化,传输至调用端。
--------------------------------------------------------------------------------------------------------------------------------------------------
Burlap
Burlap 也是有caucho提供,它和hessian的不同在于,它是基于XML-RPC协议的。
1、是基于什么协议实现的?
基于XML-RPC协议实现。
2、怎么发起请求?
根据Burlap提供的API。
3、怎么将请求转化为符合协议的格 式的?
将请求信息转化为符合协议的XML格式,转化为流进行传输。
4、使用什么传输协议传输?
Http协议。
5、响应端基于什么机制来接收请求?
监听Http请求。
6、怎么将流还原为传输格式的?
根据XML-RPC协议进行还原。
7、处理完毕后怎么回应?
返回结果写入XML中,由Burlap返回至调用端。
--------------------------------------------------------------------------------------------------------------------------------------------------
XFire、 Axis
XFire、Axis是Webservice的实现框架,WebService可算是一个完整的SOA架构实现标准了,因此采用
XFire、Axis这些也就意味着是采用webservice方式了。
1、是基于什么协议实现的?
基于SOAP协议。
2、 怎么发起请求?
获取到远端service的proxy后直接调用。
3、怎么将请求转化为符合协议的格式的?
将请求信息转化为遵循SOAP协议的XML格式,由框架转化为流进行传输。
4、使用什么传输协议传输?
Http协议。
5、 响应端基于什么机制来接收请求?
监听Http请求。
6、怎么将流还原为传输格式的?
根据SOAP协议进行还原。
7、处理完毕后怎么回应?
返回结果写入XML中,由框架返回至调用端。
--------------------------------------------------------------------------------------------------------------------------------------------------
ActiveMQ
ActiveMQ
是JMS的实现,基于JMS这类消息机制实现远程通讯是一种不错的选择,毕竟消息机制本身的功能使得基于它可以很容易的去实现同步/异步/单向调用等,而
且消息机制从容错角度上来说也是个不错的选择,这是Erlang能够做到容错的重要基础。
1、是基于什么协议实现的?
基于JMS协议。
2、怎么发起请求?
遵循JMS API发起请求。
3、怎么将请求转化为符合协议的格式的?
不太清楚,猜想应该是二进制流。
4、使用什么传输协议传输?
支持多种传输协议,例如socket、http等等。
5、 响应端基于什么机制来接收请求?
监听符合协议的端口。
6、怎么将流还原为传输格式的?
同问题3。
7、 处理完毕后怎么回应?
遵循JMS API生成消息,并写入JMS Queue中。
基于JMS此类机制实现远程通讯的例子有 Spring-Intergration、Mule、Lingo等等。
--------------------------------------------------------------------------------------------------------------------------------------------------
Mina
Mina
是Apache提供的通讯框架,在之前一直没有提到网络IO这块,之前提及的框架或library基本都是基于BIO的,而Mina是采用NIO
的,NIO在并发量增长时对比BIO而言会有明显的性能提升,而java性能的提升,与其NIO这块与OS的紧密结合是有不小的关系的。
1、是基 于什么协议实现的?
基于纯粹的Socket+NIO。
2、怎么发起请求?
通过Mina提供的Client API。
3、怎么将请求转化为符合协议的格式的?
Mina遵循java串行化机制对请求对象进行序列化。
4、使用什么传输协议传输?
支持多种传输协议,例如socket、http等等。
5、响应端基于什么机制来接收请求?
以NIO的方式监听协议端口。
6、 怎么将流还原为传输格式的?
遵循java串行化机制对请求对象进行反序列化。
7、处理完毕后怎么回应?
遵循Mina API进行返回。
MINA是NIO方式的,因此支持异步调用是毫无悬念的。
--------------------------------------------------------------------------------------------------------------------------------------------------
EJB
EJB 最突出的在于其分布式,EJB采用的是ORMI协议,和RMI协议是差不多的,但EJB在分布式通讯的安全控制、transport pool、smart
proxy等方面的突出使得其在分布式领域是不可忽视的力量。
1、是基于什么协议实现的?
基于ORMI协议。
2、怎 么发起请求?
EJB调用。
3、怎么将请求转化为符合协议的格式的?
遵循java串行化机制对请求对象进行序列化。
4、使用什么传输协议传输?
Socket。
5、响应端基于什么机制来 接收请求?
监听协议端口。
6、怎么将流还原为传输格式的?
遵循java串行化机制对请求对象进行反序列化。
7、处理完毕后怎么回应?
直接返回处理对象即可。
在之前的分布式服务框架系列的文章中对于jndi有误导的嫌疑,在这篇blog中也顺带的提下jndi的机制,由于JNDI取决于具体的实现,在这
里只能是讲解下jboss的jndi的实现了。
在将对象实例绑定到jboss jnp
server后,当远程端采用context.lookup()方式获取远程对象实例并开始调用时,jboss jndi的实现方法是从jnp
server上获取对象实例,将其序列化回本地,然后在本地进行反序列化,之后在本地进行类调用。
通过这个机制,就可以知道了,本地其实是必须有
绑定到jboss上的对象实例的class的,否则反序列化的时候肯定就失败了,而远程通讯需要做到的是在远程执行某动作,并获取到相应的结果,可见纯粹
基于JNDI是无法实现远程通讯的。
但JNDI也是实现分布式服务框架一个很关键的技术点,因为可以通过它来实现透明化的远端和本地调用,就像
ejb,另外它也是个很好的隐藏实际部署机制(就像datasource)等的方案。
总结
由上一系列的分析可知,在远程通讯领域中,涉及的知识点还是相当的多的,例如有:通信协议(Socket/tcp/http/udp
/rmi/xml-rpc
etc.)、消息机制、网络IO(BIO/NIO/AIO)、MultiThread、本地调用与远程调用的透明化方案(涉及java
classloader、Dynamic Proxy、Unit Test
etc.)、异步与同步调用、网络通信处理机制(自动重连、广播、异常、池处理等等)、Java Serialization
(各种协议的私有序列化机制等)、各种框架的实现原理(传输格式、如何将传输格式转化为流的、如何将请求信息转化为传输格式的、如何接收流的、如何将流还
原为传输格式的等等),要精通其中的哪些东西,得根据实际需求来决定了,只有在了解了原理的情况下才能很容易的做出选择,甚至可以根据需求做私有的远程通
讯协议,对于从事分布式服务平台或开发较大型的分布式应用的人而言,我觉得至少上面提及的知识点是需要比较了解的。
--------------------------------------------转载完成--------------------------------------------------------------------------
2.关于JIT编译器(Just in time)
编译器是干什么的不用多说,C和C++这种语言,他们的编译器叫做静态编译器,会把代码直接编译为可以被计算机执行的二进制代码,PHP这种语言的“编译器”,叫做解释器,只需要安装正确的解释器,它们就能运行在任何CPU之上。当程序被执行的时候,程序代码会被逐行解释并执行。两种方式各有优缺点:
编译型语言的优缺点:
速度快:因为在编译的时候它们能够获取到更多的有关程序结构的信息,从而有机会对它们进行优化。
适用性差:它们编译得到的二进制代码往往是CPU相关的,在需要适配多种CPU时,可能需要编译多次。
解释型语言的优缺点:
适应性强:只需要安装正确的解释器,程序在任何CPU上都能够被运行
速度慢:因为程序需要被逐行翻译,导致速度变慢。同时因为缺乏编译这一过程,执行代码不能通过编译器进行优化。
Java采用了一个中间方案,
Java代码静不静态编译呢?编译!被编译成Java字节码,而不是针对某种CPU的二进制代码。
Java代码解释执行不?当然!Java字节码需要被java程序解释执行,此时,Java字节码被翻译成CPU相关的二进制代码。
而JIT的作用就是在解释的时候将字节码编译为适合CPU的二进制代码,由于是在运行的时候编译,所以叫做just in time。
关于JIT调优的部分,暂时不多说。有兴趣可以百度下
3.NIO(同步非阻塞)
提到Javde的IO其实有这么几个同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。
先说BIO和NIO,他们主要的区别在于,BIO往往需要引入多线程,每个连接都有一个线程,而NIO则单线程或者是少线程,多个连接共享线程。NIO这种机制是什么样的呢?首先一个连接创建后,不会对应一个线程,它会被注册到多路复用器上面,当多路复用器轮询发现有请求了,这个时候才会去开启一个线程处理这个请求,也就是说一个请求一个线程。
在说道上面几个关键词:同步,异步,阻塞,非阻塞,不得不祭出一个很好地例子
以银行取款为例:
-
同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写)。
-
异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API)。
-
阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回)。
-
非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去 (使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)。
关于Java对于这三种的支持,如下
-
Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善。
-
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
-
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
很显然,BIO适用于连接数比较小,架构固定的应用,它的并发集中在应用内,也是1.4以前的Java唯一的选择;NIO适用于连接数目多,但是连接比较短(比如聊天)的应用,并发也集中在应用内,1.4开始支持,而AIO则适用于连接数目大,操作重/长(相册)的应用,并发充分调动了OS的并发性能,JDK7开始支持。然后说到了高性能IO就必须提到两种模式Reactor和Proactor。我们接着说,首先我们先明确几个概念:
系统I/O 可分为阻塞型, 非阻塞同步型以及非阻塞异步型
阻塞型的IO意味着控制权在调用结束之前不能返回给调用者,这个期间调用者只能做一件事,那就是等着。更让人不爽的是在等待期间,调用者所在的线程只能眼巴巴的挂在那等着不能干任何事情,这无疑是非常浪费自愿的。
与此相对的非阻塞同步型的则是立即把控制权返回给调用者。调用者不用等待,它从被调用者那里获取两种可能,要么成功了进行了,要么返回给你一个错误代码或者标示之类的东西告诉你没成功。
而在非阻塞异步型中,立即返回时,会告诉调用者,你的请求开始了,并且会在完成的时候通知调用者(比如通过回调函数啥的)。
不用说大家都能看出来,性能最好的是非阻塞异步。
然后我们来正式说说这两个多路复用IO方案(Reactor和Proactor)
一般来说有个东西叫做时间分享器(event demultiplexor),是一个IO复用机需要具备的。它的作用在于把读写事件源分发给各读写事件的处理人,好比传达室大爷,负责吼叫每个人下来拿信件。
Reactor和Proactor都涉及到这个分享器。其中Reactor是基于同步IO的,但是Proactor是基于异步IO的。他们的区别在于,Reactor等待状态,一旦可以读写,就通过回调或者通知给预先注册好的处理者,由矗立着来完成实际的操作;而在Proactor中处理者直接发起一个异步读写操作,实际的读写是由OS完成的,这种方式在发起的时候就需要提供几个比较重要的参数,包括用于存放数据的缓存区,读取数据的大小,回调函数信息等,分离器得到这个请求后默默地处理,等待完成,然后转发给相应的处理器去处理。以read操作举例(write类似)
Reactor的方式
- 某个事件处理者注册它对某个socket上的读事件很感兴趣;
- 事件分离者等着这个事件的发生;
- 当事件发生了,事件分离器被唤醒,负责通知先前那个事件处理者;
- 事件处理者收到消息,于是去那个socket上读数据了。 如果需要,它再次宣称对这个socket上的读事件感兴趣,一直重复上面的步骤;
Proactor的方式
- 事件处理者直接投递发一个读操作(当然,操作系统必须支持这个异步操作)。 这个时候,事件处理者根本不关心读事件,它只管发这么个请求,它需要的是这个完成的事件。
- 事件分离者等着这个读事件的完成(比较下与Reactor的不同);
- 当完成事件到来的同时,操作系统已经开始干活了,它从目标读取数据,放入用户提供的缓存区中,最后通知事件分离者,这个事情完成了;
- 事件分享者通知之前的事件处理者: 你吩咐的事情搞定了;
- 事件处理者这时会发现想要读的数据已经乖乖地放在他提供的缓存区中,想怎么处理都行了。如果有需要,事件处理者还像之前一样发起另外一个写操作,和上面的几个步骤一样。
然后这两种方案是不是完全不可以调和的呢?答案当然是否定地,调和方案叫做TProactor,关于这个方案,
http://blog.csdn.net/sparkliang/article/details/5390656博客讲的很赞,但是博主声明未经允许不得转载,我也就不转了,大家移步~~~
4.异常链
现在我们抛出一个异常都是可以包含有原始异常信息的,这对我们调试代码非常有帮助,但是1.4以前的JDK,这个工作却需要程序人员自己搞定,现在所有Throwable的子类子构造器中都可以接受一个cause对象作为参数,这个cause就异常原由,代表着原始异常,即使在当前位置创建并抛出行的异常,也可以通过这个cause追踪到异常最初发生的位置。需要注意的是只有Error,Exception,RunimeException提供了带cause参数的构造器,其他的所以异常就只有通过initCause()来设置cause。
注意一下几种形式的异常使用:
1.这种形式通过initCause设置异常。
Exception bussinessEx = new Exception("packag exception"); bussinessEx.initCause(ex); //ex是某个异常 throw bussinessEx;
2.这种形式使我们常用的方式
throw new Exception("packag exception", ex);
3.这种形式是错误的!!!throwable 不能是它自己的 cause。
throw (Exception)ex.fillInStackTrace().initCause(ex);
5.动态注解(这个词是这本书中给出的,实际上就是1.5开始支持的注解╮(╯▽╰)╭干啥非要加个动态....)
关于注解这东西有多方便,有多牛逼,我相信,谁用过谁知道,那叫一个灵活,Spring那些0配置文件的都靠它了。JDK5的时候开始出现,只有以下这几个
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
他们四个是标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明,被称为元注解(也有人说他们是描述注解的注解~~~ )。具体的能我也不想细说了,毕竟这种东西网上海量,说起来也毫无意义,如何灵活运用就看实例了,比如动态数据源切换啥的。
还是老规矩,不详细说就贴链接http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
6.Lambda表达式
说起Lambda表达式,必须先说明一件事,那就是函数式编程,这个事情不能太展开说,因为万一展开了,咱就不用回来说Java了,所以只能简单说(最重要的是看我心情~~ 万一说嗨了那就这么下去吧)。函数式编程这东西很早就有了,80年的时候吧Haskell就出现了,不过一直都没有火起来,最近为什么又火起来了呢,主要是大数据时代来了,分布式来了,越来越需要平行的计算,所以能这玩意就又火了,但是几个大牛又各自看对方不爽,甚至有些自己内部都有撕逼大战........所以为了不出这个霉头,我赶紧收住,千万不提那些大牛。说回Java来,Java一看这玩意这么火Python啥的都开始支持了,我不支持不行啊,这不落后了么,所以Java也在jdk8中开始对函数式编程支持,也就是lambda表达式。
(关于函数式编程我给你们贴一个阮一峰的博客~~http://www.ruanyifeng.com/blog/2012/04/functional_programming.html)
(关于lambda表达式我给你们一个翻译的文章http://www.cnblogs.com/figure9/archive/2014/10/24/4048421.html)
7.Jigsaw(这个我不想说.......拖延症患者........7的时候就说搞,拖到9)
这东西已经被推到9了.....现在还是15年末....9预计推出是在16年,不过我倒是觉得这东西9都未必能出.......等着吧.....
8.动态语言支持
这就是个拽词....其实意思就是Java7的虚拟机对多种动态程序语言增加了支持,比如:Rubby、 Python等等。对这些动态语言的支持极大地扩展了Java虚拟机的能力。对于那些熟悉这些动态语言的程序员而言,在使用Java虚拟机的过程中同样可 以使用它们熟悉的语言进行功能的编写,而这些语言是跑在功能强大的JVM之上的。仅此而已...............
9.G1垃圾回收器(Garbage-First)
这东西很牛逼,但是7中还只是实验阶段,好像在8中依然还是实验阶段,按照openjdk的进度,似乎9才会把G1作为默认的垃圾回收器,要说G1之前,先说说CMS吧,CMS的回收机制是这样的(原文参考:http://stackvoid.com/Introduce-to-G1/):
堆被分解为较小的三个部分。具体分为:新生代、老年代、持久代。
- 绝大部分新生成的对象都放在Eden区,当Eden区将满,JVM会因申请不到内存,而触发Young GC ,进行Eden区+有对象的Survivor区(设为S0区)垃圾回收,把存活的对象用复制算法拷贝到一个空的Survivor(S1)中,此时Eden 区被清空,另外一个Survivor S0也为空。下次触发Young GC回收Eden+S0,将存活对象拷贝到S1中。新生代垃圾回收简单、粗暴、高效。
- 若发现Survivor区满了,则将这些对象拷贝到old区或者Survivor没满但某些对象足够Old,也拷贝到Old区(每次Young GC都会使Survivor区存活对象值+1,直到阈值)。 3.Old区也会进行垃圾收集(Young GC),发生一次 Major GC 至少伴随一次Young GC,一般比Young GC慢十倍以上。
- JVM在Old区申请不到内存,会进行Full GC。Old区使用一般采用Concurrent-Mark–Sweep策略回收内存。
总结:Java垃圾回收器是一种“自适应的、分代的、停止—复制、标记-清扫”式的垃圾回收器。
缺点:
- GC过程中会出现STW(Stop-The-World),若Old区对象太多,STW耗费大量时间。
- CMS收集器对CPU资源很敏感。
- CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。
- CMS导致内存碎片问题。
再来看看G1是啥样子的:
在G1中,堆被划分成 许多个连续的区域(region)。每个区域大小相等,在1M~32M之间。JVM最多支持2000个区域,可推算G1能支持的最大内存为 2000*32M=62.5G。区域(region)的大小在JVM初始化的时候决定,也可以用-XX:G1HeapReginSize设置。
在G1中没有物理上的Yong(Eden/Survivor)/Old Generation,它们是逻辑的,使用一些非连续的区域(Region)组成的。
新生代收集
G1的新生代收集跟ParNew类似,当新生代占用达到一定比例的时候,开始出发收集。
被圈起的绿色部分为新生代的区域(region),经过Young GC后存活的对象被复制到一个或者多个区域空闲中,这些被填充的区域将是新的新生代;当新生代对象的年龄(逃逸过一次Young GC年龄增加1)已经达到某个阈值(ParNew默认15),被复制到老年代的区域中。
回收过程是停顿的(STW,Stop-The-Word);回收完成之后根据Young GC的统计信息调整Eden和Survivor的大小,有助于合理利用内存,提高回收效率。
回收的过程多个回收线程并发收集。
老年代收集
和CMS类似,G1收集器收集老年代对象会有短暂停顿。
-
标记阶段,首先初始标记(Initial-Mark),这个阶段是停顿的(Stop the World Event),并且会触发一次普通Mintor GC。对应GC log:GC pause (young) (inital-mark)
-
Root Region Scanning,程序运行过程中会回收survivor区(存活到老年代),这一过程必须在young GC之前完成。
-
Concurrent Marking,在整个堆中进行并发标记(和应用程序并发执行),此过程可能被young GC中断。在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那个这个区域会被立即回收(图中打X)。同时,并发标记过程中,会计算每个区域的对象活 性(区域中存活对象的比例)。
-
Remark, 再标记,会有短暂停顿(STW)。再标记阶段是用来收集 并发标记阶段 产生新的垃圾(并发阶段和应用程序一同运行);G1中采用了比CMS更快的初始快照算法:snapshot-at-the-beginning (SATB)。
-
Copy/Clean up,多线程清除失活对象,会有STW。G1将回收区域的存活对象拷贝到新区域,清除Remember Sets,并发清空回收区域并把它返回到空闲区域链表中。
- 复制/清除过程后。回收区域的活性对象已经被集中回收到深蓝色和深绿色区域。
关于Remembered Set概念:G1收集器中,Region之间的对象引用以及其他收集器中的新生代和 老年代之间的对象引用是使用Remembered Set来避免扫描全堆。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序对Reference类型数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之间(在分代中例子中就是检查是否老年代中的对象引用了新生 代的对象),如果是便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中。当内存回收时,在GC根节点的枚举范围加入Remembered Set即可保证不对全局堆扫描也不会有遗漏。
G1虽然保留了CMS关于代的概念,但是代已经不是物理上连续区域,而是一个逻辑的概念。在标记过程中,每个区域的对象活性都被计算,在回收时候, 就可以根据用户设置的停顿时间,选择活性较低的区域收集,这样既能保证垃圾回收,又能保证停顿时间,而且也不会降低太多的吞吐量。Remark阶段新算法 的运用,以及收集过程中的压缩,都弥补了CMS不足。引用Oracle官网的一句话:“G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS)”。
关于这个G1,参考
10.Coin(语言细节化)
名词挺吊,无非在7的时候就是指
数字下划线像这样
public class Coin { int test = 123_567; long test1 = 100_000L; }
switch语句中的String
public void printDay(String dayOfWeek){ case "Sunday":System.out.println("ddd");break; default:System.out.println("sss");break; }
multicatch
public Configuration getConfig(String fileName) { Configuration cfg = null; try { String fileText = getFile(fileName); cfg = verifyConfig(parseConfig(fileText)); } catch (FileNotFoundException | ParseException | ConfigurationException e) { System.err.println("Config file '" + fileName + "' is missing or malformed"); } catch (IOException iox) { System.err.println("Error while processing file '" + fileName + "'"); } return cfg; }
final重抛
try { String fileText = getFile(fileName); cfg = verifyConfig(parseConfig(fileText)); } catch (final Exception e) { throw e; }
try-with-resources(TWR) AutoCloseable
public class Java7ResourcesExample { private void run() throws IOException { File file = new File("foo"); URL url = null; try { url = new URL("http://www.google.com/"); } catch (MalformedURLException e) { } try (OutputStream out = new FileOutputStream(file); InputStream is = url.openStream()) { byte[] buf = new byte[4096]; int len; while ((len = is.read(buf)) > 0) { out.write(buf, 0, len); } } } public static void main(String[] args) throws IOException { Java7ResourcesExample instance = new Java7ResourcesExample(); instance.run(); } }
所谓的钻石语法
HashMap<String, String> a = new HashMap<>();
变参 消失的警告 @SafeVarargs
ublic class Coin { int test = 123_567; long test1 = 100_000L; @SafeVarargs public static <T> Collection<T> doSomething(T... entries){ return null; } public static void main(String[] args) { HashMap<String, String> a = new HashMap<>(); HashMap<String, String> b = new HashMap<>(); doSomething(a,b); } }
实际上还有很多新特性啊,很多关键词啊,只是我无比鸡贼的没有写出来,假装自己不知道,就不用都说了,哈哈哈哈。